forked from mirrors/gecko-dev
Bug 1791675 - Part 4: Make sure purgeHTTPCache runs after shutdown r=webdriver-reviewers,necko-reviewers,valentin,nalexander,whimboo
This adds BackgroundTasksRunner utility class as a generic way to properly run background tasks. A few argument for not extending existing BackgroundTasksUtils: 1. Simply because the existing use case is in C++. 2. I have another use case from JSM and thus I'll ultimately convert this an XPCOM component. And `CacheFileIOManager::DispatchPurgeTask` cannot get a JSM-written XPCOM instance which is required to be main-thread only. Depends on D157998 Differential Revision: https://phabricator.services.mozilla.com/D157757
This commit is contained in:
parent
a3364bdccc
commit
d20dd96f8f
11 changed files with 256 additions and 23 deletions
|
|
@ -13494,6 +13494,12 @@
|
|||
# Prefs starting with "toolkit."
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# Makes removeDirectory background task wait for the given milliseconds before removal.
|
||||
- name: toolkit.background_tasks.remove_directory.testing.sleep_ms
|
||||
type: RelaxedAtomicUint32
|
||||
value: 0
|
||||
mirror: always
|
||||
|
||||
# Returns true if BHR is disabled.
|
||||
- name: toolkit.content-background-hang-monitor.disabled
|
||||
type: bool
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsISizeOf.h"
|
||||
#include "mozilla/net/MozURL.h"
|
||||
#include "mozilla/BackgroundTasksRunner.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
|
@ -35,7 +36,6 @@
|
|||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "prproces.h"
|
||||
|
||||
// include files for ftruncate (or equivalent)
|
||||
#if defined(XP_UNIX)
|
||||
|
|
@ -4096,14 +4096,6 @@ nsresult CacheFileIOManager::DispatchPurgeTask(
|
|||
rv = XRE_GetBinaryPath(getter_AddRefs(lf));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString exePath;
|
||||
#if !defined(XP_WIN)
|
||||
rv = lf->GetNativePath(exePath);
|
||||
#else
|
||||
rv = lf->GetNativeTarget(exePath);
|
||||
#endif
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString path;
|
||||
#if !defined(XP_WIN)
|
||||
rv = profileDir->GetNativePath(path);
|
||||
|
|
@ -4112,17 +4104,8 @@ nsresult CacheFileIOManager::DispatchPurgeTask(
|
|||
#endif
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
const char* const argv[] = {exePath.get(), "--backgroundtask",
|
||||
"removeDirectory", path.get(),
|
||||
aCacheDirName.get(), aSecondsToWait.get(),
|
||||
aPurgeExtension.get(), nullptr};
|
||||
if (NS_WARN_IF(PR_FAILURE == PR_CreateProcessDetached(exePath.get(),
|
||||
(char* const*)argv,
|
||||
nullptr, nullptr))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return BackgroundTasksRunner::RemoveDirectoryInDetachedProcess(
|
||||
path, aCacheDirName, aSecondsToWait, aPurgeExtension);
|
||||
}
|
||||
|
||||
void CacheFileIOManager::SyncRemoveAllCacheFiles() {
|
||||
|
|
|
|||
1
netwerk/test/marionette/manifest.ini
Normal file
1
netwerk/test/marionette/manifest.ini
Normal file
|
|
@ -0,0 +1 @@
|
|||
[test_purge_http_cache_at_shutdown.py]
|
||||
74
netwerk/test/marionette/test_purge_http_cache_at_shutdown.py
Normal file
74
netwerk/test/marionette/test_purge_http_cache_at_shutdown.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from marionette_driver import Wait
|
||||
from marionette_harness import MarionetteTestCase
|
||||
|
||||
|
||||
class PurgeHTTPCacheAtShutdownTestCase(MarionetteTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.marionette.enforce_gecko_prefs(
|
||||
{
|
||||
"privacy.sanitize.sanitizeOnShutdown": True,
|
||||
"privacy.clearOnShutdown.cache": True,
|
||||
"network.cache.shutdown_purge_in_background_task": True,
|
||||
}
|
||||
)
|
||||
|
||||
self.profile_path = Path(self.marionette.profile_path)
|
||||
self.cache_path = self.profile_path.joinpath("cache2")
|
||||
|
||||
def tearDown(self):
|
||||
self.marionette.cleanup()
|
||||
super().tearDown()
|
||||
|
||||
def cacheDirExists(self):
|
||||
return self.cache_path.exists()
|
||||
|
||||
def renamedDirExists(self):
|
||||
return any(
|
||||
child.name.endswith(".purge.bg_rm") for child in self.profile_path.iterdir()
|
||||
)
|
||||
|
||||
def test_ensure_cache_purge_after_in_app_quit(self):
|
||||
self.assertTrue(self.cacheDirExists(), "Cache directory must exist")
|
||||
|
||||
self.marionette.quit()
|
||||
|
||||
Wait(self.marionette, timeout=60).until(
|
||||
lambda _: not self.cacheDirExists() and not self.renamedDirExists(),
|
||||
message="Cache directory must be removed after orderly shutdown",
|
||||
)
|
||||
|
||||
def test_longstanding_cache_purge_after_in_app_quit(self):
|
||||
self.assertTrue(self.cacheDirExists(), "Cache directory must exist")
|
||||
|
||||
self.marionette.set_pref(
|
||||
"toolkit.background_tasks.remove_directory.testing.sleep_ms", 5000
|
||||
)
|
||||
|
||||
self.marionette.quit()
|
||||
|
||||
Wait(self.marionette, timeout=60).until(
|
||||
lambda _: not self.cacheDirExists() and not self.renamedDirExists(),
|
||||
message="Cache directory must be removed after orderly shutdown",
|
||||
)
|
||||
|
||||
def test_ensure_cache_purge_after_forced_restart(self):
|
||||
"""
|
||||
Doing forced restart here to prevent the shutdown phase purging and only allow startup
|
||||
phase one, via `CacheFileIOManager::OnDelayedStartupFinished`.
|
||||
"""
|
||||
self.profile_path.joinpath("foo.purge.bg_rm").mkdir()
|
||||
|
||||
self.marionette.restart(in_app=False)
|
||||
|
||||
Wait(self.marionette, timeout=60).until(
|
||||
lambda _: not self.renamedDirExists(),
|
||||
message="Directories with .purge.bg_rm postfix must be removed at startup after"
|
||||
"disorderly shutdown",
|
||||
)
|
||||
|
|
@ -26,5 +26,9 @@ TESTING_JS_MODULES += [
|
|||
|
||||
PERFTESTS_MANIFESTS += ["perf/perftest.ini", "unit/perftest.ini"]
|
||||
|
||||
MARIONETTE_UNIT_MANIFESTS += [
|
||||
"marionette/manifest.ini",
|
||||
]
|
||||
|
||||
if CONFIG["FUZZING_INTERFACES"]:
|
||||
TEST_DIRS += ["fuzz"]
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@
|
|||
# layout tests
|
||||
[include:../../../../../layout/base/tests/marionette/manifest.ini]
|
||||
|
||||
# netwerk tests
|
||||
[include:../../../../../netwerk/test/marionette/manifest.ini]
|
||||
|
||||
# toolkit tests
|
||||
[include:../../../../../toolkit/components/cleardata/tests/marionette/manifest.ini]
|
||||
[include:../../../../../toolkit/components/extensions/test/marionette/manifest.ini]
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ async function cleanupOtherDirectories(parentDirPath, otherFoldersSuffix) {
|
|||
}
|
||||
|
||||
// Usage:
|
||||
// removeDirectory parentDirPath childDirName secondsToWait [otherFoldersSuffix]
|
||||
// removeDirectory parentDirPath childDirName secondsToWait [otherFoldersSuffix] [--test-sleep testSleep]
|
||||
// arg0 arg1 arg2 arg3
|
||||
// parentDirPath - The path to the parent directory that includes the target directory
|
||||
// childDirName - The "leaf name" of the moved cache directory
|
||||
|
|
@ -175,7 +175,13 @@ async function cleanupOtherDirectories(parentDirPath, otherFoldersSuffix) {
|
|||
// otherFoldersSuffix - [optional] The suffix of directories that should be removed
|
||||
// When not empty, this task will also attempt to remove all directories in
|
||||
// the parent dir that end with this suffix
|
||||
// testSleep - [optional] A test-only argument to sleep for a given milliseconds before removal.
|
||||
// This exists to test whether a long-running task can survive.
|
||||
export async function runBackgroundTask(commandLine) {
|
||||
const testSleep = Number.parseInt(
|
||||
commandLine.handleFlagWithParam("test-sleep", false)
|
||||
);
|
||||
|
||||
if (commandLine.length < 3) {
|
||||
throw new Error("Insufficient arguments");
|
||||
}
|
||||
|
|
@ -186,13 +192,26 @@ export async function runBackgroundTask(commandLine) {
|
|||
if (isNaN(secondsToWait)) {
|
||||
secondsToWait = 10;
|
||||
}
|
||||
commandLine.removeArguments(0, 2);
|
||||
|
||||
let otherFoldersSuffix = "";
|
||||
if (commandLine.length >= 4) {
|
||||
otherFoldersSuffix = commandLine.getArgument(3);
|
||||
if (commandLine.length) {
|
||||
otherFoldersSuffix = commandLine.getArgument(0);
|
||||
commandLine.removeArguments(0, 0);
|
||||
}
|
||||
|
||||
if (commandLine.length) {
|
||||
throw new Error(
|
||||
`${commandLine.length} unknown command args exist, closing.`
|
||||
);
|
||||
}
|
||||
|
||||
console.error(parentDirPath, childDirName, secondsToWait, otherFoldersSuffix);
|
||||
|
||||
if (!Number.isNaN(testSleep)) {
|
||||
await new Promise(resolve => lazy.setTimeout(resolve, testSleep));
|
||||
}
|
||||
|
||||
await deleteChildDirectory(parentDirPath, childDirName, secondsToWait);
|
||||
await cleanupOtherDirectories(parentDirPath, otherFoldersSuffix);
|
||||
|
||||
|
|
|
|||
83
toolkit/components/backgroundtasks/BackgroundTasksRunner.cpp
Normal file
83
toolkit/components/backgroundtasks/BackgroundTasksRunner.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/BackgroundTasksRunner.h"
|
||||
|
||||
#include "base/process_util.h"
|
||||
#include "mozilla/StaticPrefs_toolkit.h"
|
||||
#include "nsIFile.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
# include "mozilla/AssembleCmdLine.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
nsresult BackgroundTasksRunner::RunInDetachedProcess(
|
||||
const nsACString& aTaskName, const nsTArray<nsCString>& aArgs) {
|
||||
nsCOMPtr<nsIFile> lf;
|
||||
nsresult rv = XRE_GetBinaryPath(getter_AddRefs(lf));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoCString exePath;
|
||||
#if !defined(XP_WIN)
|
||||
rv = lf->GetNativePath(exePath);
|
||||
#else
|
||||
rv = lf->GetNativeTarget(exePath);
|
||||
#endif
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
base::LaunchOptions options;
|
||||
#ifdef XP_WIN
|
||||
options.start_independent = true;
|
||||
|
||||
nsTArray<const char*> argv = {exePath.Data(), "--backgroundtask",
|
||||
aTaskName.Data()};
|
||||
for (const nsCString& str : aArgs) {
|
||||
argv.AppendElement(str.get());
|
||||
}
|
||||
argv.AppendElement(nullptr);
|
||||
|
||||
wchar_t* assembledCmdLine = nullptr;
|
||||
if (assembleCmdLine(argv.Elements(), &assembledCmdLine, CP_UTF8) == -1) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!base::LaunchApp(assembledCmdLine, options, nullptr)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#else
|
||||
std::vector<std::string> argv = {exePath.Data(), "--backgroundtask",
|
||||
aTaskName.Data()};
|
||||
for (const nsCString& str : aArgs) {
|
||||
argv.push_back(str.get());
|
||||
}
|
||||
|
||||
if (!base::LaunchApp(argv, options, nullptr)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult BackgroundTasksRunner::RemoveDirectoryInDetachedProcess(
|
||||
const nsCString& aParentDirPath, const nsCString& aChildDirName,
|
||||
const nsCString& aSecondsToWait, const nsCString& aOtherFoldersSuffix) {
|
||||
nsTArray<nsCString> argv = {aParentDirPath, aChildDirName, aSecondsToWait,
|
||||
aOtherFoldersSuffix};
|
||||
|
||||
uint32_t testingSleepMs =
|
||||
StaticPrefs::toolkit_background_tasks_remove_directory_testing_sleep_ms();
|
||||
if (testingSleepMs > 0) {
|
||||
argv.AppendElement("--test-sleep");
|
||||
nsAutoCString sleep;
|
||||
sleep.AppendInt(testingSleepMs);
|
||||
argv.AppendElement(sleep);
|
||||
}
|
||||
|
||||
return RunInDetachedProcess("removeDirectory"_ns, argv);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
43
toolkit/components/backgroundtasks/BackgroundTasksRunner.h
Normal file
43
toolkit/components/backgroundtasks/BackgroundTasksRunner.h
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef TOOLKIT_COMPONENTS_BACKGROUNDTASKS_BACKGROUNDTASKSRUNNER_H_
|
||||
#define TOOLKIT_COMPONENTS_BACKGROUNDTASKS_BACKGROUNDTASKSRUNNER_H_
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class BackgroundTasksRunner final {
|
||||
public:
|
||||
/**
|
||||
* Runs a background process in an independent detached process. Any process
|
||||
* opened by this function can outlive the main process.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* @param aTaskName The name of the background task.
|
||||
* (BackgroundTask_{name}.sys.mjs)
|
||||
* @param aArgs The arguments that will be passed to the task process. Any
|
||||
* needed escape will happen automatically.
|
||||
*/
|
||||
static nsresult RunInDetachedProcess(const nsACString& aTaskName,
|
||||
const nsTArray<nsCString>& aArgs);
|
||||
|
||||
/**
|
||||
* Runs removeDirectory background task.
|
||||
* `toolkit.background_tasks.remove_directory.testing.sleep_ms` can be set to
|
||||
* make it wait for the given milliseconds for testing purpose.
|
||||
*
|
||||
* See BackgroundTask_removeDirectory.sys.mjs for details about the arguments.
|
||||
*/
|
||||
static nsresult RemoveDirectoryInDetachedProcess(
|
||||
const nsCString& aParentDirPath, const nsCString& aChildDirName,
|
||||
const nsCString& aSecondsToWait, const nsCString& aOtherFoldersSuffix);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // TOOLKIT_COMPONENTS_BACKGROUNDTASKS_BACKGROUNDTASKSRUNNER_H_
|
||||
|
|
@ -59,6 +59,18 @@ For more details, see [`XPCSHELL_TESTING_MODULES_URI`](https://searchfox.org/moz
|
|||
|
||||
Background task mode supports using the JavaScript debugger and the Firefox Devtools and Browser Toolbox. When invoked with the command line parameters `--jsdebugger` (and optionally `--wait-for-jsdebugger`), the background task framework will launch a Browser Toolbox, connect to the background task, and pause execution at the first line of the task implementation. The Browser Toolbox is launched with a temporary profile (sibling to the ephemeral temporary profile the background task itself creates.) The Browser Toolbox profile's preferences are copied from the default browsing profile, allowing to configure devtools preferences. (The `--start-debugger-server` command line option is also recognized; see the output of `firefox --backgroundtask success --attach-console --help` for details.)
|
||||
|
||||
## Invoking background tasks
|
||||
|
||||
Use `BackgroundTasksRunner::RunInDetachedProcess` is a helper to open a new background process within Gecko. It automatically manages the configuration 1) to let the new process outlive the launching process and 2) to escape the arguments properly. The function is safe to be called in a non-main process.
|
||||
|
||||
## Existing background tasks
|
||||
|
||||
* `BackgroundTask_removeDirectory`
|
||||
|
||||
Removes the child directory with the given name and/or child directories with the given postfix, all in the given parent directory. It's recommended to run it via the corresponding helper function `BackgroundTasksRunner::RemoveDirectoryInDetachedProcess`.
|
||||
|
||||
Tests can use `toolkit.background_tasks.remove_directory.testing.sleep_ms` to see whether a longstanding task can finish the work even after the launching process is closed.
|
||||
|
||||
## The background task mode runtime environment
|
||||
|
||||
### Most background tasks run in ephemeral temporary profiles
|
||||
|
|
|
|||
|
|
@ -17,10 +17,12 @@ for var in ("MOZ_APP_VENDOR",):
|
|||
|
||||
UNIFIED_SOURCES += [
|
||||
"BackgroundTasks.cpp",
|
||||
"BackgroundTasksRunner.cpp",
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
"BackgroundTasks.h",
|
||||
"BackgroundTasksRunner.h",
|
||||
]
|
||||
|
||||
XPCOM_MANIFESTS += [
|
||||
|
|
@ -92,3 +94,6 @@ if CONFIG["MOZ_BUILD_APP"] == "browser":
|
|||
FINAL_TARGET_FILES.defaults.backgroundtasks += [
|
||||
"defaults/backgroundtasks.js",
|
||||
]
|
||||
|
||||
# For base::LaunchApp
|
||||
include("/ipc/chromium/chromium-config.mozbuild")
|
||||
|
|
|
|||
Loading…
Reference in a new issue