<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[204718] releases/WebKitGTK/webkit-2.12/Source/WebCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/204718">204718</a></dd>
<dt>Author</dt> <dd>carlosgc@webkit.org</dd>
<dt>Date</dt> <dd>2016-08-22 09:01:15 -0700 (Mon, 22 Aug 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge <a href="http://trac.webkit.org/projects/webkit/changeset/202615">r202615</a> - [GStreamer] Adaptive streaming issues
https://bugs.webkit.org/show_bug.cgi?id=144040

Reviewed by Philippe Normand.

There are multiple deadlocks in the web process when HLS content is loaded by GStreamer. It happens because gst
is using several threads to download manifest, fragments, monitor the downloads, etc. To download the fragments
and manifest it always creates the source element in a separate thread, something that is not actually expected
to happen in WebKit source element. Our source element is always scheduling tasks (start, stop, need-data,
enough-data and seek) to the main thread, and those downloads that use the ResourceHandleStreamingClient
(there's no player associated) also happen in the main thread, because libsoup calls all its async callbacks in
the main thread. So, the result is that it can happen that we end up blocking the main thread in a lock until
the download finishes, but the download never finishes because tasks are scheduled in the main thread that is
blocked in a lock. This can be prevented by always using a secondary thread for downloads made by
ResourceHandleStreamingClient, using its own run loop with a different GMainContext so that libsoup sends
callbacks to the right thread. We also had to refactor the tasks a bit, leaving the thread safe parts to be run
in the calling thread always, and only scheduling to the main thread in case of not using
ResourceHandleStreamingClient and only for the non thread safe parts.
This patch also includes <a href="http://trac.webkit.org/projects/webkit/changeset/200455">r200455</a> that was rolled out, but it was a perfectly valid workaround for GST bug.

* platform/graphics/gstreamer/GRefPtrGStreamer.cpp:
(WTF::ensureGRef): Consume the floating ref if needed.
* platform/graphics/gstreamer/GRefPtrGStreamer.h:
* platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp:
(webkit_web_src_init): Check if object is being created in the main thread.
(webKitWebSrcStop): Stop the media resource loader in the main thread and the resource handle streaming in the
current thread.
(webKitWebSrcStart): Start the media resource loader in the main thread and the resource handle streaming in
the current thread.
(webKitWebSrcChangeState): Call webKitWebSrcStart and webKitWebSrcStop in the current thread.
(webKitWebSrcNeedData): Update status in the current thread and notify the media resource loader in the main thread.
(webKitWebSrcEnoughData): Ditto.
(webKitWebSrcSeek): Ditto.
(webKitWebSrcSetMediaPlayer): Add an assert to ensure that source elements used by WebKit are always created in
the main thread.
(ResourceHandleStreamingClient::ResourceHandleStreamingClient): Use a secondary thread to do the download.
(ResourceHandleStreamingClient::~ResourceHandleStreamingClient): Stop the secondary thread.
(ResourceHandleStreamingClient::setDefersLoading): Notify the secondary thread.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#releasesWebKitGTKwebkit212SourceWebCoreChangeLog">releases/WebKitGTK/webkit-2.12/Source/WebCore/ChangeLog</a></li>
<li><a href="#releasesWebKitGTKwebkit212SourceWebCoreplatformgraphicsgstreamerWebKitWebSourceGStreamercpp">releases/WebKitGTK/webkit-2.12/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="releasesWebKitGTKwebkit212SourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.12/Source/WebCore/ChangeLog (204717 => 204718)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.12/Source/WebCore/ChangeLog        2016-08-22 15:51:58 UTC (rev 204717)
+++ releases/WebKitGTK/webkit-2.12/Source/WebCore/ChangeLog        2016-08-22 16:01:15 UTC (rev 204718)
</span><span class="lines">@@ -1,3 +1,57 @@
</span><ins>+2016-06-28  Carlos Garcia Campos  &lt;cgarcia@igalia.com&gt;
+
+        [GStreamer] Adaptive streaming issues
+        https://bugs.webkit.org/show_bug.cgi?id=144040
+
+        Reviewed by Philippe Normand.
+
+        There are multiple deadlocks in the web process when HLS content is loaded by GStreamer. It happens because gst
+        is using several threads to download manifest, fragments, monitor the downloads, etc. To download the fragments
+        and manifest it always creates the source element in a separate thread, something that is not actually expected
+        to happen in WebKit source element. Our source element is always scheduling tasks (start, stop, need-data,
+        enough-data and seek) to the main thread, and those downloads that use the ResourceHandleStreamingClient
+        (there's no player associated) also happen in the main thread, because libsoup calls all its async callbacks in
+        the main thread. So, the result is that it can happen that we end up blocking the main thread in a lock until
+        the download finishes, but the download never finishes because tasks are scheduled in the main thread that is
+        blocked in a lock. This can be prevented by always using a secondary thread for downloads made by
+        ResourceHandleStreamingClient, using its own run loop with a different GMainContext so that libsoup sends
+        callbacks to the right thread. We also had to refactor the tasks a bit, leaving the thread safe parts to be run
+        in the calling thread always, and only scheduling to the main thread in case of not using
+        ResourceHandleStreamingClient and only for the non thread safe parts.
+        This patch also includes r200455 that was rolled out, but it was a perfectly valid workaround for GST bug.
+
+        * platform/graphics/gstreamer/GRefPtrGStreamer.cpp:
+        (WTF::ensureGRef): Consume the floating ref if needed.
+        * platform/graphics/gstreamer/GRefPtrGStreamer.h:
+        * platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp:
+        (webkit_web_src_init): Check if object is being created in the main thread.
+        (webKitWebSrcStop): Stop the media resource loader in the main thread and the resource handle streaming in the
+        current thread.
+        (webKitWebSrcStart): Start the media resource loader in the main thread and the resource handle streaming in
+        the current thread.
+        (webKitWebSrcChangeState): Call webKitWebSrcStart and webKitWebSrcStop in the current thread.
+        (webKitWebSrcNeedData): Update status in the current thread and notify the media resource loader in the main thread.
+        (webKitWebSrcEnoughData): Ditto.
+        (webKitWebSrcSeek): Ditto.
+        (webKitWebSrcSetMediaPlayer): Add an assert to ensure that source elements used by WebKit are always created in
+        the main thread.
+        (ResourceHandleStreamingClient::ResourceHandleStreamingClient): Use a secondary thread to do the download.
+        (ResourceHandleStreamingClient::~ResourceHandleStreamingClient): Stop the secondary thread.
+        (ResourceHandleStreamingClient::setDefersLoading): Notify the secondary thread.
+
+2016-06-14  Commit Queue  &lt;commit-queue@webkit.org&gt;
+
+        Unreviewed, rolling out r200455.
+        https://bugs.webkit.org/show_bug.cgi?id=158740
+
+        hangs twitter/facebook (Requested by mcatanzaro on #webkit).
+
+        Reverted changeset:
+
+        &quot;[GStreamer] Adaptive streaming issues&quot;
+        https://bugs.webkit.org/show_bug.cgi?id=144040
+        http://trac.webkit.org/changeset/200455
+
</ins><span class="cx"> 2016-05-25  Joanmarie Diggs  &lt;jdiggs@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] accessibility/meter-element.html is failing
</span></span></pre></div>
<a id="releasesWebKitGTKwebkit212SourceWebCoreplatformgraphicsgstreamerWebKitWebSourceGStreamercpp"></a>
<div class="modfile"><h4>Modified: releases/WebKitGTK/webkit-2.12/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp (204717 => 204718)</h4>
<pre class="diff"><span>
<span class="info">--- releases/WebKitGTK/webkit-2.12/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp        2016-08-22 15:51:58 UTC (rev 204717)
+++ releases/WebKitGTK/webkit-2.12/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp        2016-08-22 16:01:15 UTC (rev 204718)
</span><span class="lines">@@ -39,6 +39,8 @@
</span><span class="cx"> #include &lt;gst/app/gstappsrc.h&gt;
</span><span class="cx"> #include &lt;gst/gst.h&gt;
</span><span class="cx"> #include &lt;gst/pbutils/missing-plugins.h&gt;
</span><ins>+#include &lt;wtf/Condition.h&gt;
+#include &lt;wtf/Lock.h&gt;
</ins><span class="cx"> #include &lt;wtf/MainThread.h&gt;
</span><span class="cx"> #include &lt;wtf/Noncopyable.h&gt;
</span><span class="cx"> #include &lt;wtf/glib/GMutexLocker.h&gt;
</span><span class="lines">@@ -83,7 +85,7 @@
</span><span class="cx"> class ResourceHandleStreamingClient : public ResourceHandleClient, public StreamingClient {
</span><span class="cx">     WTF_MAKE_NONCOPYABLE(ResourceHandleStreamingClient); WTF_MAKE_FAST_ALLOCATED;
</span><span class="cx">     public:
</span><del>-        ResourceHandleStreamingClient(WebKitWebSrc*, const ResourceRequest&amp;);
</del><ins>+        ResourceHandleStreamingClient(WebKitWebSrc*, ResourceRequest&amp;&amp;);
</ins><span class="cx">         virtual ~ResourceHandleStreamingClient();
</span><span class="cx"> 
</span><span class="cx">         // StreamingClient virtual methods.
</span><span class="lines">@@ -102,6 +104,12 @@
</span><span class="cx">         virtual void wasBlocked(ResourceHandle*);
</span><span class="cx">         virtual void cannotShowURL(ResourceHandle*);
</span><span class="cx"> 
</span><ins>+        ThreadIdentifier m_thread { 0 };
+        Lock m_initializeRunLoopConditionMutex;
+        Condition m_initializeRunLoopCondition;
+        RunLoop* m_runLoop { nullptr };
+        Lock m_terminateRunLoopConditionMutex;
+        Condition m_terminateRunLoopCondition;
</ins><span class="cx">         RefPtr&lt;ResourceHandle&gt; m_resource;
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="lines">@@ -127,7 +135,7 @@
</span><span class="cx"> 
</span><span class="cx">     RefPtr&lt;PlatformMediaResourceLoader&gt; loader;
</span><span class="cx">     RefPtr&lt;PlatformMediaResource&gt; resource;
</span><del>-    ResourceHandleStreamingClient* client;
</del><ins>+    std::unique_ptr&lt;ResourceHandleStreamingClient&gt; client;
</ins><span class="cx"> 
</span><span class="cx">     bool didPassAccessControlCheck;
</span><span class="cx"> 
</span><span class="lines">@@ -134,11 +142,12 @@
</span><span class="cx">     guint64 offset;
</span><span class="cx">     guint64 size;
</span><span class="cx">     gboolean seekable;
</span><del>-    gboolean paused;
</del><ins>+    bool paused;
</ins><span class="cx">     bool isSeeking;
</span><span class="cx"> 
</span><span class="cx">     guint64 requestedOffset;
</span><span class="cx"> 
</span><ins>+    bool createdInMainThread;
</ins><span class="cx">     MainThreadNotifier&lt;MainThreadSourceNotification&gt; notifier;
</span><span class="cx">     GRefPtr&lt;GstBuffer&gt; buffer;
</span><span class="cx"> };
</span><span class="lines">@@ -172,57 +181,20 @@
</span><span class="cx"> 
</span><span class="cx"> static void webKitWebSrcNeedData(WebKitWebSrc*);
</span><span class="cx"> static void webKitWebSrcEnoughData(WebKitWebSrc*);
</span><del>-static void webKitWebSrcSeek(WebKitWebSrc*);
</del><ins>+static gboolean webKitWebSrcSeek(WebKitWebSrc*, guint64);
</ins><span class="cx"> 
</span><span class="cx"> static GstAppSrcCallbacks appsrcCallbacks = {
</span><span class="cx">     // need_data
</span><span class="cx">     [](GstAppSrc*, guint, gpointer userData) {
</span><del>-        WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
-        WebKitWebSrcPrivate* priv = src-&gt;priv;
-
-        {
-            WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
-            if (!priv-&gt;paused)
-                return;
-        }
-
-        GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
-        priv-&gt;notifier.notify(MainThreadSourceNotification::NeedData, [protector] { webKitWebSrcNeedData(protector.get()); });
</del><ins>+        webKitWebSrcNeedData(WEBKIT_WEB_SRC(userData));
</ins><span class="cx">     },
</span><span class="cx">     // enough_data
</span><span class="cx">     [](GstAppSrc*, gpointer userData) {
</span><del>-        WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
-        WebKitWebSrcPrivate* priv = src-&gt;priv;
-
-        {
-            WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
-            if (priv-&gt;paused)
-                return;
-        }
-
-        GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
-        priv-&gt;notifier.notify(MainThreadSourceNotification::EnoughData, [protector] { webKitWebSrcEnoughData(protector.get()); });
</del><ins>+        webKitWebSrcEnoughData(WEBKIT_WEB_SRC(userData));
</ins><span class="cx">     },
</span><span class="cx">     // seek_data
</span><span class="cx">     [](GstAppSrc*, guint64 offset, gpointer userData) -&gt; gboolean {
</span><del>-        WebKitWebSrc* src = WEBKIT_WEB_SRC(userData);
-        WebKitWebSrcPrivate* priv = src-&gt;priv;
-
-        {
-            WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
-            if (offset == priv-&gt;offset &amp;&amp; priv-&gt;requestedOffset == priv-&gt;offset)
-                return TRUE;
-
-            if (!priv-&gt;seekable)
-                return FALSE;
-
-            priv-&gt;isSeeking = true;
-            priv-&gt;requestedOffset = offset;
-        }
-
-        GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
-        priv-&gt;notifier.notify(MainThreadSourceNotification::Seek, [protector] { webKitWebSrcSeek(protector.get()); });
-        return TRUE;
</del><ins>+        return webKitWebSrcSeek(WEBKIT_WEB_SRC(userData), offset);
</ins><span class="cx">     },
</span><span class="cx">     { nullptr }
</span><span class="cx"> };
</span><span class="lines">@@ -287,6 +259,8 @@
</span><span class="cx">     src-&gt;priv = priv;
</span><span class="cx">     new (priv) WebKitWebSrcPrivate();
</span><span class="cx"> 
</span><ins>+    priv-&gt;createdInMainThread = isMainThread();
+
</ins><span class="cx">     priv-&gt;appsrc = GST_APP_SRC(gst_element_factory_make(&quot;appsrc&quot;, 0));
</span><span class="cx">     if (!priv-&gt;appsrc) {
</span><span class="cx">         GST_ERROR_OBJECT(src, &quot;Failed to create appsrc&quot;);
</span><span class="lines">@@ -412,28 +386,37 @@
</span><span class="cx"> {
</span><span class="cx">     WebKitWebSrcPrivate* priv = src-&gt;priv;
</span><span class="cx"> 
</span><del>-    ASSERT(isMainThread());
</del><ins>+    if (priv-&gt;resource || (priv-&gt;loader &amp;&amp; !priv-&gt;keepAlive)) {
+        GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
+        priv-&gt;notifier.cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek);
+        bool keepAlive = priv-&gt;keepAlive;
+        priv-&gt;notifier.notify(MainThreadSourceNotification::Stop, [protector, keepAlive] {
+            WebKitWebSrcPrivate* priv = protector-&gt;priv;
</ins><span class="cx"> 
</span><ins>+            WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(protector.get()));
+            if (priv-&gt;resource) {
+                priv-&gt;resource-&gt;stop();
+                priv-&gt;resource-&gt;setClient(nullptr);
+                priv-&gt;resource = nullptr;
+            }
+
+            if (!keepAlive)
+                priv-&gt;loader = nullptr;
+        });
+    }
+
</ins><span class="cx">     WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
</span><span class="cx"> 
</span><span class="cx">     bool wasSeeking = std::exchange(priv-&gt;isSeeking, false);
</span><span class="cx"> 
</span><del>-    priv-&gt;notifier.cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek);
</del><ins>+    priv-&gt;client = nullptr;
</ins><span class="cx"> 
</span><del>-    if (priv-&gt;client) {
-        delete priv-&gt;client;
-        priv-&gt;client = 0;
-    }
-
-    if (!priv-&gt;keepAlive)
-        priv-&gt;loader = nullptr;
-
</del><span class="cx">     if (priv-&gt;buffer) {
</span><span class="cx">         unmapGstBuffer(priv-&gt;buffer.get());
</span><span class="cx">         priv-&gt;buffer.clear();
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    priv-&gt;paused = FALSE;
</del><ins>+    priv-&gt;paused = false;
</ins><span class="cx"> 
</span><span class="cx">     priv-&gt;offset = 0;
</span><span class="cx">     priv-&gt;seekable = FALSE;
</span><span class="lines">@@ -510,8 +493,6 @@
</span><span class="cx"> {
</span><span class="cx">     WebKitWebSrcPrivate* priv = src-&gt;priv;
</span><span class="cx"> 
</span><del>-    ASSERT(isMainThread());
-
</del><span class="cx">     WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
</span><span class="cx"> 
</span><span class="cx">     priv-&gt;didPassAccessControlCheck = false;
</span><span class="lines">@@ -576,37 +557,41 @@
</span><span class="cx">     // We always request Icecast/Shoutcast metadata, just in case ...
</span><span class="cx">     request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, &quot;1&quot;);
</span><span class="cx"> 
</span><del>-    bool loadFailed = true;
-    if (priv-&gt;player &amp;&amp; !priv-&gt;loader)
-        priv-&gt;loader = priv-&gt;player-&gt;createResourceLoader();
</del><ins>+    if (!priv-&gt;player || !priv-&gt;createdInMainThread) {
+        priv-&gt;client = std::make_unique&lt;ResourceHandleStreamingClient&gt;(src, WTFMove(request));
+        if (priv-&gt;client-&gt;loadFailed()) {
+            GST_ERROR_OBJECT(src, &quot;Failed to setup streaming client&quot;);
+            priv-&gt;client = nullptr;
+            locker.unlock();
+            webKitWebSrcStop(src);
+        } else
+            GST_DEBUG_OBJECT(src, &quot;Started request&quot;);
+        return;
+    }
</ins><span class="cx"> 
</span><del>-    if (priv-&gt;loader) {
</del><ins>+    locker.unlock();
+    GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
+    priv-&gt;notifier.notify(MainThreadSourceNotification::Start, [protector, request] {
+        WebKitWebSrcPrivate* priv = protector-&gt;priv;
+
+        WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(protector.get()));
+        if (!priv-&gt;loader)
+            priv-&gt;loader = priv-&gt;player-&gt;createResourceLoader();
+
</ins><span class="cx">         PlatformMediaResourceLoader::LoadOptions loadOptions = 0;
</span><span class="cx">         if (request.url().protocolIsBlob())
</span><span class="cx">             loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData;
</span><span class="cx">         priv-&gt;resource = priv-&gt;loader-&gt;requestResource(request, loadOptions);
</span><del>-        loadFailed = !priv-&gt;resource;
-
-        if (priv-&gt;resource)
-            priv-&gt;resource-&gt;setClient(std::make_unique&lt;CachedResourceStreamingClient&gt;(src));
-    } else {
-        priv-&gt;client = new ResourceHandleStreamingClient(src, request);
-        loadFailed = priv-&gt;client-&gt;loadFailed();
-    }
-
-    if (loadFailed) {
-        GST_ERROR_OBJECT(src, &quot;Failed to setup streaming client&quot;);
-        if (priv-&gt;client) {
-            delete priv-&gt;client;
-            priv-&gt;client = nullptr;
</del><ins>+        if (priv-&gt;resource) {
+            priv-&gt;resource-&gt;setClient(std::make_unique&lt;CachedResourceStreamingClient&gt;(protector.get()));
+            GST_DEBUG_OBJECT(protector.get(), &quot;Started request&quot;);
+        } else {
+            GST_ERROR_OBJECT(protector.get(), &quot;Failed to setup streaming client&quot;);
+            priv-&gt;loader = nullptr;
+            locker.unlock();
+            webKitWebSrcStop(protector.get());
</ins><span class="cx">         }
</span><del>-        priv-&gt;loader = nullptr;
-        priv-&gt;resource = nullptr;
-        locker.unlock();
-        webKitWebSrcStop(src);
-        return;
-    }
-    GST_DEBUG_OBJECT(src, &quot;Started request&quot;);
</del><ins>+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition)
</span><span class="lines">@@ -638,16 +623,13 @@
</span><span class="cx">     case GST_STATE_CHANGE_READY_TO_PAUSED:
</span><span class="cx">     {
</span><span class="cx">         GST_DEBUG_OBJECT(src, &quot;READY-&gt;PAUSED&quot;);
</span><del>-        GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
-        priv-&gt;notifier.notify(MainThreadSourceNotification::Start, [protector] { webKitWebSrcStart(protector.get()); });
</del><ins>+        webKitWebSrcStart(src);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     case GST_STATE_CHANGE_PAUSED_TO_READY:
</span><span class="cx">     {
</span><span class="cx">         GST_DEBUG_OBJECT(src, &quot;PAUSED-&gt;READY&quot;);
</span><del>-        priv-&gt;notifier.cancelPendingNotifications();
-        GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
-        priv-&gt;notifier.notify(MainThreadSourceNotification::Stop, [protector] { webKitWebSrcStop(protector.get()); });
</del><ins>+        webKitWebSrcStop(src);
</ins><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx">     default:
</span><span class="lines">@@ -772,25 +754,29 @@
</span><span class="cx">     iface-&gt;set_uri = webKitWebSrcSetUri;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-// appsrc callbacks
-
</del><span class="cx"> static void webKitWebSrcNeedData(WebKitWebSrc* src)
</span><span class="cx"> {
</span><span class="cx">     WebKitWebSrcPrivate* priv = src-&gt;priv;
</span><span class="cx"> 
</span><del>-    ASSERT(isMainThread());
-
</del><span class="cx">     GST_DEBUG_OBJECT(src, &quot;Need more data&quot;);
</span><span class="cx"> 
</span><span class="cx">     {
</span><span class="cx">         WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
</span><del>-        priv-&gt;paused = FALSE;
</del><ins>+        if (!priv-&gt;paused)
+            return;
+        priv-&gt;paused = false;
+        if (priv-&gt;client) {
+            priv-&gt;client-&gt;setDefersLoading(false);
+            return;
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (priv-&gt;client)
-        priv-&gt;client-&gt;setDefersLoading(false);
-    if (priv-&gt;resource)
-        priv-&gt;resource-&gt;setDefersLoading(false);
</del><ins>+    GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
+    priv-&gt;notifier.notify(MainThreadSourceNotification::NeedData, [protector] {
+        WebKitWebSrcPrivate* priv = protector-&gt;priv;
+        if (priv-&gt;resource)
+            priv-&gt;resource-&gt;setDefersLoading(false);
+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static void webKitWebSrcEnoughData(WebKitWebSrc* src)
</span><span class="lines">@@ -797,34 +783,62 @@
</span><span class="cx"> {
</span><span class="cx">     WebKitWebSrcPrivate* priv = src-&gt;priv;
</span><span class="cx"> 
</span><del>-    ASSERT(isMainThread());
-
</del><span class="cx">     GST_DEBUG_OBJECT(src, &quot;Have enough data&quot;);
</span><span class="cx"> 
</span><span class="cx">     {
</span><span class="cx">         WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
</span><del>-        priv-&gt;paused = TRUE;
</del><ins>+        if (priv-&gt;paused)
+            return;
+        priv-&gt;paused = true;
+        if (priv-&gt;client) {
+            priv-&gt;client-&gt;setDefersLoading(true);
+            return;
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (priv-&gt;client)
-        priv-&gt;client-&gt;setDefersLoading(true);
-    if (priv-&gt;resource)
-        priv-&gt;resource-&gt;setDefersLoading(true);
</del><ins>+    GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
+    priv-&gt;notifier.notify(MainThreadSourceNotification::EnoughData, [protector] {
+        WebKitWebSrcPrivate* priv = protector-&gt;priv;
+        if (priv-&gt;resource)
+            priv-&gt;resource-&gt;setDefersLoading(true);
+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static void webKitWebSrcSeek(WebKitWebSrc* src)
</del><ins>+static gboolean webKitWebSrcSeek(WebKitWebSrc* src, guint64 offset)
</ins><span class="cx"> {
</span><del>-    ASSERT(isMainThread());
</del><ins>+    WebKitWebSrcPrivate* priv = src-&gt;priv;
</ins><span class="cx"> 
</span><ins>+    {
+        WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
+        if (offset == priv-&gt;offset &amp;&amp; priv-&gt;requestedOffset == priv-&gt;offset)
+            return TRUE;
+
+        if (!priv-&gt;seekable)
+            return FALSE;
+
+        priv-&gt;isSeeking = true;
+        priv-&gt;requestedOffset = offset;
+    }
+
</ins><span class="cx">     GST_DEBUG_OBJECT(src, &quot;Seeking to offset: %&quot; G_GUINT64_FORMAT, src-&gt;priv-&gt;requestedOffset);
</span><ins>+    if (priv-&gt;client) {
+        webKitWebSrcStop(src);
+        webKitWebSrcStart(src);
+        return TRUE;
+    }
</ins><span class="cx"> 
</span><del>-    webKitWebSrcStop(src);
-    webKitWebSrcStart(src);
</del><ins>+    GRefPtr&lt;WebKitWebSrc&gt; protector = WTF::ensureGRef(src);
+    priv-&gt;notifier.notify(MainThreadSourceNotification::Seek, [protector] {
+        webKitWebSrcStop(protector.get());
+        webKitWebSrcStart(protector.get());
+    });
+    return TRUE;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(player);
</span><ins>+    ASSERT(src-&gt;priv-&gt;createdInMainThread);
</ins><span class="cx">     WTF::GMutexLocker&lt;GMutex&gt; locker(*GST_OBJECT_GET_LOCK(src));
</span><span class="cx">     src-&gt;priv-&gt;player = player;
</span><span class="cx"> }
</span><span class="lines">@@ -1051,18 +1065,48 @@
</span><span class="cx">     handleNotifyFinished();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, const ResourceRequest&amp; request)
</del><ins>+ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, ResourceRequest&amp;&amp; request)
</ins><span class="cx">     : StreamingClient(src)
</span><span class="cx"> {
</span><del>-    m_resource = ResourceHandle::create(0 /*context*/, request, this, false, false);
</del><ins>+    LockHolder locker(m_initializeRunLoopConditionMutex);
+    m_thread = createThread(&quot;ResourceHandleStreamingClient&quot;, [this, request] {
+        {
+            LockHolder locker(m_initializeRunLoopConditionMutex);
+            m_runLoop = &amp;RunLoop::current();
+            m_resource = ResourceHandle::create(nullptr /*context*/, request, this, true, false);
+            m_initializeRunLoopCondition.notifyOne();
+        }
+        if (!m_resource)
+            return;
+
+        m_runLoop-&gt;dispatch([this] { m_resource-&gt;setDefersLoading(false); });
+        m_runLoop-&gt;run();
+        {
+            LockHolder locker(m_terminateRunLoopConditionMutex);
+            m_runLoop = nullptr;
+            m_resource-&gt;clearClient();
+            m_resource-&gt;cancel();
+            m_resource = nullptr;
+            m_terminateRunLoopCondition.notifyOne();
+        }
+    });
+    m_initializeRunLoopCondition.wait(m_initializeRunLoopConditionMutex);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> ResourceHandleStreamingClient::~ResourceHandleStreamingClient()
</span><span class="cx"> {
</span><del>-    if (m_resource) {
-        m_resource-&gt;cancel();
-        m_resource = nullptr;
</del><ins>+    if (m_thread) {
+        detachThread(m_thread);
+        m_thread = 0;
</ins><span class="cx">     }
</span><ins>+
+    if (m_runLoop == &amp;RunLoop::current())
+        m_runLoop-&gt;stop();
+    else {
+        LockHolder locker(m_terminateRunLoopConditionMutex);
+        m_runLoop-&gt;stop();
+        m_terminateRunLoopCondition.wait(m_terminateRunLoopConditionMutex);
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool ResourceHandleStreamingClient::loadFailed() const
</span><span class="lines">@@ -1072,8 +1116,10 @@
</span><span class="cx"> 
</span><span class="cx"> void ResourceHandleStreamingClient::setDefersLoading(bool defers)
</span><span class="cx"> {
</span><del>-    if (m_resource)
-        m_resource-&gt;setDefersLoading(defers);
</del><ins>+    m_runLoop-&gt;dispatch([this, defers] {
+        if (m_resource)
+            m_resource-&gt;setDefersLoading(defers);
+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> char* ResourceHandleStreamingClient::getOrCreateReadBuffer(size_t requestedSize, size_t&amp; actualSize)
</span></span></pre>
</div>
</div>

</body>
</html>