Line data Source code
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 "WorkerThread.h"
8 :
9 : #include "mozilla/Assertions.h"
10 : #include "mozilla/ipc/BackgroundChild.h"
11 : #include "nsIThreadInternal.h"
12 : #include "WorkerPrivate.h"
13 : #include "WorkerRunnable.h"
14 :
15 : #ifdef DEBUG
16 : #include "nsThreadManager.h"
17 : #endif
18 :
19 : namespace mozilla {
20 : namespace dom {
21 : namespace workers {
22 :
23 : using namespace mozilla::ipc;
24 :
25 : namespace {
26 :
27 : // The C stack size. We use the same stack size on all platforms for
28 : // consistency.
29 : const uint32_t kWorkerStackSize = 256 * sizeof(size_t) * 1024;
30 :
31 : } // namespace
32 :
33 2 : WorkerThreadFriendKey::WorkerThreadFriendKey()
34 : {
35 2 : MOZ_COUNT_CTOR(WorkerThreadFriendKey);
36 2 : }
37 :
38 2 : WorkerThreadFriendKey::~WorkerThreadFriendKey()
39 : {
40 2 : MOZ_COUNT_DTOR(WorkerThreadFriendKey);
41 2 : }
42 :
43 : class WorkerThread::Observer final
44 : : public nsIThreadObserver
45 : {
46 : WorkerPrivate* mWorkerPrivate;
47 :
48 : public:
49 1 : explicit Observer(WorkerPrivate* aWorkerPrivate)
50 1 : : mWorkerPrivate(aWorkerPrivate)
51 : {
52 1 : MOZ_ASSERT(aWorkerPrivate);
53 1 : aWorkerPrivate->AssertIsOnWorkerThread();
54 1 : }
55 :
56 : NS_DECL_THREADSAFE_ISUPPORTS
57 :
58 : private:
59 0 : ~Observer()
60 0 : {
61 0 : mWorkerPrivate->AssertIsOnWorkerThread();
62 0 : }
63 :
64 : NS_DECL_NSITHREADOBSERVER
65 : };
66 :
67 1 : WorkerThread::WorkerThread()
68 : : nsThread(nsThread::NOT_MAIN_THREAD, kWorkerStackSize)
69 : , mWorkerPrivateCondVar(mLock, "WorkerThread::mWorkerPrivateCondVar")
70 : , mWorkerPrivate(nullptr)
71 : , mOtherThreadsDispatchingViaEventTarget(0)
72 : #ifdef DEBUG
73 1 : , mAcceptingNonWorkerRunnables(true)
74 : #endif
75 : {
76 1 : }
77 :
78 0 : WorkerThread::~WorkerThread()
79 : {
80 0 : MOZ_ASSERT(!mWorkerPrivate);
81 0 : MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget);
82 0 : MOZ_ASSERT(mAcceptingNonWorkerRunnables);
83 0 : }
84 :
85 : // static
86 : already_AddRefed<WorkerThread>
87 1 : WorkerThread::Create(const WorkerThreadFriendKey& /* aKey */)
88 : {
89 2 : RefPtr<WorkerThread> thread = new WorkerThread();
90 1 : if (NS_FAILED(thread->Init())) {
91 0 : NS_WARNING("Failed to create new thread!");
92 0 : return nullptr;
93 : }
94 :
95 1 : return thread.forget();
96 : }
97 :
98 : void
99 1 : WorkerThread::SetWorker(const WorkerThreadFriendKey& /* aKey */,
100 : WorkerPrivate* aWorkerPrivate)
101 : {
102 1 : MOZ_ASSERT(PR_GetCurrentThread() == mThread);
103 :
104 1 : if (aWorkerPrivate) {
105 : {
106 2 : MutexAutoLock lock(mLock);
107 :
108 1 : MOZ_ASSERT(!mWorkerPrivate);
109 1 : MOZ_ASSERT(mAcceptingNonWorkerRunnables);
110 :
111 1 : mWorkerPrivate = aWorkerPrivate;
112 : #ifdef DEBUG
113 1 : mAcceptingNonWorkerRunnables = false;
114 : #endif
115 : }
116 :
117 1 : mObserver = new Observer(aWorkerPrivate);
118 1 : MOZ_ALWAYS_SUCCEEDS(AddObserver(mObserver));
119 : } else {
120 0 : MOZ_ALWAYS_SUCCEEDS(RemoveObserver(mObserver));
121 0 : mObserver = nullptr;
122 :
123 : {
124 0 : MutexAutoLock lock(mLock);
125 :
126 0 : MOZ_ASSERT(mWorkerPrivate);
127 0 : MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
128 0 : MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget,
129 : "XPCOM Dispatch hapenning at the same time our thread is "
130 : "being unset! This should not be possible!");
131 :
132 0 : while (mOtherThreadsDispatchingViaEventTarget) {
133 0 : mWorkerPrivateCondVar.Wait();
134 : }
135 :
136 : #ifdef DEBUG
137 0 : mAcceptingNonWorkerRunnables = true;
138 : #endif
139 0 : mWorkerPrivate = nullptr;
140 : }
141 : }
142 1 : }
143 :
144 : nsresult
145 1 : WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */,
146 : already_AddRefed<nsIRunnable> aRunnable)
147 : {
148 2 : nsCOMPtr<nsIRunnable> runnable(aRunnable);
149 :
150 : #ifdef DEBUG
151 1 : MOZ_ASSERT(PR_GetCurrentThread() != mThread);
152 1 : MOZ_ASSERT(runnable);
153 : {
154 2 : MutexAutoLock lock(mLock);
155 :
156 1 : MOZ_ASSERT(!mWorkerPrivate);
157 1 : MOZ_ASSERT(mAcceptingNonWorkerRunnables);
158 : }
159 : #endif
160 :
161 1 : nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
162 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
163 0 : return rv;
164 : }
165 :
166 1 : return NS_OK;
167 : }
168 :
169 : nsresult
170 2 : WorkerThread::DispatchAnyThread(const WorkerThreadFriendKey& /* aKey */,
171 : already_AddRefed<WorkerRunnable> aWorkerRunnable)
172 : {
173 : // May be called on any thread!
174 :
175 : #ifdef DEBUG
176 : {
177 2 : const bool onWorkerThread = PR_GetCurrentThread() == mThread;
178 : {
179 4 : MutexAutoLock lock(mLock);
180 :
181 2 : MOZ_ASSERT(mWorkerPrivate);
182 2 : MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
183 :
184 2 : if (onWorkerThread) {
185 2 : mWorkerPrivate->AssertIsOnWorkerThread();
186 : }
187 : }
188 : }
189 : #endif
190 4 : nsCOMPtr<nsIRunnable> runnable(aWorkerRunnable);
191 :
192 2 : nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
193 2 : if (NS_WARN_IF(NS_FAILED(rv))) {
194 0 : return rv;
195 : }
196 :
197 : // We don't need to notify the worker's condition variable here because we're
198 : // being called from worker-controlled code and it will make sure to wake up
199 : // the worker thread if needed.
200 :
201 2 : return NS_OK;
202 : }
203 :
204 4686 : NS_IMPL_ISUPPORTS_INHERITED0(WorkerThread, nsThread)
205 :
206 : NS_IMETHODIMP
207 0 : WorkerThread::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
208 : {
209 0 : nsCOMPtr<nsIRunnable> runnable(aRunnable);
210 0 : return Dispatch(runnable.forget(), aFlags);
211 : }
212 :
213 : NS_IMETHODIMP
214 1 : WorkerThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
215 : {
216 : // May be called on any thread!
217 2 : nsCOMPtr<nsIRunnable> runnable(aRunnable); // in case we exit early
218 :
219 : // Workers only support asynchronous dispatch.
220 1 : if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
221 0 : return NS_ERROR_UNEXPECTED;
222 : }
223 :
224 1 : const bool onWorkerThread = PR_GetCurrentThread() == mThread;
225 :
226 : #ifdef DEBUG
227 1 : if (runnable && !onWorkerThread) {
228 2 : nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
229 :
230 : {
231 2 : MutexAutoLock lock(mLock);
232 :
233 : // Only enforce cancelable runnables after we've started the worker loop.
234 1 : if (!mAcceptingNonWorkerRunnables) {
235 0 : MOZ_ASSERT(cancelable,
236 : "Only nsICancelableRunnable may be dispatched to a worker!");
237 : }
238 : }
239 : }
240 : #endif
241 :
242 1 : WorkerPrivate* workerPrivate = nullptr;
243 1 : if (onWorkerThread) {
244 : // No need to lock here because it is only modified on this thread.
245 0 : MOZ_ASSERT(mWorkerPrivate);
246 0 : mWorkerPrivate->AssertIsOnWorkerThread();
247 :
248 0 : workerPrivate = mWorkerPrivate;
249 : } else {
250 2 : MutexAutoLock lock(mLock);
251 :
252 1 : MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget < UINT32_MAX);
253 :
254 1 : if (mWorkerPrivate) {
255 0 : workerPrivate = mWorkerPrivate;
256 :
257 : // Incrementing this counter will make the worker thread sleep if it
258 : // somehow tries to unset mWorkerPrivate while we're using it.
259 0 : mOtherThreadsDispatchingViaEventTarget++;
260 : }
261 : }
262 :
263 : nsresult rv;
264 1 : if (runnable && onWorkerThread) {
265 0 : RefPtr<WorkerRunnable> workerRunnable = workerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget());
266 0 : rv = nsThread::Dispatch(workerRunnable.forget(), NS_DISPATCH_NORMAL);
267 : } else {
268 1 : rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
269 : }
270 :
271 1 : if (!onWorkerThread && workerPrivate) {
272 : // We need to wake the worker thread if we're not already on the right
273 : // thread and the dispatch succeeded.
274 0 : if (NS_SUCCEEDED(rv)) {
275 0 : MutexAutoLock workerLock(workerPrivate->mMutex);
276 :
277 0 : workerPrivate->mCondVar.Notify();
278 : }
279 :
280 : // Now unset our waiting flag.
281 : {
282 0 : MutexAutoLock lock(mLock);
283 :
284 0 : MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget);
285 :
286 0 : if (!--mOtherThreadsDispatchingViaEventTarget) {
287 0 : mWorkerPrivateCondVar.Notify();
288 : }
289 : }
290 : }
291 :
292 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
293 0 : return rv;
294 : }
295 :
296 1 : return NS_OK;
297 : }
298 :
299 : NS_IMETHODIMP
300 0 : WorkerThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
301 : {
302 0 : return NS_ERROR_NOT_IMPLEMENTED;
303 : }
304 :
305 : uint32_t
306 0 : WorkerThread::RecursionDepth(const WorkerThreadFriendKey& /* aKey */) const
307 : {
308 0 : MOZ_ASSERT(PR_GetCurrentThread() == mThread);
309 :
310 0 : return mNestedEventLoopDepth;
311 : }
312 :
313 137 : NS_IMPL_ISUPPORTS(WorkerThread::Observer, nsIThreadObserver)
314 :
315 : NS_IMETHODIMP
316 0 : WorkerThread::Observer::OnDispatchedEvent(nsIThreadInternal* /* aThread */)
317 : {
318 0 : MOZ_CRASH("OnDispatchedEvent() should never be called!");
319 : }
320 :
321 : NS_IMETHODIMP
322 35 : WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
323 : bool aMayWait)
324 : {
325 35 : mWorkerPrivate->AssertIsOnWorkerThread();
326 :
327 : // If the PBackground child is not created yet, then we must permit
328 : // blocking event processing to support
329 : // BackgroundChild::SynchronouslyCreateForCurrentThread(). If this occurs
330 : // then we are spinning on the event queue at the start of
331 : // PrimaryWorkerRunnable::Run() and don't want to process the event in
332 : // mWorkerPrivate yet.
333 35 : if (aMayWait) {
334 0 : MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth() == 2);
335 0 : MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
336 0 : return NS_OK;
337 : }
338 :
339 35 : mWorkerPrivate->OnProcessNextEvent();
340 35 : return NS_OK;
341 : }
342 :
343 : NS_IMETHODIMP
344 31 : WorkerThread::Observer::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
345 : bool /* aEventWasProcessed */)
346 : {
347 31 : mWorkerPrivate->AssertIsOnWorkerThread();
348 :
349 31 : mWorkerPrivate->AfterProcessNextEvent();
350 31 : return NS_OK;
351 : }
352 :
353 : } // namespace workers
354 : } // namespace dom
355 : } // namespace mozilla
|