- Revision
- 250530
- Author
- [email protected]
- Date
- 2019-09-30 14:09:04 -0700 (Mon, 30 Sep 2019)
Log Message
Cherry-pick r250428. rdar://problem/55825357
[iOS] Lock screen controls can fail to play web content
https://bugs.webkit.org/show_bug.cgi?id=202279
Reviewed by Chris Dumez.
When playback is paused from the lock screen via Now Playing controls, the WebProcess,
UIProcess, and Network Process will all be suspended. MediaRemote will take an assertion
and wake up the WebProcess when a remote control command to "play" is sent via the Now
Playing controls. However, if a synchronous message to the (suspended) Network or UIProcess
is issued before the notification that the process was unexpectedly unsuspended can be
issued (which will subsequently unsuspend the UIProcess and Network process), we can get
into a deadlocked state where the main thread is blocked on the sync message to a suspended
process.
To work around this problem, move all the processing from ProcessTaskStateObserver to a
WorkQueue / background thread. This requires making the ProcessTaskStateObserver thread-safe,
though its only current client is a Singleton (the WebProcess class), and so the risk of
thread safety issues is currently minimal. Regardless, access to the Client pointer must be
guarded by a Lock, and the Client itself must become ref-counted, so that the
ProcessTaskStateObserver can ref its Client (the WebProcess) during callback processing.
Unfortunately, sendWithAsyncReply() is not thread safe, nor is ProcessAssertion, so instead
just use send() and set a 5-second timeout before expiring the assertion, and just use
BKSProcessStateAssertion directly.
* Shared/Cocoa/ProcessTaskStateObserver.h:
(WebKit::ProcessTaskStateObserver::Client::ref):
(WebKit::ProcessTaskStateObserver::Client::deref):
(WebKit::ProcessTaskStateObserver::setClient): Deleted.
(WebKit::ProcessTaskStateObserver::client): Deleted.
* Shared/Cocoa/ProcessTaskStateObserver.mm:
(-[WKProcessTaskStateObserverDelegate process:taskStateDidChange:]):
(WebKit::ProcessTaskStateObserver::create):
(WebKit::ProcessTaskStateObserver::ProcessTaskStateObserver):
(WebKit::ProcessTaskStateObserver::~ProcessTaskStateObserver):
(WebKit::ProcessTaskStateObserver::invalidate):
(WebKit::ProcessTaskStateObserver::client):
(WebKit::ProcessTaskStateObserver::setTaskState):
* WebProcess/WebProcess.cpp:
(WebKit::m_taskStateObserver):
* WebProcess/WebProcess.h:
* UIProcess/Cocoa/WebProcessProxyCocoa.mm:
(WebKit::WebProcessProxy::processWasUnexpectedlyUnsuspended):
* UIProcess/WebProcessProxy.h:
* UIProcess/WebProcessProxy.messages.in:
* WebProcess/cocoa/WebProcessCocoa.mm:
(WebKit::WebProcess::processTaskStateDidChange):
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@250428 268f45cc-cd09-0410-ab3c-d52691b4dbfc
Modified Paths
Diff
Modified: branches/safari-608-branch/Source/WebKit/ChangeLog (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/ChangeLog 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/ChangeLog 2019-09-30 21:09:04 UTC (rev 250530)
@@ -1,5 +1,109 @@
2019-09-30 Kocsen Chung <[email protected]>
+ Cherry-pick r250428. rdar://problem/55825357
+
+ [iOS] Lock screen controls can fail to play web content
+ https://bugs.webkit.org/show_bug.cgi?id=202279
+
+ Reviewed by Chris Dumez.
+
+ When playback is paused from the lock screen via Now Playing controls, the WebProcess,
+ UIProcess, and Network Process will all be suspended. MediaRemote will take an assertion
+ and wake up the WebProcess when a remote control command to "play" is sent via the Now
+ Playing controls. However, if a synchronous message to the (suspended) Network or UIProcess
+ is issued before the notification that the process was unexpectedly unsuspended can be
+ issued (which will subsequently unsuspend the UIProcess and Network process), we can get
+ into a deadlocked state where the main thread is blocked on the sync message to a suspended
+ process.
+
+ To work around this problem, move all the processing from ProcessTaskStateObserver to a
+ WorkQueue / background thread. This requires making the ProcessTaskStateObserver thread-safe,
+ though its only current client is a Singleton (the WebProcess class), and so the risk of
+ thread safety issues is currently minimal. Regardless, access to the Client pointer must be
+ guarded by a Lock, and the Client itself must become ref-counted, so that the
+ ProcessTaskStateObserver can ref its Client (the WebProcess) during callback processing.
+
+ Unfortunately, sendWithAsyncReply() is not thread safe, nor is ProcessAssertion, so instead
+ just use send() and set a 5-second timeout before expiring the assertion, and just use
+ BKSProcessStateAssertion directly.
+
+ * Shared/Cocoa/ProcessTaskStateObserver.h:
+ (WebKit::ProcessTaskStateObserver::Client::ref):
+ (WebKit::ProcessTaskStateObserver::Client::deref):
+ (WebKit::ProcessTaskStateObserver::setClient): Deleted.
+ (WebKit::ProcessTaskStateObserver::client): Deleted.
+ * Shared/Cocoa/ProcessTaskStateObserver.mm:
+ (-[WKProcessTaskStateObserverDelegate process:taskStateDidChange:]):
+ (WebKit::ProcessTaskStateObserver::create):
+ (WebKit::ProcessTaskStateObserver::ProcessTaskStateObserver):
+ (WebKit::ProcessTaskStateObserver::~ProcessTaskStateObserver):
+ (WebKit::ProcessTaskStateObserver::invalidate):
+ (WebKit::ProcessTaskStateObserver::client):
+ (WebKit::ProcessTaskStateObserver::setTaskState):
+ * WebProcess/WebProcess.cpp:
+ (WebKit::m_taskStateObserver):
+ * WebProcess/WebProcess.h:
+ * UIProcess/Cocoa/WebProcessProxyCocoa.mm:
+ (WebKit::WebProcessProxy::processWasUnexpectedlyUnsuspended):
+ * UIProcess/WebProcessProxy.h:
+ * UIProcess/WebProcessProxy.messages.in:
+ * WebProcess/cocoa/WebProcessCocoa.mm:
+ (WebKit::WebProcess::processTaskStateDidChange):
+
+ git-svn-id: https://svn.webkit.org/repository/webkit/trunk@250428 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+ 2019-09-27 Jer Noble <[email protected]>
+
+ [iOS] Lock screen controls can fail to play web content
+ https://bugs.webkit.org/show_bug.cgi?id=202279
+
+ Reviewed by Chris Dumez.
+
+ When playback is paused from the lock screen via Now Playing controls, the WebProcess,
+ UIProcess, and Network Process will all be suspended. MediaRemote will take an assertion
+ and wake up the WebProcess when a remote control command to "play" is sent via the Now
+ Playing controls. However, if a synchronous message to the (suspended) Network or UIProcess
+ is issued before the notification that the process was unexpectedly unsuspended can be
+ issued (which will subsequently unsuspend the UIProcess and Network process), we can get
+ into a deadlocked state where the main thread is blocked on the sync message to a suspended
+ process.
+
+ To work around this problem, move all the processing from ProcessTaskStateObserver to a
+ WorkQueue / background thread. This requires making the ProcessTaskStateObserver thread-safe,
+ though its only current client is a Singleton (the WebProcess class), and so the risk of
+ thread safety issues is currently minimal. Regardless, access to the Client pointer must be
+ guarded by a Lock, and the Client itself must become ref-counted, so that the
+ ProcessTaskStateObserver can ref its Client (the WebProcess) during callback processing.
+
+ Unfortunately, sendWithAsyncReply() is not thread safe, nor is ProcessAssertion, so instead
+ just use send() and set a 5-second timeout before expiring the assertion, and just use
+ BKSProcessStateAssertion directly.
+
+ * Shared/Cocoa/ProcessTaskStateObserver.h:
+ (WebKit::ProcessTaskStateObserver::Client::ref):
+ (WebKit::ProcessTaskStateObserver::Client::deref):
+ (WebKit::ProcessTaskStateObserver::setClient): Deleted.
+ (WebKit::ProcessTaskStateObserver::client): Deleted.
+ * Shared/Cocoa/ProcessTaskStateObserver.mm:
+ (-[WKProcessTaskStateObserverDelegate process:taskStateDidChange:]):
+ (WebKit::ProcessTaskStateObserver::create):
+ (WebKit::ProcessTaskStateObserver::ProcessTaskStateObserver):
+ (WebKit::ProcessTaskStateObserver::~ProcessTaskStateObserver):
+ (WebKit::ProcessTaskStateObserver::invalidate):
+ (WebKit::ProcessTaskStateObserver::client):
+ (WebKit::ProcessTaskStateObserver::setTaskState):
+ * WebProcess/WebProcess.cpp:
+ (WebKit::m_taskStateObserver):
+ * WebProcess/WebProcess.h:
+ * UIProcess/Cocoa/WebProcessProxyCocoa.mm:
+ (WebKit::WebProcessProxy::processWasUnexpectedlyUnsuspended):
+ * UIProcess/WebProcessProxy.h:
+ * UIProcess/WebProcessProxy.messages.in:
+ * WebProcess/cocoa/WebProcessCocoa.mm:
+ (WebKit::WebProcess::processTaskStateDidChange):
+
+2019-09-30 Kocsen Chung <[email protected]>
+
Cherry-pick r250405. rdar://problem/55825353
Add some logging to help diagnose blank or stuck WKWebViews
Modified: branches/safari-608-branch/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.h (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.h 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.h 2019-09-30 21:09:04 UTC (rev 250530)
@@ -27,7 +27,9 @@
#if PLATFORM(IOS_FAMILY)
+#include <wtf/Lock.h>
#include <wtf/RetainPtr.h>
+#include <wtf/ThreadSafeRefCounted.h>
OBJC_CLASS WKProcessTaskStateObserverDelegate;
OBJC_CLASS BKSProcess;
@@ -34,12 +36,11 @@
namespace WebKit {
-class ProcessTaskStateObserver {
+class ProcessTaskStateObserver : public ThreadSafeRefCounted<ProcessTaskStateObserver> {
public:
class Client;
- ProcessTaskStateObserver();
- explicit ProcessTaskStateObserver(Client&);
+ static Ref<ProcessTaskStateObserver> create(Client&);
~ProcessTaskStateObserver();
enum TaskState {
@@ -54,15 +55,15 @@
virtual void processTaskStateDidChange(TaskState) = 0;
};
- void setClient(Client& client) { m_client = &client; }
- Client* client() { return m_client; }
-
+ void invalidate();
TaskState taskState() const { return m_taskState; }
private:
+ explicit ProcessTaskStateObserver(Client&);
void setTaskState(TaskState);
- Client* m_client { nullptr };
+ Client* m_client;
+ Lock m_clientLock;
TaskState m_taskState { None };
RetainPtr<BKSProcess> m_process;
RetainPtr<WKProcessTaskStateObserverDelegate> m_delegate;
Modified: branches/safari-608-branch/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.mm (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.mm 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/Shared/Cocoa/ProcessTaskStateObserver.mm 2019-09-30 21:09:04 UTC (rev 250530)
@@ -47,10 +47,8 @@
{
RELEASE_LOG(ProcessSuspension, "%p -[WKProcessTaskStateObserverDelegate process:taskStateDidChange:], process(%p), newState(%d)", self, process, (int)newState);
- dispatch_async(dispatch_get_main_queue(), ^{
- if (self.taskStateChangedCallback)
- self.taskStateChangedCallback(newState);
- });
+ if (auto callback = _taskStateChangedCallback)
+ callback(newState);
}
@end
@@ -64,25 +62,32 @@
return static_cast<ProcessTaskStateObserver::TaskState>(state);
}
-ProcessTaskStateObserver::ProcessTaskStateObserver()
- : m_process([getBKSProcessClass() currentProcess])
+Ref<ProcessTaskStateObserver> ProcessTaskStateObserver::create(Client& client)
+{
+ return adoptRef(*new ProcessTaskStateObserver(client));
+}
+
+ProcessTaskStateObserver::ProcessTaskStateObserver(Client& client)
+ : m_client(&client)
+ , m_process([getBKSProcessClass() currentProcess])
, m_delegate(adoptNS([[WKProcessTaskStateObserverDelegate alloc] init]))
{
RELEASE_LOG(ProcessSuspension, "%p - ProcessTaskStateObserver::ProcessTaskStateObserver(), m_process(%p)", this, m_process.get());
- m_delegate.get().taskStateChangedCallback = [this] (BKSProcessTaskState state) {
- setTaskState(toProcessTaskStateObserverTaskState(state));
+ m_delegate.get().taskStateChangedCallback = [protectedThis = makeRefPtr(this)] (BKSProcessTaskState state) {
+ protectedThis->setTaskState(toProcessTaskStateObserverTaskState(state));
};
m_process.get().delegate = m_delegate.get();
}
-ProcessTaskStateObserver::ProcessTaskStateObserver(Client& client)
- : ProcessTaskStateObserver()
+ProcessTaskStateObserver::~ProcessTaskStateObserver()
{
- setClient(client);
+ m_delegate.get().taskStateChangedCallback = nil;
}
-ProcessTaskStateObserver::~ProcessTaskStateObserver()
+void ProcessTaskStateObserver::invalidate()
{
+ LockHolder holder(m_clientLock);
+ m_client = nullptr;
m_delegate.get().taskStateChangedCallback = nil;
}
@@ -92,6 +97,8 @@
return;
m_taskState = state;
+
+ LockHolder holder(m_clientLock);
if (m_client)
m_client->processTaskStateDidChange(state);
}
Modified: branches/safari-608-branch/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/UIProcess/Cocoa/WebProcessProxyCocoa.mm 2019-09-30 21:09:04 UTC (rev 250530)
@@ -190,12 +190,11 @@
#endif
#if PLATFORM(IOS_FAMILY)
-void WebProcessProxy::processWasUnexpectedlyUnsuspended(CompletionHandler<void()>&& completion)
+void WebProcessProxy::processWasUnexpectedlyUnsuspended()
{
if (m_throttler.shouldBeRunnable()) {
// The process becoming unsuspended was not unexpected; it likely was notified of its running state
// before receiving a procsessDidResume() message from the UIProcess.
- completion();
return;
}
@@ -206,7 +205,6 @@
RELEASE_LOG(ProcessSuspension, "%p - WebProcessProxy::processWasUnexpectedlyUnsuspended() - lambda, background activity timed out", weakThis.get());
};
m_unexpectedActivityTimer = std::make_unique<WebCore::DeferrableOneShotTimer>(WTFMove(backgroundActivityTimeoutHandler), unexpectedActivityDuration);
- completion();
}
#endif
Modified: branches/safari-608-branch/Source/WebKit/UIProcess/WebProcessProxy.h (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/UIProcess/WebProcessProxy.h 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/UIProcess/WebProcessProxy.h 2019-09-30 21:09:04 UTC (rev 250530)
@@ -303,7 +303,7 @@
#endif
#if PLATFORM(IOS_FAMILY)
- void processWasUnexpectedlyUnsuspended(CompletionHandler<void()>&&);
+ void processWasUnexpectedlyUnsuspended();
#endif
void webPageMediaStateDidChange(WebPageProxy&);
Modified: branches/safari-608-branch/Source/WebKit/UIProcess/WebProcessProxy.messages.in (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/UIProcess/WebProcessProxy.messages.in 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/UIProcess/WebProcessProxy.messages.in 2019-09-30 21:09:04 UTC (rev 250530)
@@ -81,6 +81,6 @@
#endif
#if PLATFORM(IOS_FAMILY)
- ProcessWasUnexpectedlyUnsuspended() -> () Async
+ ProcessWasUnexpectedlyUnsuspended()
#endif
}
Modified: branches/safari-608-branch/Source/WebKit/WebProcess/WebProcess.cpp (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/WebProcess/WebProcess.cpp 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/WebProcess/WebProcess.cpp 2019-09-30 21:09:04 UTC (rev 250530)
@@ -192,6 +192,7 @@
, m_nonVisibleProcessCleanupTimer(*this, &WebProcess::nonVisibleProcessCleanupTimerFired)
#if PLATFORM(IOS_FAMILY)
, m_webSQLiteDatabaseTracker([this](bool isHoldingLockedFiles) { parentProcessConnection()->send(Messages::WebProcessProxy::SetIsHoldingLockedFiles(isHoldingLockedFiles), 0); })
+ , m_taskStateObserver(ProcessTaskStateObserver::create(*this))
#endif
{
// Initialize our platform strategies.
@@ -231,6 +232,9 @@
WebProcess::~WebProcess()
{
+#if PLATFORM(IOS_FAMILY)
+ m_taskStateObserver->invalidate();
+#endif
}
void WebProcess::initializeProcess(const AuxiliaryProcessInitializationParameters& parameters)
Modified: branches/safari-608-branch/Source/WebKit/WebProcess/WebProcess.h (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/WebProcess/WebProcess.h 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/WebProcess/WebProcess.h 2019-09-30 21:09:04 UTC (rev 250530)
@@ -529,7 +529,7 @@
#if PLATFORM(IOS_FAMILY)
WebSQLiteDatabaseTracker m_webSQLiteDatabaseTracker;
- ProcessTaskStateObserver m_taskStateObserver { *this };
+ Ref<ProcessTaskStateObserver> m_taskStateObserver;
#endif
enum PageMarkingLayersAsVolatileCounterType { };
Modified: branches/safari-608-branch/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm (250529 => 250530)
--- branches/safari-608-branch/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm 2019-09-30 21:08:59 UTC (rev 250529)
+++ branches/safari-608-branch/Source/WebKit/WebProcess/cocoa/WebProcessCocoa.mm 2019-09-30 21:09:04 UTC (rev 250530)
@@ -27,6 +27,7 @@
#import "WebProcess.h"
#import "WebProcessCocoa.h"
+#import "AssertionServicesSPI.h"
#import "LegacyCustomProtocolManager.h"
#import "LogInitialization.h"
#import "Logging.h"
@@ -294,6 +295,7 @@
#if PLATFORM(IOS_FAMILY)
void WebProcess::processTaskStateDidChange(ProcessTaskStateObserver::TaskState taskState)
{
+ // NOTE: This will be called from a background thread.
RELEASE_LOG(ProcessSuspension, "%p - WebProcess::processTaskStateDidChange() - taskState(%d)", this, taskState);
if (taskState == ProcessTaskStateObserver::None)
return;
@@ -311,8 +313,10 @@
// We were awakened from suspension unexpectedly. Notify the WebProcessProxy, but take a process assertion on our parent PID
// to ensure that it too is awakened.
- auto uiProcessAssertion = std::make_unique<ProcessAssertion>(parentProcessConnection()->remoteProcessID(), "Unexpectedly resumed", AssertionState::Background, AssertionReason::FinishTask);
- parentProcessConnection()->sendWithAsyncReply(Messages::WebProcessProxy::ProcessWasUnexpectedlyUnsuspended(), [uiProcessAssertion = WTFMove(uiProcessAssertion)] { });
+
+ auto uiProcessAssertion = adoptNS([[BKSProcessAssertion alloc] initWithPID:parentProcessConnection()->remoteProcessID() flags:BKSProcessAssertionPreventTaskSuspend reason:BKSProcessAssertionReasonFinishTask name:@"Unexpectedly resumed" withHandler:nil]);
+ parentProcessConnection()->send(Messages::WebProcessProxy::ProcessWasUnexpectedlyUnsuspended(), 0);
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), [assertion = WTFMove(uiProcessAssertion)] { [assertion invalidate]; });
}
#endif