1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/workers/WorkerRunnable.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,525 @@ 1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "WorkerRunnable.h" 1.10 + 1.11 +#include "nsIEventTarget.h" 1.12 +#include "nsIRunnable.h" 1.13 +#include "nsThreadUtils.h" 1.14 + 1.15 +#include "mozilla/DebugOnly.h" 1.16 + 1.17 +#include "js/RootingAPI.h" 1.18 +#include "js/Value.h" 1.19 + 1.20 +#include "WorkerPrivate.h" 1.21 + 1.22 +USING_WORKERS_NAMESPACE 1.23 + 1.24 +namespace { 1.25 + 1.26 +const nsIID kWorkerRunnableIID = { 1.27 + 0x320cc0b5, 0xef12, 0x4084, { 0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68 } 1.28 +}; 1.29 + 1.30 +void 1.31 +MaybeReportMainThreadException(JSContext* aCx, bool aResult) 1.32 +{ 1.33 + AssertIsOnMainThread(); 1.34 + 1.35 + if (aCx && !aResult) { 1.36 + JS_ReportPendingException(aCx); 1.37 + } 1.38 +} 1.39 + 1.40 +} // anonymous namespace 1.41 + 1.42 +#ifdef DEBUG 1.43 +WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate, 1.44 + TargetAndBusyBehavior aBehavior) 1.45 +: mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0), 1.46 + mCallingCancelWithinRun(false) 1.47 +{ 1.48 + MOZ_ASSERT(aWorkerPrivate); 1.49 +} 1.50 +#endif 1.51 + 1.52 +bool 1.53 +WorkerRunnable::PreDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate) 1.54 +{ 1.55 +#ifdef DEBUG 1.56 + MOZ_ASSERT(aWorkerPrivate); 1.57 + 1.58 + switch (mBehavior) { 1.59 + case ParentThreadUnchangedBusyCount: 1.60 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.61 + break; 1.62 + 1.63 + case WorkerThreadModifyBusyCount: 1.64 + aWorkerPrivate->AssertIsOnParentThread(); 1.65 + MOZ_ASSERT(aCx); 1.66 + break; 1.67 + 1.68 + case WorkerThreadUnchangedBusyCount: 1.69 + aWorkerPrivate->AssertIsOnParentThread(); 1.70 + break; 1.71 + 1.72 + default: 1.73 + MOZ_ASSUME_UNREACHABLE("Unknown behavior!"); 1.74 + } 1.75 +#endif 1.76 + 1.77 + if (mBehavior == WorkerThreadModifyBusyCount) { 1.78 + return aWorkerPrivate->ModifyBusyCount(aCx, true); 1.79 + } 1.80 + 1.81 + return true; 1.82 +} 1.83 + 1.84 +bool 1.85 +WorkerRunnable::Dispatch(JSContext* aCx) 1.86 +{ 1.87 + bool ok; 1.88 + 1.89 + if (!aCx) { 1.90 + ok = PreDispatch(nullptr, mWorkerPrivate); 1.91 + if (ok) { 1.92 + ok = DispatchInternal(); 1.93 + } 1.94 + PostDispatch(nullptr, mWorkerPrivate, ok); 1.95 + return ok; 1.96 + } 1.97 + 1.98 + JSAutoRequest ar(aCx); 1.99 + 1.100 + JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx)); 1.101 + 1.102 + Maybe<JSAutoCompartment> ac; 1.103 + if (global) { 1.104 + ac.construct(aCx, global); 1.105 + } 1.106 + 1.107 + ok = PreDispatch(aCx, mWorkerPrivate); 1.108 + 1.109 + if (ok && !DispatchInternal()) { 1.110 + ok = false; 1.111 + } 1.112 + 1.113 + PostDispatch(aCx, mWorkerPrivate, ok); 1.114 + 1.115 + return ok; 1.116 +} 1.117 + 1.118 +bool 1.119 +WorkerRunnable::DispatchInternal() 1.120 +{ 1.121 + if (mBehavior == WorkerThreadModifyBusyCount || 1.122 + mBehavior == WorkerThreadUnchangedBusyCount) { 1.123 + return NS_SUCCEEDED(mWorkerPrivate->Dispatch(this)); 1.124 + } 1.125 + 1.126 + MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount); 1.127 + 1.128 + if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) { 1.129 + return NS_SUCCEEDED(parent->Dispatch(this)); 1.130 + } 1.131 + 1.132 + nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); 1.133 + MOZ_ASSERT(mainThread); 1.134 + 1.135 + return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL)); 1.136 +} 1.137 + 1.138 +void 1.139 +WorkerRunnable::PostDispatch(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.140 + bool aDispatchResult) 1.141 +{ 1.142 + MOZ_ASSERT(aWorkerPrivate); 1.143 + 1.144 +#ifdef DEBUG 1.145 + switch (mBehavior) { 1.146 + case ParentThreadUnchangedBusyCount: 1.147 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.148 + break; 1.149 + 1.150 + case WorkerThreadModifyBusyCount: 1.151 + aWorkerPrivate->AssertIsOnParentThread(); 1.152 + MOZ_ASSERT(aCx); 1.153 + break; 1.154 + 1.155 + case WorkerThreadUnchangedBusyCount: 1.156 + aWorkerPrivate->AssertIsOnParentThread(); 1.157 + break; 1.158 + 1.159 + default: 1.160 + MOZ_ASSUME_UNREACHABLE("Unknown behavior!"); 1.161 + } 1.162 +#endif 1.163 + 1.164 + if (!aDispatchResult) { 1.165 + if (mBehavior == WorkerThreadModifyBusyCount) { 1.166 + aWorkerPrivate->ModifyBusyCount(aCx, false); 1.167 + } 1.168 + if (aCx) { 1.169 + JS_ReportPendingException(aCx); 1.170 + } 1.171 + } 1.172 +} 1.173 + 1.174 +void 1.175 +WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.176 + bool aRunResult) 1.177 +{ 1.178 + MOZ_ASSERT(aCx); 1.179 + MOZ_ASSERT(aWorkerPrivate); 1.180 + 1.181 +#ifdef DEBUG 1.182 + switch (mBehavior) { 1.183 + case ParentThreadUnchangedBusyCount: 1.184 + aWorkerPrivate->AssertIsOnParentThread(); 1.185 + break; 1.186 + 1.187 + case WorkerThreadModifyBusyCount: 1.188 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.189 + break; 1.190 + 1.191 + case WorkerThreadUnchangedBusyCount: 1.192 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.193 + break; 1.194 + 1.195 + default: 1.196 + MOZ_ASSUME_UNREACHABLE("Unknown behavior!"); 1.197 + } 1.198 +#endif 1.199 + 1.200 + if (mBehavior == WorkerThreadModifyBusyCount) { 1.201 + if (!aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false)) { 1.202 + aRunResult = false; 1.203 + } 1.204 + } 1.205 + 1.206 + if (!aRunResult) { 1.207 + JS_ReportPendingException(aCx); 1.208 + } 1.209 +} 1.210 + 1.211 +// static 1.212 +WorkerRunnable* 1.213 +WorkerRunnable::FromRunnable(nsIRunnable* aRunnable) 1.214 +{ 1.215 + MOZ_ASSERT(aRunnable); 1.216 + 1.217 + WorkerRunnable* runnable; 1.218 + nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID, 1.219 + reinterpret_cast<void**>(&runnable)); 1.220 + if (NS_FAILED(rv)) { 1.221 + return nullptr; 1.222 + } 1.223 + 1.224 + MOZ_ASSERT(runnable); 1.225 + return runnable; 1.226 +} 1.227 + 1.228 +NS_IMPL_ADDREF(WorkerRunnable) 1.229 +NS_IMPL_RELEASE(WorkerRunnable) 1.230 + 1.231 +NS_INTERFACE_MAP_BEGIN(WorkerRunnable) 1.232 + NS_INTERFACE_MAP_ENTRY(nsIRunnable) 1.233 + NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable) 1.234 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.235 + // kWorkerRunnableIID is special in that it does not AddRef its result. 1.236 + if (aIID.Equals(kWorkerRunnableIID)) { 1.237 + *aInstancePtr = this; 1.238 + return NS_OK; 1.239 + } 1.240 + else 1.241 +NS_INTERFACE_MAP_END 1.242 + 1.243 +NS_IMETHODIMP 1.244 +WorkerRunnable::Run() 1.245 +{ 1.246 + bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount || 1.247 + mBehavior == WorkerThreadUnchangedBusyCount; 1.248 + 1.249 +#ifdef DEBUG 1.250 + MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread); 1.251 + if (targetIsWorkerThread) { 1.252 + mWorkerPrivate->AssertIsOnWorkerThread(); 1.253 + } 1.254 + else { 1.255 + MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount); 1.256 + mWorkerPrivate->AssertIsOnParentThread(); 1.257 + } 1.258 +#endif 1.259 + 1.260 + if (IsCanceled() && !mCallingCancelWithinRun) { 1.261 + return NS_OK; 1.262 + } 1.263 + 1.264 + JSContext* cx; 1.265 + nsRefPtr<WorkerPrivate> kungFuDeathGrip; 1.266 + nsCxPusher pusher; 1.267 + 1.268 + if (targetIsWorkerThread) { 1.269 + if (mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() && 1.270 + !IsCanceled() && 1.271 + !mCallingCancelWithinRun) { 1.272 + 1.273 + // Prevent recursion. 1.274 + mCallingCancelWithinRun = true; 1.275 + 1.276 + Cancel(); 1.277 + 1.278 + MOZ_ASSERT(mCallingCancelWithinRun); 1.279 + mCallingCancelWithinRun = false; 1.280 + 1.281 + MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!"); 1.282 + 1.283 + return NS_OK; 1.284 + } 1.285 + 1.286 + cx = mWorkerPrivate->GetJSContext(); 1.287 + MOZ_ASSERT(cx); 1.288 + } 1.289 + else { 1.290 + cx = mWorkerPrivate->ParentJSContext(); 1.291 + MOZ_ASSERT(cx); 1.292 + 1.293 + kungFuDeathGrip = mWorkerPrivate; 1.294 + 1.295 + if (!mWorkerPrivate->GetParent()) { 1.296 + AssertIsOnMainThread(); 1.297 + pusher.Push(cx); 1.298 + } 1.299 + } 1.300 + 1.301 + JSAutoRequest ar(cx); 1.302 + 1.303 + JS::Rooted<JSObject*> targetCompartmentObject(cx); 1.304 + if (targetIsWorkerThread) { 1.305 + targetCompartmentObject = JS::CurrentGlobalOrNull(cx); 1.306 + } else { 1.307 + targetCompartmentObject = mWorkerPrivate->GetWrapper(); 1.308 + } 1.309 + 1.310 + Maybe<JSAutoCompartment> ac; 1.311 + if (targetCompartmentObject) { 1.312 + ac.construct(cx, targetCompartmentObject); 1.313 + } 1.314 + 1.315 + bool result = WorkerRun(cx, mWorkerPrivate); 1.316 + 1.317 + // In the case of CompileScriptRunnnable, WorkerRun above can cause us to 1.318 + // lazily create a global, in which case we need to be in its compartment 1.319 + // when calling PostRun() below. Maybe<> this time... 1.320 + if (targetIsWorkerThread && 1.321 + ac.empty() && 1.322 + js::DefaultObjectForContextOrNull(cx)) { 1.323 + ac.construct(cx, js::DefaultObjectForContextOrNull(cx)); 1.324 + } 1.325 + 1.326 + PostRun(cx, mWorkerPrivate, result); 1.327 + 1.328 + return result ? NS_OK : NS_ERROR_FAILURE; 1.329 +} 1.330 + 1.331 +NS_IMETHODIMP 1.332 +WorkerRunnable::Cancel() 1.333 +{ 1.334 + uint32_t canceledCount = ++mCanceled; 1.335 + 1.336 + MOZ_ASSERT(canceledCount, "Cancel() overflow!"); 1.337 + 1.338 + // The docs say that Cancel() should not be called more than once and that we 1.339 + // should throw NS_ERROR_UNEXPECTED if it is. 1.340 + return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED; 1.341 +} 1.342 + 1.343 +WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate, 1.344 + nsIEventTarget* aSyncLoopTarget) 1.345 +: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.346 + mSyncLoopTarget(aSyncLoopTarget) 1.347 +{ 1.348 +#ifdef DEBUG 1.349 + if (mSyncLoopTarget) { 1.350 + mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget); 1.351 + } 1.352 +#endif 1.353 +} 1.354 + 1.355 +WorkerSyncRunnable::WorkerSyncRunnable( 1.356 + WorkerPrivate* aWorkerPrivate, 1.357 + already_AddRefed<nsIEventTarget>&& aSyncLoopTarget) 1.358 +: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount), 1.359 + mSyncLoopTarget(aSyncLoopTarget) 1.360 +{ 1.361 +#ifdef DEBUG 1.362 + if (mSyncLoopTarget) { 1.363 + mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget); 1.364 + } 1.365 +#endif 1.366 +} 1.367 + 1.368 +WorkerSyncRunnable::~WorkerSyncRunnable() 1.369 +{ 1.370 +} 1.371 + 1.372 +bool 1.373 +WorkerSyncRunnable::DispatchInternal() 1.374 +{ 1.375 + if (mSyncLoopTarget) { 1.376 + return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL)); 1.377 + } 1.378 + 1.379 + return WorkerRunnable::DispatchInternal(); 1.380 +} 1.381 + 1.382 +void 1.383 +MainThreadWorkerSyncRunnable::PostDispatch(JSContext* aCx, 1.384 + WorkerPrivate* aWorkerPrivate, 1.385 + bool aDispatchResult) 1.386 +{ 1.387 + MaybeReportMainThreadException(aCx, aDispatchResult); 1.388 +} 1.389 + 1.390 +StopSyncLoopRunnable::StopSyncLoopRunnable( 1.391 + WorkerPrivate* aWorkerPrivate, 1.392 + already_AddRefed<nsIEventTarget>&& aSyncLoopTarget, 1.393 + bool aResult) 1.394 +: WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)), mResult(aResult) 1.395 +{ 1.396 +#ifdef DEBUG 1.397 + mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget); 1.398 +#endif 1.399 +} 1.400 + 1.401 +NS_IMETHODIMP 1.402 +StopSyncLoopRunnable::Cancel() 1.403 +{ 1.404 + nsresult rv = Run(); 1.405 + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Run() failed"); 1.406 + 1.407 + nsresult rv2 = WorkerSyncRunnable::Cancel(); 1.408 + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv2), "Cancel() failed"); 1.409 + 1.410 + return NS_FAILED(rv) ? rv : rv2; 1.411 +} 1.412 + 1.413 +bool 1.414 +StopSyncLoopRunnable::WorkerRun(JSContext* aCx, 1.415 + WorkerPrivate* aWorkerPrivate) 1.416 +{ 1.417 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.418 + MOZ_ASSERT(mSyncLoopTarget); 1.419 + 1.420 + nsCOMPtr<nsIEventTarget> syncLoopTarget; 1.421 + mSyncLoopTarget.swap(syncLoopTarget); 1.422 + 1.423 + if (!mResult) { 1.424 + MaybeSetException(aCx); 1.425 + } 1.426 + 1.427 + aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult); 1.428 + return true; 1.429 +} 1.430 + 1.431 +bool 1.432 +StopSyncLoopRunnable::DispatchInternal() 1.433 +{ 1.434 + MOZ_ASSERT(mSyncLoopTarget); 1.435 + 1.436 + return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(this, NS_DISPATCH_NORMAL)); 1.437 +} 1.438 + 1.439 +void 1.440 +MainThreadStopSyncLoopRunnable::PostDispatch(JSContext* aCx, 1.441 + WorkerPrivate* aWorkerPrivate, 1.442 + bool aDispatchResult) 1.443 +{ 1.444 + MaybeReportMainThreadException(aCx, aDispatchResult); 1.445 +} 1.446 + 1.447 +#ifdef DEBUG 1.448 +WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate, 1.449 + TargetAndBusyBehavior aBehavior) 1.450 +: WorkerRunnable(aWorkerPrivate, aBehavior) 1.451 +{ 1.452 + MOZ_ASSERT(aWorkerPrivate); 1.453 + MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount || 1.454 + aBehavior == WorkerThreadUnchangedBusyCount, 1.455 + "WorkerControlRunnables should not modify the busy count"); 1.456 +} 1.457 +#endif 1.458 + 1.459 +bool 1.460 +WorkerControlRunnable::DispatchInternal() 1.461 +{ 1.462 + if (mBehavior == WorkerThreadUnchangedBusyCount) { 1.463 + return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(this)); 1.464 + } 1.465 + 1.466 + if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) { 1.467 + return NS_SUCCEEDED(parent->DispatchControlRunnable(this)); 1.468 + } 1.469 + 1.470 + nsCOMPtr<nsIThread> mainThread = do_GetMainThread(); 1.471 + MOZ_ASSERT(mainThread); 1.472 + 1.473 + return NS_SUCCEEDED(mainThread->Dispatch(this, NS_DISPATCH_NORMAL)); 1.474 +} 1.475 + 1.476 +void 1.477 +MainThreadWorkerControlRunnable::PostDispatch(JSContext* aCx, 1.478 + WorkerPrivate* aWorkerPrivate, 1.479 + bool aDispatchResult) 1.480 +{ 1.481 + AssertIsOnMainThread(); 1.482 + 1.483 + if (aCx && !aDispatchResult) { 1.484 + JS_ReportPendingException(aCx); 1.485 + } 1.486 +} 1.487 + 1.488 +NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable) 1.489 + 1.490 +bool 1.491 +WorkerSameThreadRunnable::PreDispatch(JSContext* aCx, 1.492 + WorkerPrivate* aWorkerPrivate) 1.493 +{ 1.494 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.495 + return true; 1.496 +} 1.497 + 1.498 +void 1.499 +WorkerSameThreadRunnable::PostDispatch(JSContext* aCx, 1.500 + WorkerPrivate* aWorkerPrivate, 1.501 + bool aDispatchResult) 1.502 +{ 1.503 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.504 + if (aDispatchResult) { 1.505 + DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(aCx, true); 1.506 + // Should never fail since if this thread is still running, so should the 1.507 + // parent and it should be able to process a control runnable. 1.508 + MOZ_ASSERT(willIncrement); 1.509 + } 1.510 +} 1.511 + 1.512 +void 1.513 +WorkerSameThreadRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1.514 + bool aRunResult) 1.515 +{ 1.516 + MOZ_ASSERT(aCx); 1.517 + MOZ_ASSERT(aWorkerPrivate); 1.518 + 1.519 + aWorkerPrivate->AssertIsOnWorkerThread(); 1.520 + 1.521 + DebugOnly<bool> willDecrement = aWorkerPrivate->ModifyBusyCountFromWorker(aCx, false); 1.522 + MOZ_ASSERT(willDecrement); 1.523 + 1.524 + if (!aRunResult) { 1.525 + JS_ReportPendingException(aCx); 1.526 + } 1.527 +} 1.528 +