WorkerThread.cpp 8.8 KB

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