Skip to content

Commit

Permalink
Don't wait 8 minutes to suspend background tabs on iOS
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=265064
rdar://118578232

Reviewed by Chris Dumez.

Disable the NearSuspended state, which allows background WebContent processes to suspend immediately
rather than after 8 minutes.

To do this, we need to fix the heuristic in `prepareToDropLastAssertion`, which fires just before a
process task_suspends. Previously it was generally called after a process ended up in the process
cache. Now that we are calling it earlier, we want it to prevent clearing out the caches from
processes that might end up in the process cache in the future, so we have to consult
`canBeAddedToWebProcessCache` about that.

Another change is that the `SetIsInProcessCache` message might be sent while the process is
task_suspended. To fix this, change that message to one with an async reply handler so that it takes
out a background activity while the IPC is being handled.

Finally, the `WebProcess::releaseMemory` IPC handler was often causing the process to completely exit,
since it invokes the memory pressure handler, and the memory pressure handler exits if there are no
active pages in the process. This seems undesired (if the UIProcess really wanted to terminate the
WebProcess, it could just send the `AuxiliaryProcess::ShutDown` message). So we now suppress the
memory pressure handler called by `WebProcess::releaseMemory` from exiting the process.

* Source/WebKit/UIProcess/WebProcessProxy.cpp:
(WebKit::WebProcessProxy::setIsInProcessCache):
(WebKit::WebProcessProxy::prepareToDropLastAssertion):
* Source/WebKit/WebProcess/WebProcess.cpp:
(WebKit::WebProcess::initializeWebProcess):
(WebKit::WebProcess::setIsInProcessCache):
(WebKit::WebProcess::releaseMemory):
(WebKit::WebProcess::prepareToSuspend):
* Source/WebKit/WebProcess/WebProcess.h:
* Source/WebKit/WebProcess/WebProcess.messages.in:

Canonical link: https://commits.webkit.org/272937@main
  • Loading branch information
bnham committed Jan 11, 2024
1 parent d6d777b commit b0259bf
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Source/WTF/Scripts/Preferences/UnifiedWebPreferences.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6160,7 +6160,7 @@ ShouldTakeNearSuspendedAssertions:
WebKitLegacy:
default: true
WebKit:
default: true
default: defaultShouldTakeNearSuspendedAssertion()
WebCore:
default: true

Expand Down
1 change: 1 addition & 0 deletions Source/WTF/wtf/cocoa/RuntimeApplicationChecksCocoa.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ enum class SDKAlignedBehavior {
ExpiredOnlyReloadBehavior,
ForbidsDotPrefixedFonts,
FullySuspendsBackgroundContent,
FullySuspendsBackgroundContentImmediately,
HasUIContextMenuInteraction,
HTMLDocumentSupportedPropertyNames,
InitializeWebKit2MainThreadAssertion,
Expand Down
10 changes: 10 additions & 0 deletions Source/WebKit/Shared/WebPreferencesDefaultValues.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,16 @@ bool defaultShouldDropNearSuspendedAssertionAfterDelay()
#endif
}

bool defaultShouldTakeNearSuspendedAssertion()
{
#if PLATFORM(IOS_FAMILY)
static bool newSDK = linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::FullySuspendsBackgroundContentImmediately);
return !newSDK;
#else
return true;
#endif
}

bool defaultLiveRangeSelectionEnabled()
{
#if PLATFORM(IOS_FAMILY)
Expand Down
1 change: 1 addition & 0 deletions Source/WebKit/Shared/WebPreferencesDefaultValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ bool defaultGamepadVibrationActuatorEnabled();

bool defaultRunningBoardThrottlingEnabled();
bool defaultShouldDropNearSuspendedAssertionAfterDelay();
bool defaultShouldTakeNearSuspendedAssertion();
bool defaultShowModalDialogEnabled();
bool defaultLiveRangeSelectionEnabled();

Expand Down
27 changes: 19 additions & 8 deletions Source/WebKit/UIProcess/WebProcessProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,9 @@ void WebProcessProxy::setIsInProcessCache(bool value, WillShutDown willShutDown)
if (willShutDown == WillShutDown::Yes)
return;

send(Messages::WebProcess::SetIsInProcessCache(m_isInProcessCache), 0);
// The WebProcess might be task_suspended at this point, so use sendWithAsyncReply to resume
// the process via a background activity long enough to process the IPC if necessary.
sendWithAsyncReply(Messages::WebProcess::SetIsInProcessCache(m_isInProcessCache), []() { });

if (m_isInProcessCache) {
// WebProcessProxy objects normally keep the process pool alive but we do not want this to be the case
Expand Down Expand Up @@ -1758,16 +1760,25 @@ void WebProcessProxy::didDropLastAssertion()

void WebProcessProxy::prepareToDropLastAssertion(CompletionHandler<void()>&& completionHandler)
{
#if PLATFORM(MAC)
if (isInProcessCache()) {
// We don't free caches in cached WebProcesses on macOS for performance reasons.
// Cached WebProcess will anyway shutdown on memory pressure.
#if ENABLE(WEBPROCESS_CACHE)
if (isInProcessCache() || !m_suspendedPages.isEmptyIgnoringNullReferences() || (canTerminateAuxiliaryProcess() && canBeAddedToWebProcessCache())) {
// We avoid freeing caches if:
//
// 1. The process is already in the WebProcess cache.
// 2. The process is already in the back/forward cache.
// 3. The process might end up in the process cache (canTerminateAuxiliaryProcess() && canBeAddedToWebProcessCache())
//
// The idea here is that we want these cached processes to retain useful data if they're
// reused. They have a low jetsam priority and will be killed by our low memory handler or
// the kernel if necessary.
return completionHandler();
}
#endif
// We don't slim down the process in the PrepareToSuspend IPC, we delay clearing the
// caches until we release the suspended assertion.
// When the WebProcess cache is enabled, instead of freeing caches in the PrepareToSuspend
// we free caches here just before we drop our last process assertion.
sendWithAsyncReply(Messages::WebProcess::ReleaseMemory(), WTFMove(completionHandler), 0, { }, ShouldStartProcessThrottlerActivity::No);
#else
completionHandler();
#endif
}

String WebProcessProxy::environmentIdentifier() const
Expand Down
12 changes: 7 additions & 5 deletions Source/WebKit/WebProcess/WebProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ void WebProcess::initializeWebProcess(WebProcessCreationParameters&& parameters)

#if PLATFORM(COCOA)
// If this is a process we keep around for performance, kill it on memory pressure instead of trying to free up its memory.
if (!m_isSuspending && (m_processType == ProcessType::CachedWebContent || m_processType == ProcessType::PrewarmedWebContent || areAllPagesSuspended())) {
if (m_allowExitOnMemoryPressure && (m_processType == ProcessType::CachedWebContent || m_processType == ProcessType::PrewarmedWebContent || areAllPagesSuspended())) {
if (m_processType == ProcessType::CachedWebContent)
WEBPROCESS_RELEASE_LOG(Process, "initializeWebProcess: Cached WebProcess is exiting due to memory pressure");
else if (m_processType == ProcessType::PrewarmedWebContent)
Expand All @@ -448,8 +448,8 @@ void WebProcess::initializeWebProcess(WebProcessCreationParameters&& parameters)
}
#endif

auto maintainBackForwardCache = m_isSuspending ? WebCore::MaintainBackForwardCache::Yes : WebCore::MaintainBackForwardCache::No;
auto maintainMemoryCache = m_isSuspending && m_hasSuspendedPageProxy ? WebCore::MaintainMemoryCache::Yes : WebCore::MaintainMemoryCache::No;
auto maintainBackForwardCache = m_allowExitOnMemoryPressure ? WebCore::MaintainBackForwardCache::No : WebCore::MaintainBackForwardCache::Yes;
auto maintainMemoryCache = (m_allowExitOnMemoryPressure || !m_hasSuspendedPageProxy) ? WebCore::MaintainMemoryCache::No : WebCore::MaintainMemoryCache::Yes;
WebCore::releaseMemory(critical, synchronous, maintainBackForwardCache, maintainMemoryCache);
for (auto& page : m_pageMap.values())
page->releaseMemory(critical);
Expand Down Expand Up @@ -674,7 +674,7 @@ void WebProcess::setHasSuspendedPageProxy(bool hasSuspendedPageProxy)
m_hasSuspendedPageProxy = hasSuspendedPageProxy;
}

void WebProcess::setIsInProcessCache(bool isInProcessCache)
void WebProcess::setIsInProcessCache(bool isInProcessCache, CompletionHandler<void()>&& completionHandler)
{
#if PLATFORM(COCOA)
if (isInProcessCache) {
Expand All @@ -690,6 +690,7 @@ void WebProcess::setIsInProcessCache(bool isInProcessCache)
#else
UNUSED_PARAM(isInProcessCache);
#endif
completionHandler();
}

void WebProcess::markIsNoLongerPrewarmed()
Expand Down Expand Up @@ -1523,6 +1524,7 @@ void WebProcess::pageActivityStateDidChange(PageIdentifier, OptionSet<WebCore::A
void WebProcess::releaseMemory(CompletionHandler<void()>&& completionHandler)
{
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "releaseMemory: BEGIN");
SetForScope allowExitScope(m_allowExitOnMemoryPressure, false);
MemoryPressureHandler::singleton().releaseMemory(Critical::Yes, Synchronous::Yes);
for (auto& page : m_pageMap.values())
page->releaseMemory(Critical::Yes);
Expand All @@ -1537,7 +1539,7 @@ void WebProcess::prepareToSuspend(bool isSuspensionImminent, MonotonicTime estim
double remainingRunTime = nowTime > estimatedSuspendTime ? (nowTime - estimatedSuspendTime).value() : 0.0;
#endif
WEBPROCESS_RELEASE_LOG(ProcessSuspension, "prepareToSuspend: isSuspensionImminent=%d, remainingRunTime=%fs", isSuspensionImminent, remainingRunTime);
SetForScope suspensionScope(m_isSuspending, true);
SetForScope allowExitScope(m_allowExitOnMemoryPressure, false);
m_processIsSuspended = true;

flushResourceLoadStatistics();
Expand Down
4 changes: 2 additions & 2 deletions Source/WebKit/WebProcess/WebProcess.h
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ class WebProcess : public AuxiliaryProcess
void platformTerminate();

void setHasSuspendedPageProxy(bool);
void setIsInProcessCache(bool);
void setIsInProcessCache(bool, CompletionHandler<void()>&&);
void markIsNoLongerPrewarmed();

void registerURLSchemeAsEmptyDocument(const String&);
Expand Down Expand Up @@ -781,7 +781,7 @@ class WebProcess : public AuxiliaryProcess
#endif

bool m_hasSuspendedPageProxy { false };
bool m_isSuspending { false };
bool m_allowExitOnMemoryPressure { true };
bool m_isLockdownModeEnabled { false };

#if ENABLE(MEDIA_STREAM) && ENABLE(SANDBOX_EXTENSIONS)
Expand Down
2 changes: 1 addition & 1 deletion Source/WebKit/WebProcess/WebProcess.messages.in
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ messages -> WebProcess LegacyReceiver NotRefCounted {
EstablishRemoteWorkerContextConnectionToNetworkProcess(enum:uint8_t WebKit::RemoteWorkerType workerType, WebKit::PageGroupIdentifier pageGroupID, WebKit::WebPageProxyIdentifier webPageProxyID, WebCore::PageIdentifier pageID, struct WebKit::WebPreferencesStore store, WebCore::RegistrableDomain domain, std::optional<WebCore::ScriptExecutionContextIdentifier> serviceWorkerPageIdentifier, struct WebKit::RemoteWorkerInitializationData initializationData) -> ()

SetHasSuspendedPageProxy(bool hasSuspendedPageProxy);
SetIsInProcessCache(bool isInProcessCache)
SetIsInProcessCache(bool isInProcessCache) -> ()
MarkIsNoLongerPrewarmed()
GetActivePagesOriginsForTesting() -> (Vector<String> activeOrigins)

Expand Down

0 comments on commit b0259bf

Please sign in to comment.