Title: [231968] trunk
Revision
231968
Author
[email protected]
Date
2018-05-18 11:28:37 -0700 (Fri, 18 May 2018)

Log Message

[Curl] Bug fix on suspend/resume behavior.
https://bugs.webkit.org/show_bug.cgi?id=183089

The flag was not set correctly. Also wrong method was called.

Patch by Basuke Suzuki <[email protected]> on 2018-05-18
Reviewed by Youenn Fablet.

Source/WebCore:

Enable loader tests to cover this case.

* platform/network/curl/CurlRequest.cpp:
(WebCore::CurlRequest::cancel): Remove unnecessary cleanup. Use runXXX method.
(WebCore::CurlRequest::suspend): Added cancel check.
(WebCore::CurlRequest::resume): Ditto.
(WebCore::CurlRequest::callClient): Use runXXX method. Change to move semantics.
(WebCore::runOnMainThread): Added.
(WebCore::CurlRequest::runOnWorkerThreadIfRequired): Added.
(WebCore::CurlRequest::setupTransfer): Bug fix. Call setRequestPaused directly.
(WebCore::CurlRequest::didReceiveData): Add state flag update.
(WebCore::CurlRequest::invokeDidReceiveResponseForFile): Use runXXX to simplify.
(WebCore::CurlRequest::completeDidReceiveResponse): Ditto.
(WebCore::CurlRequest::setRequestPaused): Protect state change by mutex.
(WebCore::CurlRequest::setCallbackPaused): Ditto.
(WebCore::CurlRequest::invokeCancel): Added.
(WebCore::CurlRequest::pausedStatusChanged): Use runXXX to simplify.
(WebCore::CurlRequest::updateHandlePauseState): Accessor for m_isHandlePaused.
(WebCore::CurlRequest::isHandlePaused const): Ditto.
* platform/network/curl/CurlRequest.h: Add mutex and paused state.
(WebCore::CurlRequest::shouldBePaused const): Rename from isPaused.
(WebCore::CurlRequest::isPaused const): Deleted.

LayoutTests:

* platform/wincairo/TestExpectations: Enable loader/ tests for WinCairo.

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (231967 => 231968)


--- trunk/LayoutTests/ChangeLog	2018-05-18 18:23:45 UTC (rev 231967)
+++ trunk/LayoutTests/ChangeLog	2018-05-18 18:28:37 UTC (rev 231968)
@@ -1,3 +1,14 @@
+2018-05-18  Basuke Suzuki  <[email protected]>
+
+        [Curl] Bug fix on suspend/resume behavior.
+        https://bugs.webkit.org/show_bug.cgi?id=183089
+
+        The flag was not set correctly. Also wrong method was called.
+
+        Reviewed by Youenn Fablet.
+
+        * platform/wincairo/TestExpectations: Enable loader/ tests for WinCairo.
+
 2018-05-18  Wenson Hsieh  <[email protected]>
 
         [Extra zoom mode] Clearing text fields should dispatch input events of type "deleteContent"

Modified: trunk/LayoutTests/platform/wincairo/TestExpectations (231967 => 231968)


--- trunk/LayoutTests/platform/wincairo/TestExpectations	2018-05-18 18:23:45 UTC (rev 231967)
+++ trunk/LayoutTests/platform/wincairo/TestExpectations	2018-05-18 18:28:37 UTC (rev 231968)
@@ -1561,7 +1561,9 @@
 legacy-animation-engine/fast/layers [ Skip ]
 legacy-animation-engine/fast/shadow-dom [ Skip ]
 legacy-animation-engine/fullscreen [ Skip ]
-loader [ Skip ]
+loader/reload-subresource-when-type-changes.html [ Skip ]
+loader/stateobjects/pushstate-size-iframe.html [ Skip ]
+loader/stateobjects/pushstate-size.html [ Skip ]
 mathml [ Skip ]
 media [ Skip ]
 pageoverlay [ Skip ]

Modified: trunk/Source/WebCore/ChangeLog (231967 => 231968)


--- trunk/Source/WebCore/ChangeLog	2018-05-18 18:23:45 UTC (rev 231967)
+++ trunk/Source/WebCore/ChangeLog	2018-05-18 18:28:37 UTC (rev 231968)
@@ -1,3 +1,35 @@
+2018-05-18  Basuke Suzuki  <[email protected]>
+
+        [Curl] Bug fix on suspend/resume behavior.
+        https://bugs.webkit.org/show_bug.cgi?id=183089
+
+        The flag was not set correctly. Also wrong method was called.
+
+        Reviewed by Youenn Fablet.
+
+        Enable loader tests to cover this case.
+
+        * platform/network/curl/CurlRequest.cpp:
+        (WebCore::CurlRequest::cancel): Remove unnecessary cleanup. Use runXXX method.
+        (WebCore::CurlRequest::suspend): Added cancel check.
+        (WebCore::CurlRequest::resume): Ditto.
+        (WebCore::CurlRequest::callClient): Use runXXX method. Change to move semantics.
+        (WebCore::runOnMainThread): Added.
+        (WebCore::CurlRequest::runOnWorkerThreadIfRequired): Added.
+        (WebCore::CurlRequest::setupTransfer): Bug fix. Call setRequestPaused directly.
+        (WebCore::CurlRequest::didReceiveData): Add state flag update.
+        (WebCore::CurlRequest::invokeDidReceiveResponseForFile): Use runXXX to simplify.
+        (WebCore::CurlRequest::completeDidReceiveResponse): Ditto.
+        (WebCore::CurlRequest::setRequestPaused): Protect state change by mutex.
+        (WebCore::CurlRequest::setCallbackPaused): Ditto.
+        (WebCore::CurlRequest::invokeCancel): Added.
+        (WebCore::CurlRequest::pausedStatusChanged): Use runXXX to simplify.
+        (WebCore::CurlRequest::updateHandlePauseState): Accessor for m_isHandlePaused.
+        (WebCore::CurlRequest::isHandlePaused const): Ditto.
+        * platform/network/curl/CurlRequest.h: Add mutex and paused state.
+        (WebCore::CurlRequest::shouldBePaused const): Rename from isPaused.
+        (WebCore::CurlRequest::isPaused const): Deleted.
+
 2018-05-18  Chris Dumez  <[email protected]>
 
         Avoid keeping the frame alive when ref'ing a WindowProxy

Modified: trunk/Source/WebCore/platform/network/curl/CurlRequest.cpp (231967 => 231968)


--- trunk/Source/WebCore/platform/network/curl/CurlRequest.cpp	2018-05-18 18:23:45 UTC (rev 231967)
+++ trunk/Source/WebCore/platform/network/curl/CurlRequest.cpp	2018-05-18 18:28:37 UTC (rev 231968)
@@ -38,6 +38,8 @@
 
 namespace WebCore {
 
+static void runOnMainThread(Function<void()>&& task);
+
 CurlRequest::CurlRequest(const ResourceRequest&request, CurlRequestClient* client, bool shouldSuspend, bool enableMultipart)
     : m_request(request.isolatedCopy())
     , m_client(client)
@@ -113,8 +115,8 @@
         auto& scheduler = CurlContext::singleton().scheduler();
 
         if (needToInvokeDidCancelTransfer()) {
-            scheduler.callOnWorkerThread([protectedThis = makeRef(*this)]() {
-                protectedThis->didCancelTransfer();
+            runOnWorkerThreadIfRequired([this, protectedThis = makeRef(*this)]() {
+                didCancelTransfer();
             });
         } else
             scheduler.cancel(this);
@@ -123,8 +125,6 @@
             didCancelTransfer();
     }
 
-    setRequestPaused(false);
-    setCallbackPaused(false);
     invalidateClient();
 }
 
@@ -132,6 +132,9 @@
 {
     ASSERT(isMainThread());
 
+    if (isCompletedOrCancelled())
+        return;
+
     setRequestPaused(true);
 }
 
@@ -139,27 +142,38 @@
 {
     ASSERT(isMainThread());
 
+    if (isCompletedOrCancelled())
+        return;
+
     setRequestPaused(false);
 }
 
 /* `this` is protected inside this method. */
-void CurlRequest::callClient(WTF::Function<void(CurlRequest&, CurlRequestClient&)> task)
+void CurlRequest::callClient(Function<void(CurlRequest&, CurlRequestClient&)>&& task)
 {
-    if (isMainThread()) {
+    runOnMainThread([this, protectedThis = makeRef(*this), task = WTFMove(task)]() mutable {
         if (CurlRequestClient* client = m_client) {
-            RefPtr<CurlRequestClient> protectedClient(client);
-            task(*this, *client);
+            task(*this, makeRef(*client));
         }
-    } else {
-        callOnMainThread([this, protectedThis = makeRef(*this), task = WTFMove(task)]() mutable {
-            if (CurlRequestClient* client = protectedThis->m_client) {
-                RefPtr<CurlRequestClient> protectedClient(client);
-                task(*this, *client);
-            }
-        });
-    }
+    });
 }
 
+static void runOnMainThread(Function<void()>&& task)
+{
+    if (isMainThread())
+        task();
+    else
+        callOnMainThread(WTFMove(task));
+}
+
+void CurlRequest::runOnWorkerThreadIfRequired(Function<void()>&& task)
+{
+    if (isMainThread() && !m_isSyncRequest)
+        CurlContext::singleton().scheduler().callOnWorkerThread(WTFMove(task));
+    else
+        task();
+}
+
 CURL* CurlRequest::setupTransfer()
 {
     auto& sslHandle = CurlContext::singleton().sslHandle();
@@ -228,7 +242,7 @@
     m_curlHandle->setCACertPath(sslHandle.getCACertPath().utf8().data());
 
     if (m_shouldSuspend)
-        suspend();
+        setRequestPaused(true);
 
 #ifndef NDEBUG
     m_curlHandle->enableVerboseIfUsed();
@@ -358,6 +372,9 @@
             // For asynchronous, pause until completeDidReceiveResponse() is called.
             setCallbackPaused(true);
             invokeDidReceiveResponse(m_response, Action::ReceiveData);
+            // Because libcurl pauses the handle after returning this CURL_WRITEFUNC_PAUSE,
+            // we need to update its state here.
+            updateHandlePauseState(true);
             return CURL_WRITEFUNC_PAUSE;
         }
 
@@ -537,8 +554,8 @@
 
     if (!m_isSyncRequest) {
         // DidReceiveResponse must not be called immediately
-        CurlContext::singleton().scheduler().callOnWorkerThread([protectedThis = makeRef(*this)]() {
-            protectedThis->invokeDidReceiveResponse(protectedThis->m_response, Action::StartTransfer);
+        runOnWorkerThreadIfRequired([this, protectedThis = makeRef(*this)]() {
+            invokeDidReceiveResponse(m_response, Action::StartTransfer);
         });
     } else {
         // For synchronous, completeDidReceiveResponse() is called in platformContinueSynchronousDidReceiveResponse().
@@ -580,8 +597,8 @@
         startWithJobManager();
     } else if (m_actionAfterInvoke == Action::FinishTransfer) {
         if (!m_isSyncRequest) {
-            CurlContext::singleton().scheduler().callOnWorkerThread([protectedThis = makeRef(*this), finishedResultCode = m_finishedResultCode]() {
-                protectedThis->didCompleteTransfer(finishedResultCode);
+            runOnWorkerThreadIfRequired([this, protectedThis = makeRef(*this), finishedResultCode = m_finishedResultCode]() {
+                didCompleteTransfer(finishedResultCode);
             });
         } else
             didCompleteTransfer(m_finishedResultCode);
@@ -590,57 +607,83 @@
 
 void CurlRequest::setRequestPaused(bool paused)
 {
-    auto wasPaused = isPaused();
+    {
+        LockHolder lock(m_pauseStateMutex);
 
-    m_isPausedOfRequest = paused;
+        auto savedState = shouldBePaused();
+        m_shouldSuspend = m_isPausedOfRequest = paused;
+        if (shouldBePaused() == savedState)
+            return;
+    }
 
-    if (isPaused() == wasPaused)
-        return;
-
     pausedStatusChanged();
 }
 
 void CurlRequest::setCallbackPaused(bool paused)
 {
-    auto wasPaused = isPaused();
+    {
+        LockHolder lock(m_pauseStateMutex);
 
-    m_isPausedOfCallback = paused;
+        auto savedState = shouldBePaused();
+        m_isPausedOfCallback = paused;
 
-    if (isPaused() == wasPaused)
-        return;
+        // If pause is requested, it is called within didReceiveData() which means
+        // actual change happens inside libcurl. No need to update manually here.
+        if (shouldBePaused() == savedState || paused)
+            return;
+    }
 
-    // In this case, PAUSE will be executed within didReceiveData(). Change pause state and return.
-    if (paused)
-        return;
-
     pausedStatusChanged();
 }
 
+void CurlRequest::invokeCancel()
+{
+    // There's no need to extract this method. This is a workaround for MSVC's bug
+    // which happens when using lambda inside other lambda. The compiler loses context
+    // of `this` which prevent makeRef.
+    runOnMainThread([this, protectedThis = makeRef(*this)]() {
+        cancel();
+    });
+}
+
 void CurlRequest::pausedStatusChanged()
 {
-    if (isCompletedOrCancelled())
-        return;
+    runOnWorkerThreadIfRequired([this, protectedThis = makeRef(*this)]() {
+        if (isCompletedOrCancelled())
+            return;
 
-    if (!m_isSyncRequest && isMainThread()) {
-        CurlContext::singleton().scheduler().callOnWorkerThread([protectedThis = makeRef(*this), paused = isPaused()]() {
-            if (protectedThis->isCompletedOrCancelled())
+        bool needCancel { false };
+        {
+            LockHolder lock(m_pauseStateMutex);
+            bool paused = shouldBePaused();
+
+            if (isHandlePaused() == paused)
                 return;
 
-            auto error = protectedThis->m_curlHandle->pause(paused ? CURLPAUSE_ALL : CURLPAUSE_CONT);
-            if ((error != CURLE_OK) && !paused) {
-                // Restarting the handle has failed so just cancel it.
-                callOnMainThread([protectedThis = makeRef(protectedThis.get())]() {
-                    protectedThis->cancel();
-                });
-            }
-        });
-    } else {
-        auto error = m_curlHandle->pause(isPaused() ? CURLPAUSE_ALL : CURLPAUSE_CONT);
-        if ((error != CURLE_OK) && !isPaused())
-            cancel();
-    }
+            auto error = m_curlHandle->pause(paused ? CURLPAUSE_ALL : CURLPAUSE_CONT);
+            if (error == CURLE_OK)
+                updateHandlePauseState(paused);
+
+            needCancel = (error != CURLE_OK && paused);
+        }
+
+        if (needCancel)
+            invokeCancel();
+    });
 }
 
+void CurlRequest::updateHandlePauseState(bool paused)
+{
+    ASSERT(!isMainThread() || m_isSyncRequest);
+    m_isHandlePaused = paused;
+}
+
+bool CurlRequest::isHandlePaused() const
+{
+    ASSERT(!isMainThread() || m_isSyncRequest);
+    return m_isHandlePaused;
+}
+
 void CurlRequest::enableDownloadToFile()
 {
     LockHolder locker(m_downloadMutex);

Modified: trunk/Source/WebCore/platform/network/curl/CurlRequest.h (231967 => 231968)


--- trunk/Source/WebCore/platform/network/curl/CurlRequest.h	2018-05-18 18:23:45 UTC (rev 231967)
+++ trunk/Source/WebCore/platform/network/curl/CurlRequest.h	2018-05-18 18:28:37 UTC (rev 231968)
@@ -105,7 +105,8 @@
 
     void startWithJobManager();
 
-    void callClient(WTF::Function<void(CurlRequest&, CurlRequestClient&)>);
+    void callClient(Function<void(CurlRequest&, CurlRequestClient&)>&&);
+    void runOnWorkerThreadIfRequired(Function<void()>&&);
 
     // Transfer processing of Request body, Response header/body
     // Called by worker thread in case of async, main thread in case of sync.
@@ -119,6 +120,7 @@
     void didCompleteTransfer(CURLcode) override;
     void didCancelTransfer() override;
     void finalizeTransfer();
+    void invokeCancel();
 
     // For setup 
     void appendAcceptLanguageHeader(HTTPHeaderMap&);
@@ -134,7 +136,9 @@
     void setRequestPaused(bool);
     void setCallbackPaused(bool);
     void pausedStatusChanged();
-    bool isPaused() const { return m_isPausedOfRequest || m_isPausedOfCallback; };
+    bool shouldBePaused() const { return m_isPausedOfRequest || m_isPausedOfCallback; };
+    void updateHandlePauseState(bool);
+    bool isHandlePaused() const;
 
     // Download
     void writeDataToDownloadFileIfEnabled(const SharedBuffer&);
@@ -173,6 +177,16 @@
 
     bool m_isPausedOfRequest { false };
     bool m_isPausedOfCallback { false };
+    Lock m_pauseStateMutex;
+    // Following `m_isHandlePaused` is actual paused state of CurlHandle. It's required because pause
+    // request coming from main thread has a time lag until it invokes and receive callback can
+    // change the state by returning a special value. So that is must be managed by this flag.
+    // Unfortunately libcurl doesn't have an interface to check the state.
+    // There's also no need to protect this flag by the mutex because it is and MUST BE accessed only
+    // within worker thread. The access must be using accessor to detect irregular usage.
+    // [TODO] When libcurl is updated to fetch paused state, remove this state variable and
+    // setter/getter above.
+    bool m_isHandlePaused { false };
 
     Lock m_downloadMutex;
     bool m_isEnabledDownloadToFile { false };
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to