Bug 1488808 Part 10 - Notify the record/replay system when a message pump thread blocks after diverging from the recording, r=froydnj.

--HG--
extra : rebase_source : c82088f0903b618caee3b0366a8c681ff6f445a1
This commit is contained in:
Brian Hackett 2018-10-17 10:05:02 -06:00
parent fc6a34c667
commit 3c4231fd5b
6 changed files with 42 additions and 10 deletions

View File

@ -60,6 +60,19 @@ void MessagePumpDefault::Run(Delegate* delegate) {
AUTO_PROFILER_LABEL("MessagePumpDefault::Run:Wait", IDLE);
{
AUTO_PROFILER_THREAD_SLEEP;
// Some threads operating a message pump (i.e. the compositor thread)
// can diverge from Web Replay recordings, after which the thread needs
// to be explicitly notified in order to coordinate with the
// record/replay checkpoint system.
if (mozilla::recordreplay::IsRecordingOrReplaying()) {
// Workaround static analysis. Message pumps for which the lambda
// below might be called will not be destroyed.
void* thiz = this;
mozilla::recordreplay::MaybeWaitForCheckpointSave();
mozilla::recordreplay::NotifyUnrecordedWait([=]() { ((MessagePumpDefault*)thiz)->ScheduleWork(); },
/* aOnlyWhenDiverged = */ true);
}
event_.Wait();
}
} else {

View File

@ -162,7 +162,8 @@ MaybeWaitForRecordReplayCheckpointSave(AutoLockHelperThreadState& locked)
// Now that we are holding the helper thread state lock again,
// notify the record/replay system that we might block soon.
mozilla::recordreplay::NotifyUnrecordedWait(HelperThread::WakeupAll);
mozilla::recordreplay::NotifyUnrecordedWait(HelperThread::WakeupAll,
/* aOnlyWhenDiverged = */ false);
}
}

View File

@ -51,7 +51,8 @@ namespace recordreplay {
Macro(InternalRecordReplayBytes, \
(void* aData, size_t aSize), (aData, aSize)) \
Macro(NotifyUnrecordedWait, \
(const std::function<void()>& aCallback), (aCallback)) \
(const std::function<void()>& aCallback, bool aOnlyWhenDiverged), \
(aCallback, aOnlyWhenDiverged)) \
Macro(MaybeWaitForCheckpointSave, (), ()) \
Macro(InternalInvalidateRecording, (const char* aWhy), (aWhy)) \
Macro(InternalDestroyPLDHashTableCallbacks, \

View File

@ -258,12 +258,16 @@ static inline bool HasDivergedFromRecording();
// The callback passed to NotifyUnrecordedWait will be invoked at most once
// by the main thread whenever the main thread is waiting for other threads to
// become idle, and at most once after the call to NotifyUnrecordedWait if the
// main thread is already waiting for other threads to become idle.
// main thread is already waiting for other threads to become idle. If
// aOnlyWhenDiverged is specified, the callback will only be invoked if the
// thread has diverged from the recording (causing all resources to be treated
// as unrecorded).
//
// The callback should poke the thread so that it is no longer blocked on the
// resource. The thread must call MaybeWaitForCheckpointSave before blocking
// again.
MFBT_API void NotifyUnrecordedWait(const std::function<void()>& aCallback);
MFBT_API void NotifyUnrecordedWait(const std::function<void()>& aCallback,
bool aOnlyWhenDiverged);
MFBT_API void MaybeWaitForCheckpointSave();
// API for debugging inconsistent behavior between recording and replay.

View File

@ -389,7 +389,15 @@ Thread::WaitForIdleThreads()
Thread* thread = GetById(i);
if (!thread->mIdle) {
done = false;
if (thread->mUnrecordedWaitCallback && !thread->mUnrecordedWaitNotified) {
// Check if there is a callback we can invoke to get this thread to
// make progress. The mUnrecordedWaitOnlyWhenDiverged flag is used to
// avoid perturbing the behavior of threads that may or may not be
// waiting on an unrecorded resource, depending on whether they have
// diverged from the recording yet.
if (thread->mUnrecordedWaitCallback && !thread->mUnrecordedWaitNotified &&
(!thread->mUnrecordedWaitOnlyWhenDiverged ||
thread->WillDivergeFromRecordingSoon())) {
// Set this flag before releasing the idle lock. Otherwise it's
// possible the thread could call NotifyUnrecordedWait while we
// aren't holding the lock, and we would set the flag afterwards
@ -397,10 +405,11 @@ Thread::WaitForIdleThreads()
thread->mUnrecordedWaitNotified = true;
// Release the idle lock here to avoid any risk of deadlock.
std::function<void()> callback = thread->mUnrecordedWaitCallback;
{
MonitorAutoUnlock unlock(*gMonitor);
AutoPassThroughThreadEvents pt;
thread->mUnrecordedWaitCallback();
callback();
}
// Releasing the global lock means that we need to start over
@ -438,7 +447,7 @@ Thread::ResumeIdleThreads()
}
void
Thread::NotifyUnrecordedWait(const std::function<void()>& aCallback)
Thread::NotifyUnrecordedWait(const std::function<void()>& aCallback, bool aOnlyWhenDiverged)
{
MonitorAutoLock lock(*gMonitor);
if (mUnrecordedWaitCallback) {
@ -451,6 +460,7 @@ Thread::NotifyUnrecordedWait(const std::function<void()>& aCallback)
}
mUnrecordedWaitCallback = aCallback;
mUnrecordedWaitOnlyWhenDiverged = aOnlyWhenDiverged;
// The main thread might be able to make progress now by calling the routine
// if it is waiting for idle replay threads.
@ -472,9 +482,10 @@ Thread::MaybeWaitForCheckpointSave()
extern "C" {
MOZ_EXPORT void
RecordReplayInterface_NotifyUnrecordedWait(const std::function<void()>& aCallback)
RecordReplayInterface_NotifyUnrecordedWait(const std::function<void()>& aCallback,
bool aOnlyWhenDiverged)
{
Thread::Current()->NotifyUnrecordedWait(aCallback);
Thread::Current()->NotifyUnrecordedWait(aCallback, aOnlyWhenDiverged);
}
MOZ_EXPORT void

View File

@ -134,6 +134,7 @@ private:
// and whether the callback has been invoked yet while the main thread is
// waiting for threads to become idle. Protected by the thread monitor.
std::function<void()> mUnrecordedWaitCallback;
bool mUnrecordedWaitOnlyWhenDiverged;
bool mUnrecordedWaitNotified;
public:
@ -277,7 +278,8 @@ public:
static void WaitForeverNoIdle();
// See RecordReplay.h.
void NotifyUnrecordedWait(const std::function<void()>& aCallback);
void NotifyUnrecordedWait(const std::function<void()>& aCallback,
bool aOnlyWhenDiverged);
static void MaybeWaitForCheckpointSave();
// Wait for all other threads to enter the idle state necessary for saving