/* * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO , PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "ProcessThrottler.h" #include "ProcessThrottlerClient.h" namespace WebKit { static const unsigned processSuspensionTimeout = 30; ProcessThrottler::ProcessThrottler(ProcessThrottlerClient& process) : m_process(process) , m_suspendTimer(RunLoop::main(), this, &ProcessThrottler::suspendTimerFired) , m_foregroundCounter([this](bool) { updateAssertion(); }) , m_backgroundCounter([this](bool) { updateAssertion(); }) , m_suspendMessageCount(0) { } AssertionState ProcessThrottler::assertionState() { ASSERT(!m_suspendTimer.isActive()); if (m_foregroundCounter.value()) return AssertionState::Foreground; if (m_backgroundCounter.value()) return AssertionState::Background; return AssertionState::Suspended; } void ProcessThrottler::updateAssertionNow() { m_suspendTimer.stop(); if (m_assertion) { m_assertion->setState(assertionState()); m_process.didSetAssertionState(assertionState()); } } void ProcessThrottler::updateAssertion() { // If the process is currently runnable but will be suspended then first give it a chance to complete what it was doing // and clean up - move it to the background and send it a message to notify. Schedule a timeout so it can't stay running // in the background for too long. if (m_assertion && m_assertion->state() != AssertionState::Suspended && !m_foregroundCounter.value() && !m_backgroundCounter.value()) { ++m_suspendMessageCount; m_process.sendPrepareToSuspend(); m_suspendTimer.startOneShot(processSuspensionTimeout); m_assertion->setState(AssertionState::Background); m_process.didSetAssertionState(AssertionState::Background); return; } bool shouldBeRunnable = m_foregroundCounter.value() || m_backgroundCounter.value(); // If we're currently waiting for the Web process to do suspension cleanup, but no longer need to be suspended, tell the Web process to cancel the cleanup. if (m_suspendTimer.isActive() && shouldBeRunnable) m_process.sendCancelPrepareToSuspend(); if (m_assertion && m_assertion->state() == AssertionState::Suspended && shouldBeRunnable) m_process.sendProcessDidResume(); updateAssertionNow(); } void ProcessThrottler::didConnectToProcess(PlatformProcessIdentifier pid) { m_suspendTimer.stop(); m_assertion = std::make_unique(pid, assertionState()); m_process.didSetAssertionState(assertionState()); m_assertion->setClient(*this); } void ProcessThrottler::suspendTimerFired() { updateAssertionNow(); } void ProcessThrottler::processReadyToSuspend() { if (!--m_suspendMessageCount) updateAssertionNow(); ASSERT(m_suspendMessageCount >= 0); } void ProcessThrottler::didCancelProcessSuspension() { if (!--m_suspendMessageCount) updateAssertionNow(); ASSERT(m_suspendMessageCount >= 0); } void ProcessThrottler::assertionWillExpireImminently() { m_process.sendProcessWillSuspendImminently(); } }