Use StaticLock instead of NeverDestroyed<Lock>
[WebKit-https.git] / Source / WebCore / platform / ios / WebSQLiteDatabaseTrackerClient.mm
1 /*
2  * Copyright (C) 2017 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebSQLiteDatabaseTrackerClient.h"
28
29 #if PLATFORM(IOS)
30
31 #import "WebBackgroundTaskController.h"
32 #import <WebCore/DatabaseTracker.h>
33 #import <WebCore/SQLiteDatabaseTracker.h>
34 #import <wtf/MainThread.h>
35 #import <wtf/NeverDestroyed.h>
36
37 @interface WebDatabaseTransactionBackgroundTaskController : NSObject
38 + (void)startBackgroundTask;
39 + (void)endBackgroundTask;
40 @end
41
42 namespace WebCore {
43
44 const Seconds hysteresisDuration { 2_s };
45
46 WebSQLiteDatabaseTrackerClient& WebSQLiteDatabaseTrackerClient::sharedWebSQLiteDatabaseTrackerClient()
47 {
48     static NeverDestroyed<WebSQLiteDatabaseTrackerClient> client;
49     return client;
50 }
51
52 WebSQLiteDatabaseTrackerClient::WebSQLiteDatabaseTrackerClient()
53     : m_hysteresis([this](PAL::HysteresisState state) { hysteresisUpdated(state); }, hysteresisDuration)
54 {
55     ASSERT(pthread_main_np());
56 }
57
58 WebSQLiteDatabaseTrackerClient::~WebSQLiteDatabaseTrackerClient()
59 {
60 }
61
62 void WebSQLiteDatabaseTrackerClient::willBeginFirstTransaction()
63 {
64     dispatch_async(dispatch_get_main_queue(), [this] {
65         m_hysteresis.start();
66     });
67 }
68
69 void WebSQLiteDatabaseTrackerClient::didFinishLastTransaction()
70 {
71     dispatch_async(dispatch_get_main_queue(), [this] {
72         m_hysteresis.stop();
73     });
74 }
75
76 void WebSQLiteDatabaseTrackerClient::hysteresisUpdated(PAL::HysteresisState state)
77 {
78     ASSERT(pthread_main_np());
79     if (state == PAL::HysteresisState::Started)
80         [WebDatabaseTransactionBackgroundTaskController startBackgroundTask];
81     else
82         [WebDatabaseTransactionBackgroundTaskController endBackgroundTask];
83 }
84
85 }
86
87 static StaticLock transactionBackgroundTaskIdentifierLock;
88
89 static NSUInteger transactionBackgroundTaskIdentifier;
90
91 static void setTransactionBackgroundTaskIdentifier(NSUInteger identifier)
92 {
93     transactionBackgroundTaskIdentifier = identifier;
94 }
95
96 static NSUInteger getTransactionBackgroundTaskIdentifier()
97 {
98     static dispatch_once_t pred;
99     dispatch_once(&pred, ^ {
100         setTransactionBackgroundTaskIdentifier([[WebBackgroundTaskController sharedController] invalidBackgroundTaskIdentifier]);
101     });
102
103     return transactionBackgroundTaskIdentifier;
104 }
105
106 @implementation WebDatabaseTransactionBackgroundTaskController
107
108 + (void)startBackgroundTask
109 {
110     auto locker = holdLock(transactionBackgroundTaskIdentifierLock);
111
112     // If there's already an existing background task going on, there's no need to start a new one.
113     WebBackgroundTaskController *backgroundTaskController = [WebBackgroundTaskController sharedController];
114     if (getTransactionBackgroundTaskIdentifier() != [backgroundTaskController invalidBackgroundTaskIdentifier])
115         return;
116
117     setTransactionBackgroundTaskIdentifier([backgroundTaskController startBackgroundTaskWithExpirationHandler:(^ {
118         WebCore::DatabaseTracker::singleton().closeAllDatabases(WebCore::CurrentQueryBehavior::Interrupt);
119         [self endBackgroundTask];
120     })]);
121 }
122
123 + (void)endBackgroundTask
124 {
125     auto locker = holdLock(transactionBackgroundTaskIdentifierLock);
126
127     // It is possible that we were unable to start the background task when the first transaction began.
128     // Don't try to end the task in that case.
129     // It is also possible we finally finish the last transaction right when the background task expires
130     // and this will end up being called twice for the same background task. transactionBackgroundTaskIdentifier
131     // will be invalid for the second caller.
132     WebBackgroundTaskController *backgroundTaskController = [WebBackgroundTaskController sharedController];
133     if (getTransactionBackgroundTaskIdentifier() == [backgroundTaskController invalidBackgroundTaskIdentifier])
134         return;
135
136     [backgroundTaskController endBackgroundTaskWithIdentifier:getTransactionBackgroundTaskIdentifier()];
137     setTransactionBackgroundTaskIdentifier([backgroundTaskController invalidBackgroundTaskIdentifier]);
138 }
139
140 @end
141
142 #endif // PLATFORM(IOS)