/* * Copyright (C) 2017 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. ``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 * 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. */ #import "config.h" #import "WebSQLiteDatabaseTrackerClient.h" #if PLATFORM(IOS_FAMILY) #import "WebBackgroundTaskController.h" #import #import #import #import #import @interface WebDatabaseTransactionBackgroundTaskController : NSObject + (void)startBackgroundTask; + (void)endBackgroundTask; @end namespace WebCore { const Seconds hysteresisDuration { 2_s }; WebSQLiteDatabaseTrackerClient& WebSQLiteDatabaseTrackerClient::sharedWebSQLiteDatabaseTrackerClient() { static NeverDestroyed client; return client; } WebSQLiteDatabaseTrackerClient::WebSQLiteDatabaseTrackerClient() : m_hysteresis([this](PAL::HysteresisState state) { hysteresisUpdated(state); }, hysteresisDuration) { ASSERT(pthread_main_np()); } WebSQLiteDatabaseTrackerClient::~WebSQLiteDatabaseTrackerClient() { } void WebSQLiteDatabaseTrackerClient::willBeginFirstTransaction() { RunLoop::main().dispatch([this] { m_hysteresis.start(); }); } void WebSQLiteDatabaseTrackerClient::didFinishLastTransaction() { RunLoop::main().dispatch([this] { m_hysteresis.stop(); }); } void WebSQLiteDatabaseTrackerClient::hysteresisUpdated(PAL::HysteresisState state) { ASSERT(pthread_main_np()); if (state == PAL::HysteresisState::Started) [WebDatabaseTransactionBackgroundTaskController startBackgroundTask]; else [WebDatabaseTransactionBackgroundTaskController endBackgroundTask]; } } static Lock transactionBackgroundTaskIdentifierLock; static NSUInteger transactionBackgroundTaskIdentifier WTF_GUARDED_BY_LOCK(transactionBackgroundTaskIdentifierLock); static void setTransactionBackgroundTaskIdentifier(NSUInteger identifier) WTF_REQUIRES_LOCK(transactionBackgroundTaskIdentifierLock) { transactionBackgroundTaskIdentifier = identifier; } static NSUInteger getTransactionBackgroundTaskIdentifier() WTF_REQUIRES_LOCK(transactionBackgroundTaskIdentifierLock) { static dispatch_once_t pred; dispatch_once(&pred, ^ { setTransactionBackgroundTaskIdentifier([[WebBackgroundTaskController sharedController] invalidBackgroundTaskIdentifier]); }); return transactionBackgroundTaskIdentifier; } @implementation WebDatabaseTransactionBackgroundTaskController + (void)startBackgroundTask { Locker locker { transactionBackgroundTaskIdentifierLock }; // If there's already an existing background task going on, there's no need to start a new one. WebBackgroundTaskController *backgroundTaskController = [WebBackgroundTaskController sharedController]; if (getTransactionBackgroundTaskIdentifier() != [backgroundTaskController invalidBackgroundTaskIdentifier]) return; setTransactionBackgroundTaskIdentifier([backgroundTaskController startBackgroundTaskWithExpirationHandler:(^ { WebCore::DatabaseTracker::singleton().closeAllDatabases(WebCore::CurrentQueryBehavior::Interrupt); [self endBackgroundTask]; })]); } + (void)endBackgroundTask { Locker locker { transactionBackgroundTaskIdentifierLock }; // It is possible that we were unable to start the background task when the first transaction began. // Don't try to end the task in that case. // It is also possible we finally finish the last transaction right when the background task expires // and this will end up being called twice for the same background task. transactionBackgroundTaskIdentifier // will be invalid for the second caller. WebBackgroundTaskController *backgroundTaskController = [WebBackgroundTaskController sharedController]; if (getTransactionBackgroundTaskIdentifier() == [backgroundTaskController invalidBackgroundTaskIdentifier]) return; [backgroundTaskController endBackgroundTaskWithIdentifier:getTransactionBackgroundTaskIdentifier()]; setTransactionBackgroundTaskIdentifier([backgroundTaskController invalidBackgroundTaskIdentifier]); } @end #endif // PLATFORM(IOS_FAMILY)