Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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/. */
7 #include "MediaTaskQueue.h"
8 #include "nsThreadUtils.h"
9 #include "SharedThreadPool.h"
11 namespace mozilla {
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 }
22 MediaTaskQueue::~MediaTaskQueue()
23 {
24 MonitorAutoLock mon(mQueueMonitor);
25 MOZ_ASSERT(mIsShutdown);
26 MOZ_COUNT_DTOR(MediaTaskQueue);
27 }
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;
48 return NS_OK;
49 }
51 void
52 MediaTaskQueue::AwaitIdle()
53 {
54 MonitorAutoLock mon(mQueueMonitor);
55 AwaitIdleLocked();
56 }
58 void
59 MediaTaskQueue::AwaitIdleLocked()
60 {
61 mQueueMonitor.AssertCurrentThreadOwns();
62 MOZ_ASSERT(mIsRunning || mTasks.empty());
63 while (mIsRunning) {
64 mQueueMonitor.Wait();
65 }
66 }
68 void
69 MediaTaskQueue::Shutdown()
70 {
71 MonitorAutoLock mon(mQueueMonitor);
72 mIsShutdown = true;
73 AwaitIdleLocked();
74 }
76 void
77 MediaTaskQueue::Flush()
78 {
79 MonitorAutoLock mon(mQueueMonitor);
80 while (!mTasks.empty()) {
81 mTasks.pop();
82 }
83 AwaitIdleLocked();
84 }
86 bool
87 MediaTaskQueue::IsEmpty()
88 {
89 MonitorAutoLock mon(mQueueMonitor);
90 return mTasks.empty();
91 }
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 }
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);
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();
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;
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 }
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 }
168 return NS_OK;
169 }
171 } // namespace mozilla