content/media/MediaTaskQueue.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "MediaTaskQueue.h"
michael@0 8 #include "nsThreadUtils.h"
michael@0 9 #include "SharedThreadPool.h"
michael@0 10
michael@0 11 namespace mozilla {
michael@0 12
michael@0 13 MediaTaskQueue::MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool)
michael@0 14 : mPool(aPool)
michael@0 15 , mQueueMonitor("MediaTaskQueue::Queue")
michael@0 16 , mIsRunning(false)
michael@0 17 , mIsShutdown(false)
michael@0 18 {
michael@0 19 MOZ_COUNT_CTOR(MediaTaskQueue);
michael@0 20 }
michael@0 21
michael@0 22 MediaTaskQueue::~MediaTaskQueue()
michael@0 23 {
michael@0 24 MonitorAutoLock mon(mQueueMonitor);
michael@0 25 MOZ_ASSERT(mIsShutdown);
michael@0 26 MOZ_COUNT_DTOR(MediaTaskQueue);
michael@0 27 }
michael@0 28
michael@0 29 nsresult
michael@0 30 MediaTaskQueue::Dispatch(TemporaryRef<nsIRunnable> aRunnable)
michael@0 31 {
michael@0 32 MonitorAutoLock mon(mQueueMonitor);
michael@0 33 if (mIsShutdown) {
michael@0 34 return NS_ERROR_FAILURE;
michael@0 35 }
michael@0 36 mTasks.push(aRunnable);
michael@0 37 if (mIsRunning) {
michael@0 38 return NS_OK;
michael@0 39 }
michael@0 40 RefPtr<nsIRunnable> runner(new Runner(this));
michael@0 41 nsresult rv = mPool->Dispatch(runner, NS_DISPATCH_NORMAL);
michael@0 42 if (NS_FAILED(rv)) {
michael@0 43 NS_WARNING("Failed to dispatch runnable to run MediaTaskQueue");
michael@0 44 return rv;
michael@0 45 }
michael@0 46 mIsRunning = true;
michael@0 47
michael@0 48 return NS_OK;
michael@0 49 }
michael@0 50
michael@0 51 void
michael@0 52 MediaTaskQueue::AwaitIdle()
michael@0 53 {
michael@0 54 MonitorAutoLock mon(mQueueMonitor);
michael@0 55 AwaitIdleLocked();
michael@0 56 }
michael@0 57
michael@0 58 void
michael@0 59 MediaTaskQueue::AwaitIdleLocked()
michael@0 60 {
michael@0 61 mQueueMonitor.AssertCurrentThreadOwns();
michael@0 62 MOZ_ASSERT(mIsRunning || mTasks.empty());
michael@0 63 while (mIsRunning) {
michael@0 64 mQueueMonitor.Wait();
michael@0 65 }
michael@0 66 }
michael@0 67
michael@0 68 void
michael@0 69 MediaTaskQueue::Shutdown()
michael@0 70 {
michael@0 71 MonitorAutoLock mon(mQueueMonitor);
michael@0 72 mIsShutdown = true;
michael@0 73 AwaitIdleLocked();
michael@0 74 }
michael@0 75
michael@0 76 void
michael@0 77 MediaTaskQueue::Flush()
michael@0 78 {
michael@0 79 MonitorAutoLock mon(mQueueMonitor);
michael@0 80 while (!mTasks.empty()) {
michael@0 81 mTasks.pop();
michael@0 82 }
michael@0 83 AwaitIdleLocked();
michael@0 84 }
michael@0 85
michael@0 86 bool
michael@0 87 MediaTaskQueue::IsEmpty()
michael@0 88 {
michael@0 89 MonitorAutoLock mon(mQueueMonitor);
michael@0 90 return mTasks.empty();
michael@0 91 }
michael@0 92
michael@0 93 bool
michael@0 94 MediaTaskQueue::IsCurrentThreadIn()
michael@0 95 {
michael@0 96 #ifdef DEBUG
michael@0 97 MonitorAutoLock mon(mQueueMonitor);
michael@0 98 return NS_GetCurrentThread() == mRunningThread;
michael@0 99 #else
michael@0 100 return false;
michael@0 101 #endif
michael@0 102 }
michael@0 103
michael@0 104 nsresult
michael@0 105 MediaTaskQueue::Runner::Run()
michael@0 106 {
michael@0 107 RefPtr<nsIRunnable> event;
michael@0 108 {
michael@0 109 MonitorAutoLock mon(mQueue->mQueueMonitor);
michael@0 110 MOZ_ASSERT(mQueue->mIsRunning);
michael@0 111 mQueue->mRunningThread = NS_GetCurrentThread();
michael@0 112 if (mQueue->mTasks.size() == 0) {
michael@0 113 mQueue->mIsRunning = false;
michael@0 114 mon.NotifyAll();
michael@0 115 return NS_OK;
michael@0 116 }
michael@0 117 event = mQueue->mTasks.front();
michael@0 118 mQueue->mTasks.pop();
michael@0 119 }
michael@0 120 MOZ_ASSERT(event);
michael@0 121
michael@0 122 // Note that dropping the queue monitor before running the task, and
michael@0 123 // taking the monitor again after the task has run ensures we have memory
michael@0 124 // fences enforced. This means that if the object we're calling wasn't
michael@0 125 // designed to be threadsafe, it will be, provided we're only calling it
michael@0 126 // in this task queue.
michael@0 127 event->Run();
michael@0 128
michael@0 129 // Drop the reference to event. The event will hold a reference to the
michael@0 130 // object it's calling, and we don't want to keep it alive, it may be
michael@0 131 // making assumptions what holds references to it. This is especially
michael@0 132 // the case if the object is waiting for us to shutdown, so that it
michael@0 133 // can shutdown (like in the MediaDecoderStateMachine's SHUTDOWN case).
michael@0 134 event = nullptr;
michael@0 135
michael@0 136 {
michael@0 137 MonitorAutoLock mon(mQueue->mQueueMonitor);
michael@0 138 if (mQueue->mTasks.size() == 0) {
michael@0 139 // No more events to run. Exit the task runner.
michael@0 140 mQueue->mIsRunning = false;
michael@0 141 mon.NotifyAll();
michael@0 142 mQueue->mRunningThread = nullptr;
michael@0 143 return NS_OK;
michael@0 144 }
michael@0 145 }
michael@0 146
michael@0 147 // There's at least one more event that we can run. Dispatch this Runner
michael@0 148 // to the thread pool again to ensure it runs again. Note that we don't just
michael@0 149 // run in a loop here so that we don't hog the thread pool. This means we may
michael@0 150 // run on another thread next time, but we rely on the memory fences from
michael@0 151 // mQueueMonitor for thread safety of non-threadsafe tasks.
michael@0 152 {
michael@0 153 MonitorAutoLock mon(mQueue->mQueueMonitor);
michael@0 154 // Note: Hold the monitor *before* we dispatch, in case we context switch
michael@0 155 // to another thread pool in the queue immediately and take the lock in the
michael@0 156 // other thread; mRunningThread could be set to the new thread's value and
michael@0 157 // then incorrectly anulled below in that case.
michael@0 158 nsresult rv = mQueue->mPool->Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 159 if (NS_FAILED(rv)) {
michael@0 160 // Failed to dispatch, shutdown!
michael@0 161 mQueue->mIsRunning = false;
michael@0 162 mQueue->mIsShutdown = true;
michael@0 163 mon.NotifyAll();
michael@0 164 }
michael@0 165 mQueue->mRunningThread = nullptr;
michael@0 166 }
michael@0 167
michael@0 168 return NS_OK;
michael@0 169 }
michael@0 170
michael@0 171 } // namespace mozilla

mercurial