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