Bug 848954 - Part 7 - Get rid of the stack allocated message queue. r=roc

In the next patches, when the AudioDriver will be implemented, the audio backend
thread (that we don't control), will return from the stack frame where the
nsTArray that allows the MSG thread to exchange message queues in a efficient
manner with the main thread. We put it in the MediaStreamGraph to avoid adding
an allocation/deallocation per iteration on the MSG thread.

In addition, the graph will be able to run on different threads during its
lifetime, so we can't guarantee a stable stack frame to allocate things on
anymore.

The array are renamed with meaningful names, explaining the double-buffering
pattern: the back queue is filled by the main thread, and is swapped with the
front queue that is processed by the MSG thread.

Arrays accesses are synchronized using the driver's monitor.
This commit is contained in:
Paul Adenot 2014-08-25 15:26:21 +02:00
parent 02b493fd95
commit 06aec8f087
4 changed files with 47 additions and 42 deletions

View File

@ -28,14 +28,14 @@ struct AutoProfilerUnregisterThread
};
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")
{ }
: mIterationStart(INITIAL_CURRENT_TIME),
mIterationEnd(INITIAL_CURRENT_TIME),
mStateComputedTime(INITIAL_CURRENT_TIME),
mGraphImpl(aGraphImpl),
mWaitState(WAITSTATE_RUNNING),
mNeedAnotherIteration(false),
mMonitor("MediaStreamGraphMonitor")
{ }
DriverHolder::DriverHolder(MediaStreamGraphImpl* aGraphImpl)
: mGraphImpl(aGraphImpl),
@ -75,6 +75,10 @@ public:
{
char aLocal;
profiler_register_thread("MediaStreamGraph", &aLocal);
{
MonitorAutoLock mon(mDriver->GetThreadMonitor());
mDriver->mGraphImpl->SwapMessageQueues();
}
mDriver->RunThread();
return NS_OK;
}
@ -122,13 +126,6 @@ void
ThreadedDriver::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!");
bool stillProcessing = true;
while (stillProcessing) {
@ -142,8 +139,7 @@ ThreadedDriver::RunThread()
stillProcessing = mGraphImpl->OneIteration(prevCurrentTime,
nextCurrentTime,
StateComputedTime(),
nextStateComputedTime,
messageQueue);
nextStateComputedTime);
}
}

View File

@ -204,6 +204,8 @@ protected:
GraphTime mLastSwitchOffset;
};
class MediaStreamGraphInitThreadRunnable;
/**
* This class is a driver that manages its own thread.
*/
@ -216,6 +218,7 @@ public:
virtual void Stop() MOZ_OVERRIDE;
virtual void Dispatch(nsIRunnable* aEvent) MOZ_OVERRIDE;
void RunThread();
friend MediaStreamGraphInitThreadRunnable;
private:
nsCOMPtr<nsIThread> mThread;
};

View File

@ -1260,21 +1260,20 @@ MediaStreamGraphImpl::ResumeAllAudioOutputs()
}
void
MediaStreamGraphImpl::UpdateGraph(nsTArray<MessageBlock>& aMessageQueue,
GraphTime aEndBlockingDecision)
MediaStreamGraphImpl::UpdateGraph(GraphTime aEndBlockingDecision)
{
// 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;
nsTArray<nsAutoPtr<ControlMessage> >& messages = aMessageQueue[i].mMessages;
for (uint32_t i = 0; i < mFrontMessageQueue.Length(); ++i) {
mProcessingGraphUpdateIndex = mFrontMessageQueue[i].mGraphUpdateIndex;
nsTArray<nsAutoPtr<ControlMessage> >& messages = mFrontMessageQueue[i].mMessages;
for (uint32_t j = 0; j < messages.Length(); ++j) {
messages[j]->Run();
}
}
aMessageQueue.Clear();
mFrontMessageQueue.Clear();
if (mStreamOrderDirty) {
UpdateStreamOrder();
@ -1383,12 +1382,11 @@ MediaStreamGraphImpl::Process(GraphTime aFrom, GraphTime aTo)
bool
MediaStreamGraphImpl::OneIteration(GraphTime aFrom, GraphTime aTo,
GraphTime aStateFrom, GraphTime aStateEnd,
nsTArray<MessageBlock>& aMessageQueue)
GraphTime aStateFrom, GraphTime aStateEnd)
{
UpdateCurrentTimeForStreams(aFrom, aTo);
UpdateGraph(aMessageQueue, aStateEnd);
UpdateGraph(aStateEnd);
Process(aStateFrom, aStateEnd);
@ -1398,7 +1396,7 @@ MediaStreamGraphImpl::OneIteration(GraphTime aFrom, GraphTime aTo,
MonitorAutoLock lock(CurrentDriver()->GetThreadMonitor());
bool finalUpdate = mForceShutDown ||
(IterationEnd() >= mEndTime && AllFinishedStreamsNotified()) ||
(IsEmpty() && mMessageQueue.IsEmpty());
(IsEmpty() && mBackMessageQueue.IsEmpty());
PrepareUpdatesToMainThreadState(finalUpdate);
if (finalUpdate) {
// Enter shutdown mode. The stable-state handler will detect this
@ -1413,7 +1411,7 @@ MediaStreamGraphImpl::OneIteration(GraphTime aFrom, GraphTime aTo,
CurrentDriver()->WaitForNextIteration();
aMessageQueue.SwapElements(mMessageQueue);
SwapMessageQueues();
}
return true;
@ -1609,7 +1607,7 @@ MediaStreamGraphImpl::RunInStableState()
}
} else {
if (mLifecycleState <= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
MessageBlock* block = mMessageQueue.AppendElement();
MessageBlock* block = mBackMessageQueue.AppendElement();
block->mMessages.SwapElements(mCurrentTaskMessageQueue);
block->mGraphUpdateIndex = mNextGraphUpdateIndex;
++mNextGraphUpdateIndex;
@ -1634,11 +1632,11 @@ MediaStreamGraphImpl::RunInStableState()
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];
for (uint32_t i = 0; i < mBackMessageQueue.Length(); ++i) {
MessageBlock& mb = mBackMessageQueue[i];
controlMessagesToRunDuringShutdown.MoveElementsFrom(mb.mMessages);
}
mMessageQueue.Clear();
mBackMessageQueue.Clear();
MOZ_ASSERT(mCurrentTaskMessageQueue.IsEmpty());
// Stop MediaStreamGraph threads. Do not clear gGraph since
// we have outstanding DOM objects that may need it.

View File

@ -181,15 +181,16 @@ public:
* 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);
void DoIteration();
bool OneIteration(GraphTime aFrom, GraphTime aTo,
GraphTime aStateFrom, GraphTime aStateEnd,
nsTArray<MessageBlock>& aMessageQueue);
GraphTime aStateFrom, GraphTime aStateEnd);
// Get
nsTArray<MessageBlock>& MessageQueue() {
CurrentDriver()->GetThreadMonitor().AssertCurrentThreadOwns();
return mFrontMessageQueue;
}
/* This is the end of the current iteration, that is, the current time of the
* graph. */
@ -226,8 +227,12 @@ public:
* Process graph message for this iteration, update stream processing order,
* and recompute stream blocking until aEndBlockingDecisions.
*/
void UpdateGraph(nsTArray<MessageBlock>& aMessageQueue,
GraphTime aEndBlockingDecisions);
void UpdateGraph(GraphTime aEndBlockingDecisions);
void SwapMessageQueues() {
CurrentDriver()->GetThreadMonitor().AssertCurrentThreadOwns();
mFrontMessageQueue.SwapElements(mBackMessageQueue);
}
/**
* Do all the processing and play the audio and video, ffrom aFrom to aTo.
*/
@ -487,7 +492,10 @@ public:
* A list of batches of messages to process. Each batch is processed
* as an atomic unit.
*/
nsTArray<MessageBlock> mMessageQueue;
/* Message queue processed by the MSG thread during an iteration. */
nsTArray<MessageBlock> mFrontMessageQueue;
/* Message queue in which the main thread appends messages. */
nsTArray<MessageBlock> mBackMessageQueue;
/**
* This enum specifies where this graph is in its lifecycle. This is used
* to control shutdown.