1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/MediaTaskQueue.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,171 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "MediaTaskQueue.h" 1.11 +#include "nsThreadUtils.h" 1.12 +#include "SharedThreadPool.h" 1.13 + 1.14 +namespace mozilla { 1.15 + 1.16 +MediaTaskQueue::MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool) 1.17 + : mPool(aPool) 1.18 + , mQueueMonitor("MediaTaskQueue::Queue") 1.19 + , mIsRunning(false) 1.20 + , mIsShutdown(false) 1.21 +{ 1.22 + MOZ_COUNT_CTOR(MediaTaskQueue); 1.23 +} 1.24 + 1.25 +MediaTaskQueue::~MediaTaskQueue() 1.26 +{ 1.27 + MonitorAutoLock mon(mQueueMonitor); 1.28 + MOZ_ASSERT(mIsShutdown); 1.29 + MOZ_COUNT_DTOR(MediaTaskQueue); 1.30 +} 1.31 + 1.32 +nsresult 1.33 +MediaTaskQueue::Dispatch(TemporaryRef<nsIRunnable> aRunnable) 1.34 +{ 1.35 + MonitorAutoLock mon(mQueueMonitor); 1.36 + if (mIsShutdown) { 1.37 + return NS_ERROR_FAILURE; 1.38 + } 1.39 + mTasks.push(aRunnable); 1.40 + if (mIsRunning) { 1.41 + return NS_OK; 1.42 + } 1.43 + RefPtr<nsIRunnable> runner(new Runner(this)); 1.44 + nsresult rv = mPool->Dispatch(runner, NS_DISPATCH_NORMAL); 1.45 + if (NS_FAILED(rv)) { 1.46 + NS_WARNING("Failed to dispatch runnable to run MediaTaskQueue"); 1.47 + return rv; 1.48 + } 1.49 + mIsRunning = true; 1.50 + 1.51 + return NS_OK; 1.52 +} 1.53 + 1.54 +void 1.55 +MediaTaskQueue::AwaitIdle() 1.56 +{ 1.57 + MonitorAutoLock mon(mQueueMonitor); 1.58 + AwaitIdleLocked(); 1.59 +} 1.60 + 1.61 +void 1.62 +MediaTaskQueue::AwaitIdleLocked() 1.63 +{ 1.64 + mQueueMonitor.AssertCurrentThreadOwns(); 1.65 + MOZ_ASSERT(mIsRunning || mTasks.empty()); 1.66 + while (mIsRunning) { 1.67 + mQueueMonitor.Wait(); 1.68 + } 1.69 +} 1.70 + 1.71 +void 1.72 +MediaTaskQueue::Shutdown() 1.73 +{ 1.74 + MonitorAutoLock mon(mQueueMonitor); 1.75 + mIsShutdown = true; 1.76 + AwaitIdleLocked(); 1.77 +} 1.78 + 1.79 +void 1.80 +MediaTaskQueue::Flush() 1.81 +{ 1.82 + MonitorAutoLock mon(mQueueMonitor); 1.83 + while (!mTasks.empty()) { 1.84 + mTasks.pop(); 1.85 + } 1.86 + AwaitIdleLocked(); 1.87 +} 1.88 + 1.89 +bool 1.90 +MediaTaskQueue::IsEmpty() 1.91 +{ 1.92 + MonitorAutoLock mon(mQueueMonitor); 1.93 + return mTasks.empty(); 1.94 +} 1.95 + 1.96 +bool 1.97 +MediaTaskQueue::IsCurrentThreadIn() 1.98 +{ 1.99 +#ifdef DEBUG 1.100 + MonitorAutoLock mon(mQueueMonitor); 1.101 + return NS_GetCurrentThread() == mRunningThread; 1.102 +#else 1.103 + return false; 1.104 +#endif 1.105 +} 1.106 + 1.107 +nsresult 1.108 +MediaTaskQueue::Runner::Run() 1.109 +{ 1.110 + RefPtr<nsIRunnable> event; 1.111 + { 1.112 + MonitorAutoLock mon(mQueue->mQueueMonitor); 1.113 + MOZ_ASSERT(mQueue->mIsRunning); 1.114 + mQueue->mRunningThread = NS_GetCurrentThread(); 1.115 + if (mQueue->mTasks.size() == 0) { 1.116 + mQueue->mIsRunning = false; 1.117 + mon.NotifyAll(); 1.118 + return NS_OK; 1.119 + } 1.120 + event = mQueue->mTasks.front(); 1.121 + mQueue->mTasks.pop(); 1.122 + } 1.123 + MOZ_ASSERT(event); 1.124 + 1.125 + // Note that dropping the queue monitor before running the task, and 1.126 + // taking the monitor again after the task has run ensures we have memory 1.127 + // fences enforced. This means that if the object we're calling wasn't 1.128 + // designed to be threadsafe, it will be, provided we're only calling it 1.129 + // in this task queue. 1.130 + event->Run(); 1.131 + 1.132 + // Drop the reference to event. The event will hold a reference to the 1.133 + // object it's calling, and we don't want to keep it alive, it may be 1.134 + // making assumptions what holds references to it. This is especially 1.135 + // the case if the object is waiting for us to shutdown, so that it 1.136 + // can shutdown (like in the MediaDecoderStateMachine's SHUTDOWN case). 1.137 + event = nullptr; 1.138 + 1.139 + { 1.140 + MonitorAutoLock mon(mQueue->mQueueMonitor); 1.141 + if (mQueue->mTasks.size() == 0) { 1.142 + // No more events to run. Exit the task runner. 1.143 + mQueue->mIsRunning = false; 1.144 + mon.NotifyAll(); 1.145 + mQueue->mRunningThread = nullptr; 1.146 + return NS_OK; 1.147 + } 1.148 + } 1.149 + 1.150 + // There's at least one more event that we can run. Dispatch this Runner 1.151 + // to the thread pool again to ensure it runs again. Note that we don't just 1.152 + // run in a loop here so that we don't hog the thread pool. This means we may 1.153 + // run on another thread next time, but we rely on the memory fences from 1.154 + // mQueueMonitor for thread safety of non-threadsafe tasks. 1.155 + { 1.156 + MonitorAutoLock mon(mQueue->mQueueMonitor); 1.157 + // Note: Hold the monitor *before* we dispatch, in case we context switch 1.158 + // to another thread pool in the queue immediately and take the lock in the 1.159 + // other thread; mRunningThread could be set to the new thread's value and 1.160 + // then incorrectly anulled below in that case. 1.161 + nsresult rv = mQueue->mPool->Dispatch(this, NS_DISPATCH_NORMAL); 1.162 + if (NS_FAILED(rv)) { 1.163 + // Failed to dispatch, shutdown! 1.164 + mQueue->mIsRunning = false; 1.165 + mQueue->mIsShutdown = true; 1.166 + mon.NotifyAll(); 1.167 + } 1.168 + mQueue->mRunningThread = nullptr; 1.169 + } 1.170 + 1.171 + return NS_OK; 1.172 +} 1.173 + 1.174 +} // namespace mozilla