[iOS-WK1] Fix thread safety issue in WebSQLiteDatabaseTrackerClient
authorcdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 4 Nov 2017 01:57:57 +0000 (01:57 +0000)
committercdumez@apple.com <cdumez@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 4 Nov 2017 01:57:57 +0000 (01:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=179190

Reviewed by David Kilzer.

WebSQLiteDatabaseTrackerClient and its HystererisActivity member are constructed on the UIThread. The
HystererisActivity activity also fires on the UIThread, which means that WebSQLiteDatabaseTrackerClient::hysteresisUpdated()
gets called on the UIThread.

However, the code in WebSQLiteDatabaseTrackerClient::willBeginFirstTransaction() / WebSQLiteDatabaseTrackerClient::didFinishLastTransaction()
uses callOnMainThread() before calling methods on the HysteresisActivity. callOnMainThread() dispatches to the WebThread on WK1 iOS, which
would lead to crashes when calling methods of the HystererisActivity object:
*** -[CFRunLoopTimer respondsToSelector:]: message sent to deallocated instance 0x1c0b6a500

To address the issue, we now dispatch_async() to the main queue in willBeginFirstTransaction() / didFinishLastTransaction()
instead of using callOnMainThread(). I also added assertions to catch issues like these.

* platform/ios/WebSQLiteDatabaseTrackerClient.mm:
(WebCore::WebSQLiteDatabaseTrackerClient::willBeginFirstTransaction):
(WebCore::WebSQLiteDatabaseTrackerClient::didFinishLastTransaction):
(WebCore::WebSQLiteDatabaseTrackerClient::hysteresisUpdated):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@224452 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/WebCore/ChangeLog
Source/WebCore/platform/ios/WebSQLiteDatabaseTrackerClient.mm

index 0f9cea9..9e90ba4 100644 (file)
@@ -1,3 +1,27 @@
+2017-11-03  Chris Dumez  <cdumez@apple.com>
+
+        [iOS-WK1] Fix thread safety issue in WebSQLiteDatabaseTrackerClient
+        https://bugs.webkit.org/show_bug.cgi?id=179190
+
+        Reviewed by David Kilzer.
+
+        WebSQLiteDatabaseTrackerClient and its HystererisActivity member are constructed on the UIThread. The
+        HystererisActivity activity also fires on the UIThread, which means that WebSQLiteDatabaseTrackerClient::hysteresisUpdated()
+        gets called on the UIThread.
+
+        However, the code in WebSQLiteDatabaseTrackerClient::willBeginFirstTransaction() / WebSQLiteDatabaseTrackerClient::didFinishLastTransaction()
+        uses callOnMainThread() before calling methods on the HysteresisActivity. callOnMainThread() dispatches to the WebThread on WK1 iOS, which
+        would lead to crashes when calling methods of the HystererisActivity object:
+        *** -[CFRunLoopTimer respondsToSelector:]: message sent to deallocated instance 0x1c0b6a500
+
+        To address the issue, we now dispatch_async() to the main queue in willBeginFirstTransaction() / didFinishLastTransaction()
+        instead of using callOnMainThread(). I also added assertions to catch issues like these.
+
+        * platform/ios/WebSQLiteDatabaseTrackerClient.mm:
+        (WebCore::WebSQLiteDatabaseTrackerClient::willBeginFirstTransaction):
+        (WebCore::WebSQLiteDatabaseTrackerClient::didFinishLastTransaction):
+        (WebCore::WebSQLiteDatabaseTrackerClient::hysteresisUpdated):
+
 2017-11-03  Ryosuke Niwa  <rniwa@webkit.org>
 
         ASSERTION FAILED: NoEventDispatchAssertion::InMainThread::isEventAllowed() || (frameView && frameView->isInChildFrameWithFrameFlattening())
index 07d3d5d..4bd3fa1 100644 (file)
@@ -52,6 +52,7 @@ WebSQLiteDatabaseTrackerClient& WebSQLiteDatabaseTrackerClient::sharedWebSQLiteD
 WebSQLiteDatabaseTrackerClient::WebSQLiteDatabaseTrackerClient()
     : m_hysteresis([this](PAL::HysteresisState state) { hysteresisUpdated(state); }, hysteresisDuration)
 {
+    ASSERT(pthread_main_np());
 }
 
 WebSQLiteDatabaseTrackerClient::~WebSQLiteDatabaseTrackerClient()
@@ -60,20 +61,21 @@ WebSQLiteDatabaseTrackerClient::~WebSQLiteDatabaseTrackerClient()
 
 void WebSQLiteDatabaseTrackerClient::willBeginFirstTransaction()
 {
-    callOnMainThread([this] {
+    dispatch_async(dispatch_get_main_queue(), [this] {
         m_hysteresis.start();
     });
 }
 
 void WebSQLiteDatabaseTrackerClient::didFinishLastTransaction()
 {
-    callOnMainThread([this] {
+    dispatch_async(dispatch_get_main_queue(), [this] {
         m_hysteresis.stop();
     });
 }
 
 void WebSQLiteDatabaseTrackerClient::hysteresisUpdated(PAL::HysteresisState state)
 {
+    ASSERT(pthread_main_np());
     if (state == PAL::HysteresisState::Started)
         [WebDatabaseTransactionBackgroundTaskController startBackgroundTask];
     else