Bug 848954 - Part 2 - Put the thread management in the driver. r=roc
authorPaul Adenot <paul@paul.cx>
Fri, 25 Apr 2014 18:03:04 +0200
changeset 223492 78ad22d3b5f3de3028ffb6d633a84332b0d7d01b
parent 223491 bf9daaf1ef28cd833eab11b2f3fd3427286a1e1b
child 223493 37cd9c64ce972071d50a15b34bc134f3fe88d770
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs848954
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 848954 - Part 2 - Put the thread management in the driver. r=roc
content/media/GraphDriver.cpp
content/media/GraphDriver.h
content/media/MediaStreamGraph.cpp
content/media/MediaStreamGraphImpl.h
--- a/content/media/GraphDriver.cpp
+++ b/content/media/GraphDriver.cpp
@@ -9,16 +9,29 @@
 extern PRLogModuleInfo* gMediaStreamGraphLog;
 #define STREAM_LOG(type, msg) PR_LOG(gMediaStreamGraphLog, type, msg)
 #else
 #define STREAM_LOG(type, msg)
 #endif
 
 namespace mozilla {
 
+struct AutoProfilerUnregisterThread
+{
+  // The empty ctor is used to silence a pre-4.8.0 GCC unused variable warning.
+  AutoProfilerUnregisterThread()
+  {
+  }
+
+  ~AutoProfilerUnregisterThread()
+  {
+    profiler_unregister_thread();
+  }
+};
+
 GraphDriver::GraphDriver(MediaStreamGraphImpl* aGraphImpl)
     : mIterationStart(INITIAL_CURRENT_TIME),
       mIterationEnd(INITIAL_CURRENT_TIME),
       mStateComputedTime(INITIAL_CURRENT_TIME),
       mGraphImpl(aGraphImpl),
       mWaitState(WAITSTATE_RUNNING),
       mNeedAnotherIteration(false),
       mMonitor("MediaStreamGraphMonitor")
@@ -50,20 +63,72 @@ SystemClockDriver::SystemClockDriver(Med
     mInitialTimeStamp(TimeStamp::Now()),
     mLastTimeStamp(TimeStamp::Now()),
     mCurrentTimeStamp(TimeStamp::Now())
 {}
 
 SystemClockDriver::~SystemClockDriver()
 { }
 
+class MediaStreamGraphInitThreadRunnable : public nsRunnable {
+public:
+  explicit MediaStreamGraphInitThreadRunnable(GraphDriver* aDriver)
+    : mDriver(aDriver)
+  {
+  }
+  NS_IMETHOD Run()
+  {
+    char aLocal;
+    profiler_register_thread("MediaStreamGraph", &aLocal);
+    mDriver->RunThread();
+    return NS_OK;
+  }
+private:
+  GraphDriver* mDriver;
+};
+
 void
-SystemClockDriver::RunThread(nsTArray<MessageBlock>& aMessageQueue)
+SystemClockDriver::Start()
+{
+  nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
+  NS_NewNamedThread("MediaStreamGrph", getter_AddRefs(mThread), event);
+}
+
+void
+SystemClockDriver::Dispatch(nsIRunnable* aEvent)
+{
+  mThread->Dispatch(aEvent, NS_DISPATCH_NORMAL);
+}
+
+void
+SystemClockDriver::Stop()
 {
-  while(mGraphImpl->OneIteration(aMessageQueue));
+  NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
+  // mGraph's thread is not running so it's OK to do whatever here
+  STREAM_LOG(PR_LOG_DEBUG, ("Stopping threads for MediaStreamGraph %p", this));
+
+  if (mThread) {
+    mThread->Shutdown();
+    mThread = nullptr;
+  }
+}
+
+void
+SystemClockDriver::RunThread()
+{
+  AutoProfilerUnregisterThread autoUnregister;
+  nsTArray<MessageBlock> messageQueue;
+  {
+    MonitorAutoLock lock(mMonitor);
+    messageQueue.SwapElements(mGraphImpl->MessageQueue());
+  }
+  NS_ASSERTION(!messageQueue.IsEmpty(),
+               "Shouldn't have started a graph with empty message queue!");
+
+  while(mGraphImpl->OneIteration(messageQueue));
 }
 
 void
 SystemClockDriver::GetIntervalForIteration(GraphTime& aFrom, GraphTime& aTo)
 {
   TimeStamp now = TimeStamp::Now();
   aFrom = mIterationStart = IterationEnd();
   aTo = mIterationEnd = mGraphImpl->SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds()) + IterationEnd();
@@ -88,16 +153,22 @@ SystemClockDriver::GetIntervalForIterati
 }
 
 GraphTime
 SystemClockDriver::GetCurrentTime()
 {
   return IterationEnd();
 }
 
+TimeStamp
+SystemClockDriver::GetCurrentTimeStamp()
+{
+  return mCurrentTimeStamp;
+}
+
 void
 SystemClockDriver::DoIteration(nsTArray<MessageBlock>& aMessageQueue)
 {
   mGraphImpl->DoIteration(aMessageQueue);
 }
 
 void
 SystemClockDriver::WaitForNextIteration()
@@ -142,19 +213,54 @@ OfflineClockDriver::OfflineClockDriver(M
 {
 
 }
 
 OfflineClockDriver::~OfflineClockDriver()
 { }
 
 void
-OfflineClockDriver::RunThread(nsTArray<MessageBlock>& aMessageQueue)
+OfflineClockDriver::Start()
+{
+  nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
+  NS_NewNamedThread("MediaStreamGrph", getter_AddRefs(mThread), event);
+}
+
+void
+OfflineClockDriver::Stop()
 {
-  while(mGraphImpl->OneIteration(aMessageQueue));
+  NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
+  // mGraph's thread is not running so it's OK to do whatever here
+  STREAM_LOG(PR_LOG_DEBUG, ("Stopping threads for MediaStreamGraph %p", this));
+
+  if (mThread) {
+    mThread->Shutdown();
+    mThread = nullptr;
+  }
+}
+
+void
+OfflineClockDriver::Dispatch(nsIRunnable* aEvent)
+{
+  mThread->Dispatch(aEvent, NS_DISPATCH_NORMAL);
+}
+
+void
+OfflineClockDriver::RunThread()
+{
+  AutoProfilerUnregisterThread autoUnregister;
+  nsTArray<MessageBlock> messageQueue;
+  {
+    MonitorAutoLock lock(mMonitor);
+    messageQueue.SwapElements(mGraphImpl->MessageQueue());
+  }
+  NS_ASSERTION(!messageQueue.IsEmpty(),
+               "Shouldn't have started a graph with empty message queue!");
+
+  while(mGraphImpl->OneIteration(messageQueue));
 }
 
 void
 OfflineClockDriver::GetIntervalForIteration(GraphTime& aFrom, GraphTime& aTo)
 {
   aFrom = mIterationStart = IterationEnd();
   aTo = mIterationEnd = IterationEnd() + mGraphImpl->MillisecondsToMediaTime(mSlice);
   PR_LOG(gMediaStreamGraphLog, PR_LOG_DEBUG+1, ("Updating offline current time to %f (%f)",
--- a/content/media/GraphDriver.h
+++ b/content/media/GraphDriver.h
@@ -33,33 +33,46 @@ const GraphTime GRAPH_TIME_MAX = MEDIA_T
  */
 class GraphDriver
 {
 public:
   GraphDriver(MediaStreamGraphImpl* aGraphImpl);
   virtual ~GraphDriver()
   { }
 
-  /* Main loop */
-  virtual void RunThread(nsTArray<MessageBlock>& aMessageQueue) = 0;
+  /**
+   * Runs main control loop on the graph thread. Normally a single invocation
+   * of this runs for the entire lifetime of the graph thread.
+   */
+  virtual void RunThread() = 0;
   /* When the graph wakes up to do an iteration, this returns the range of time
    * that will be processed. */
   virtual void GetIntervalForIteration(GraphTime& aFrom,
                                        GraphTime& aTo) = 0;
   /* Returns the current time for this graph. This is the end of the current
    * iteration. */
   virtual GraphTime GetCurrentTime() = 0;
   /* Do one iteration of the graph. aMessageQueue are the messages that will be
    * processed at the beginning of the iteration. */
   virtual void DoIteration(nsTArray<MessageBlock>& aMessageQueue) = 0;
   /* For real-time graphs, this waits until it's time to process more data. For
    * offline graphs, this is a no-op. */
   virtual void WaitForNextIteration() = 0;
   /* Wakes up the graph if it is waiting. */
   virtual void WakeUp() = 0;
+  /* Start the graph, creating the thread. */
+  virtual void Start() = 0;
+  /* Stop the graph, shutting down the thread. */
+  virtual void Stop() = 0;
+  /* Dispatch an event to the graph's thread. */
+  virtual void Dispatch(nsIRunnable* aEvent) = 0;
+
+  virtual TimeStamp GetCurrentTimeStamp() {
+    MOZ_ASSERT(false, "This clock does not support getting the current time stamp.");
+  }
 
   bool IsWaiting() {
     return mWaitState == WAITSTATE_WAITING_INDEFINITELY ||
            mWaitState == WAITSTATE_WAITING_FOR_NEXT_ITERATION;
   }
 
   bool IsWaitingIndefinitly() {
     return mWaitState == WAITSTATE_WAITING_INDEFINITELY;
@@ -143,16 +156,17 @@ protected:
     WAITSTATE_WAITING_FOR_NEXT_ITERATION,
     // RunThread() is paused indefinitely waiting for something to change
     WAITSTATE_WAITING_INDEFINITELY,
     // Something has signaled RunThread() to wake up immediately,
     // but it hasn't done so yet
     WAITSTATE_WAKING_UP
   };
   WaitState mWaitState;
+
   bool mNeedAnotherIteration;
   // Monitor to synchronize the graph thread and the main thread.
   Monitor mMonitor;
 };
 
 /**
  * A driver holder allows a MediaStreamGraph to seamlessly switch between
  * different Drivers, remembering the current time of the graph at time of
@@ -186,47 +200,57 @@ protected:
  * A SystemClockDriver drives a MediaStreamGraph using a system clock, and waits
  * using a monitor, between each iteration.
  */
 class SystemClockDriver : public GraphDriver
 {
 public:
   SystemClockDriver(MediaStreamGraphImpl* aGraphImpl);
   virtual ~SystemClockDriver();
-  virtual void RunThread(nsTArray<MessageBlock>& aMessageQueue) MOZ_OVERRIDE;
+  virtual void Start() MOZ_OVERRIDE;
+  virtual void Stop() MOZ_OVERRIDE;
+  virtual void Dispatch(nsIRunnable* aEvent) MOZ_OVERRIDE;
+  virtual void RunThread() MOZ_OVERRIDE;
   virtual void GetIntervalForIteration(GraphTime& aFrom,
                                        GraphTime& aTo) MOZ_OVERRIDE;
   virtual GraphTime GetCurrentTime() MOZ_OVERRIDE;
   virtual void DoIteration(nsTArray<MessageBlock>& aMessageQueue) MOZ_OVERRIDE;
   virtual void WaitForNextIteration() MOZ_OVERRIDE;
   virtual void WakeUp() MOZ_OVERRIDE;
 
+  virtual TimeStamp GetCurrentTimeStamp() MOZ_OVERRIDE;
+
 private:
   TimeStamp mInitialTimeStamp;
   TimeStamp mLastTimeStamp;
   TimeStamp mCurrentTimeStamp;
+  nsCOMPtr<nsIThread> mThread;
 };
 
 /**
  * An OfflineClockDriver runs the graph as fast as possible, without waiting
  * between iteration.
  */
 class OfflineClockDriver : public GraphDriver
 {
 public:
   OfflineClockDriver(MediaStreamGraphImpl* aGraphImpl, GraphTime aSlice);
   virtual ~OfflineClockDriver();
-  virtual void RunThread(nsTArray<MessageBlock>& aMessageQueue) MOZ_OVERRIDE;
+  virtual void Start() MOZ_OVERRIDE;
+  virtual void Stop() MOZ_OVERRIDE;
+  virtual void Dispatch(nsIRunnable* aEvent) MOZ_OVERRIDE;
+  virtual void RunThread() MOZ_OVERRIDE;
   virtual void GetIntervalForIteration(GraphTime& aFrom,
                                        GraphTime& aTo) MOZ_OVERRIDE;
   virtual GraphTime GetCurrentTime() MOZ_OVERRIDE;
   virtual void DoIteration(nsTArray<MessageBlock>& aMessageQueue) MOZ_OVERRIDE;
   virtual void WaitForNextIteration() MOZ_OVERRIDE;
   virtual void WakeUp() MOZ_OVERRIDE;
 
 private:
   // Time, in GraphTime, for each iteration
   GraphTime mSlice;
+  nsCOMPtr<nsIThread> mThread;
 };
 
 }
 
 #endif // GRAPHDRIVER_H_
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -734,17 +734,17 @@ MediaStreamGraphImpl::RecomputeBlocking(
         RecomputeBlockingAt(streamSet, t, aEndBlockingDecisions, &end);
         if (end < GRAPH_TIME_MAX) {
           blockingDecisionsWillChange = true;
         }
       }
     }
 
     GraphTime end;
-    stream->mBlocked.GetAt(mCurrentTime, &end);
+    stream->mBlocked.GetAt(IterationEnd(), &end);
     if (end < GRAPH_TIME_MAX) {
       blockingDecisionsWillChange = true;
     }
   }
   STREAM_LOG(PR_LOG_DEBUG+1, ("Media graph %p computed blocking for interval %f to %f",
                               this, MediaTimeToSeconds(CurrentDriver()->StateComputedTime()),
                               MediaTimeToSeconds(aEndBlockingDecisions)));
 
@@ -1092,17 +1092,17 @@ MediaStreamGraphImpl::PlayVideo(MediaStr
   if (!frame || *frame == aStream->mLastPlayedVideoFrame)
     return;
 
   STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing video frame %p (%dx%d)",
                               aStream, frame->GetImage(), frame->GetIntrinsicSize().width,
                               frame->GetIntrinsicSize().height));
   GraphTime startTime = StreamTimeToGraphTime(aStream,
       track->TicksToTimeRoundDown(start), INCLUDE_TRAILING_BLOCKED_INTERVAL);
-  TimeStamp targetTime = mCurrentTimeStamp +
+  TimeStamp targetTime = CurrentDriver()->GetCurrentTimeStamp() +
       TimeDuration::FromMilliseconds(double(startTime - IterationEnd()));
   for (uint32_t i = 0; i < aStream->mVideoOutputs.Length(); ++i) {
     VideoFrameContainer* output = aStream->mVideoOutputs[i];
 
     if (frame->GetForceBlack()) {
       nsRefPtr<Image> image =
         output->GetImageContainer()->CreateImage(ImageFormat::PLANAR_YCBCR);
       if (image) {
@@ -1268,29 +1268,16 @@ MediaStreamGraphImpl::ResumeAllAudioOutp
     for (uint32_t j = 0; j < s->mAudioOutputStreams.Length(); ++j) {
       s->mAudioOutputStreams[j].mStream->Resume();
     }
   }
 
   mAudioOutputsPaused = false;
 }
 
-struct AutoProfilerUnregisterThread
-{
-  // The empty ctor is used to silence a pre-4.8.0 GCC unused variable warning.
-  AutoProfilerUnregisterThread()
-  {
-  }
-
-  ~AutoProfilerUnregisterThread()
-  {
-    profiler_unregister_thread();
-  }
-};
-
 void
 MediaStreamGraphImpl::DoIteration(nsTArray<MessageBlock>& aMessageQueue)
 {
   // Calculate independent action times for each batch of messages (each
   // batch corresponding to an event loop task). This isolates the performance
   // of different scripts to some extent.
   for (uint32_t i = 0; i < aMessageQueue.Length(); ++i) {
     mProcessingGraphUpdateIndex = aMessageQueue[i].mGraphUpdateIndex;
@@ -1436,32 +1423,16 @@ MediaStreamGraphImpl::OneIteration(nsTAr
 
     aMessageQueue.SwapElements(mMessageQueue);
   }
 
   return true;
 }
 
 void
-MediaStreamGraphImpl::RunThread()
-{
-  nsTArray<MessageBlock> messageQueue;
-  {
-    MonitorAutoLock lock(CurrentDriver()->GetThreadMonitor());
-    messageQueue.SwapElements(mMessageQueue);
-  }
-  NS_ASSERTION(!messageQueue.IsEmpty(),
-               "Shouldn't have started a graph with empty message queue!");
-
-  AutoProfilerUnregisterThread autoUnregister;
-
-  CurrentDriver()->RunThread(messageQueue);
-}
-
-void
 MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate)
 {
   CurrentDriver()->GetThreadMonitor().AssertCurrentThreadOwns();
 
   MediaStream* stream = aUpdate->mStream;
   if (!stream)
     return;
   stream->mMainThreadCurrentTime = aUpdate->mNextMainThreadCurrentTime;
@@ -1471,83 +1442,57 @@ MediaStreamGraphImpl::ApplyStreamUpdate(
     stream->mWrapper->NotifyStreamStateChanged();
   }
   for (int32_t i = stream->mMainThreadListeners.Length() - 1; i >= 0; --i) {
     stream->mMainThreadListeners[i]->NotifyMainThreadStateChanged();
   }
 }
 
 void
-MediaStreamGraphImpl::ShutdownThreads()
-{
-  NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
-  // mGraph's thread is not running so it's OK to do whatever here
-  STREAM_LOG(PR_LOG_DEBUG, ("Stopping threads for MediaStreamGraph %p", this));
-
-  if (mThread) {
-    mThread->Shutdown();
-    mThread = nullptr;
-  }
-}
-
-void
 MediaStreamGraphImpl::ForceShutDown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
   STREAM_LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p ForceShutdown", this));
   {
     MonitorAutoLock lock(CurrentDriver()->GetThreadMonitor());
     mForceShutDown = true;
     CurrentDriver()->EnsureNextIterationLocked();
   }
 }
 
 namespace {
 
-class MediaStreamGraphInitThreadRunnable : public nsRunnable {
+class MediaStreamGraphThreadRunnable : public nsRunnable {
 public:
-  explicit MediaStreamGraphInitThreadRunnable(MediaStreamGraphImpl* aGraph)
-    : mGraph(aGraph)
+  explicit MediaStreamGraphThreadRunnable(GraphDriver* aDriver)
+    : mDriver(aDriver)
   {
   }
   NS_IMETHOD Run()
   {
-    char aLocal;
-    profiler_register_thread("MediaStreamGraph", &aLocal);
-    mGraph->RunThread();
+    mDriver->RunThread();
     return NS_OK;
   }
 private:
-  MediaStreamGraphImpl* mGraph;
-};
-
-class MediaStreamGraphThreadRunnable : public nsRunnable {
-public:
-  explicit MediaStreamGraphThreadRunnable(MediaStreamGraphImpl* aGraph)
-    : mGraph(aGraph)
-  {
-  }
-  NS_IMETHOD Run()
-  {
-    mGraph->RunThread();
-    return NS_OK;
-  }
-private:
-  MediaStreamGraphImpl* mGraph;
+  GraphDriver* mDriver;
 };
 
 class MediaStreamGraphShutDownRunnable : public nsRunnable {
 public:
-  MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph) : mGraph(aGraph) {}
+  MediaStreamGraphShutDownRunnable(MediaStreamGraphImpl* aGraph,
+                                   GraphDriver* aDriver)
+    : mGraph(aGraph),
+      mDriver(aDriver)
+  {}
   NS_IMETHOD Run()
   {
     NS_ASSERTION(mGraph->mDetectedNotRunning,
                  "We should know the graph thread control loop isn't running!");
 
-    mGraph->ShutdownThreads();
+    mDriver->Stop();
 
     // mGraph's thread is not running so it's OK to do whatever here
     if (mGraph->IsEmpty()) {
       // mGraph is no longer needed, so delete it.
       mGraph->Destroy();
     } else {
       // The graph is not empty.  We must be in a forced shutdown, or a
       // non-realtime graph that has finished processing.  Some later
@@ -1564,16 +1509,17 @@ public:
 
       mGraph->mLifecycleState =
         MediaStreamGraphImpl::LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION;
     }
     return NS_OK;
   }
 private:
   MediaStreamGraphImpl* mGraph;
+  GraphDriver* mDriver;
 };
 
 class MediaStreamGraphStableStateRunnable : public nsRunnable {
 public:
   explicit MediaStreamGraphStableStateRunnable(MediaStreamGraphImpl* aGraph)
     : mGraph(aGraph)
   {
   }
@@ -1645,34 +1591,33 @@ MediaStreamGraphImpl::RunInStableState()
     // Don't start the thread for a non-realtime graph until it has been
     // explicitly started by StartNonRealtimeProcessing.
     if (mLifecycleState == LIFECYCLE_THREAD_NOT_STARTED &&
         (mRealtime || mNonRealtimeProcessing)) {
       mLifecycleState = LIFECYCLE_RUNNING;
       // Start the thread now. We couldn't start it earlier because
       // the graph might exit immediately on finding it has no streams. The
       // first message for a new graph must create a stream.
-      nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
-      NS_NewNamedThread("MediaStreamGrph", getter_AddRefs(mThread), event);
+      CurrentDriver()->Start();
     }
 
     if (mCurrentTaskMessageQueue.IsEmpty()) {
       if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP && IsEmpty()) {
         // Complete shutdown. First, ensure that this graph is no longer used.
         // A new graph graph will be created if one is needed.
         STREAM_LOG(PR_LOG_DEBUG, ("Disconnecting MediaStreamGraph %p", this));
         if (this == gGraph) {
           // null out gGraph if that's the graph being shut down
           gGraph = nullptr;
         }
         // Asynchronously clean up old graph. We don't want to do this
         // synchronously because it spins the event loop waiting for threads
         // to shut down, and we don't want to do that in a stable state handler.
         mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
-        nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this);
+        nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this, CurrentDriver());
         NS_DispatchToMainThread(event);
       }
     } else {
       if (mLifecycleState <= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
         MessageBlock* block = mMessageQueue.AppendElement();
         block->mMessages.SwapElements(mCurrentTaskMessageQueue);
         block->mGraphUpdateIndex = mNextGraphUpdateIndex;
         ++mNextGraphUpdateIndex;
@@ -1684,34 +1629,34 @@ MediaStreamGraphImpl::RunInStableState()
       // shutdown or it's a non-realtime graph that has already terminated
       // processing.
       if (mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP &&
           mRealtime && !mForceShutDown) {
         mLifecycleState = LIFECYCLE_RUNNING;
         // Revive the MediaStreamGraph since we have more messages going to it.
         // Note that we need to put messages into its queue before reviving it,
         // or it might exit immediately.
-        nsCOMPtr<nsIRunnable> event = new MediaStreamGraphThreadRunnable(this);
-        mThread->Dispatch(event, 0);
+        nsCOMPtr<nsIRunnable> event = new MediaStreamGraphThreadRunnable(CurrentDriver());
+        CurrentDriver()->Dispatch(event);
       }
     }
 
     if ((mForceShutDown || !mRealtime) &&
         mLifecycleState == LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
       // Defer calls to RunDuringShutdown() to happen while mMonitor is not held.
       for (uint32_t i = 0; i < mMessageQueue.Length(); ++i) {
         MessageBlock& mb = mMessageQueue[i];
         controlMessagesToRunDuringShutdown.MoveElementsFrom(mb.mMessages);
       }
       mMessageQueue.Clear();
       MOZ_ASSERT(mCurrentTaskMessageQueue.IsEmpty());
       // Stop MediaStreamGraph threads. Do not clear gGraph since
       // we have outstanding DOM objects that may need it.
       mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
-      nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this);
+      nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this, CurrentDriver());
       NS_DispatchToMainThread(event);
     }
 
     mDetectedNotRunning = mLifecycleState > LIFECYCLE_RUNNING;
   }
 
   // Make sure we get a new current time in the next event loop task
   mPostedRunInStableState = false;
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -173,29 +173,28 @@ public:
 
   /**
    * Called before the thread runs.
    */
   void Init();
   // The following methods run on the graph thread (or possibly the main thread if
   // mLifecycleState > LIFECYCLE_RUNNING)
   /**
-   * Runs main control loop on the graph thread. Normally a single invocation
-   * of this runs for the entire lifetime of the graph thread.
-   */
-  void RunThread();
-  /**
    * Do one full iteration of the graph. Returns false if the graph should
    * stop, true otherwise.
    */
   bool OneIteration(nsTArray<MessageBlock>& aMessageQueue);
   /*
    * This does the actual iteration: Message processing, MediaStream ordering,
    * blocking computation and processing.
    */
+  nsTArray<MessageBlock>& MessageQueue() {
+    CurrentDriver()->GetThreadMonitor().AssertCurrentThreadOwns();
+    return mMessageQueue;
+  }
   void DoIteration(nsTArray<MessageBlock>& aMessageQueue);
 
   /* This is the end of the current iteration, that is, the current time of the
    * graph. */
   GraphTime IterationEnd();
   /**
    * Ensure there is an event posted to the main thread to run RunInStableState.
    * mMonitor must be held.
@@ -412,22 +411,16 @@ public:
 
   TrackTicks TimeToTicksRoundDown(TrackRate aRate, StreamTime aTime)
   {
     return RateConvertTicksRoundDown(aRate, GraphRate(), aTime);
   }
 
   // Data members
 
-  /**
-   * Media graph thread.
-   * Readonly after initialization on the main thread.
-   */
-  nsCOMPtr<nsIThread> mThread;
-
   // The following state is managed on the graph thread only, unless
   // mLifecycleState > LIFECYCLE_RUNNING in which case the graph thread
   // is not running and this state can be used from the main thread.
 
   /**
    * The graph keeps a reference to each stream.
    * References are maintained manually to simplify reordering without
    * unnecessary thread-safe refcount changes.
@@ -435,35 +428,16 @@ public:
   nsTArray<MediaStream*> mStreams;
   /**
    * Streams from mFirstCycleBreaker to the end of mStreams produce output
    * before they receive input.  They correspond to DelayNodes that are in
    * cycles.
    */
   uint32_t mFirstCycleBreaker;
   /**
-   * The current graph time for the current iteration of the RunThread control
-   * loop.
-   */
-  GraphTime mCurrentTime;
-  /**
-   * Blocking decisions and all stream contents have been computed up to this
-   * time. The next batch of updates from the main thread will be processed
-   * at this time. Always >= mCurrentTime.
-   */
-  GraphTime mStateComputedTime;
-  /**
-   * A timestamp corresponding to INITIAL_CURRENT_TIME.
-   */
-  TimeStamp mInitialTimeStamp;
-  /**
-   * The real timestamp of the latest run of UpdateCurrentTime.
-   */
-  TimeStamp mCurrentTimeStamp;
-  /**
    * Date of the last time we updated the main thread with the graph state.
    */
   TimeStamp mLastMainThreadUpdate;
   /**
    * Which update batch we are currently processing.
    */
   int64_t mProcessingGraphUpdateIndex;
   /**