WorkerDebuggerManager.cpp 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  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 "WorkerDebuggerManager.h"
  6. #include "nsISimpleEnumerator.h"
  7. #include "mozilla/ClearOnShutdown.h"
  8. #include "WorkerPrivate.h"
  9. USING_WORKERS_NAMESPACE
  10. namespace {
  11. class RegisterDebuggerMainThreadRunnable final : public mozilla::Runnable
  12. {
  13. WorkerPrivate* mWorkerPrivate;
  14. bool mNotifyListeners;
  15. public:
  16. RegisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
  17. bool aNotifyListeners)
  18. : mWorkerPrivate(aWorkerPrivate),
  19. mNotifyListeners(aNotifyListeners)
  20. { }
  21. private:
  22. ~RegisterDebuggerMainThreadRunnable()
  23. { }
  24. NS_IMETHOD
  25. Run() override
  26. {
  27. WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
  28. MOZ_ASSERT(manager);
  29. manager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners);
  30. return NS_OK;
  31. }
  32. };
  33. class UnregisterDebuggerMainThreadRunnable final : public mozilla::Runnable
  34. {
  35. WorkerPrivate* mWorkerPrivate;
  36. public:
  37. explicit UnregisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
  38. : mWorkerPrivate(aWorkerPrivate)
  39. { }
  40. private:
  41. ~UnregisterDebuggerMainThreadRunnable()
  42. { }
  43. NS_IMETHOD
  44. Run() override
  45. {
  46. WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
  47. MOZ_ASSERT(manager);
  48. manager->UnregisterDebuggerMainThread(mWorkerPrivate);
  49. return NS_OK;
  50. }
  51. };
  52. // Does not hold an owning reference.
  53. static WorkerDebuggerManager* gWorkerDebuggerManager;
  54. } /* anonymous namespace */
  55. BEGIN_WORKERS_NAMESPACE
  56. class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
  57. {
  58. nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
  59. uint32_t mIndex;
  60. public:
  61. explicit WorkerDebuggerEnumerator(
  62. const nsTArray<RefPtr<WorkerDebugger>>& aDebuggers)
  63. : mDebuggers(aDebuggers), mIndex(0)
  64. {
  65. }
  66. NS_DECL_ISUPPORTS
  67. NS_DECL_NSISIMPLEENUMERATOR
  68. private:
  69. ~WorkerDebuggerEnumerator() {}
  70. };
  71. NS_IMPL_ISUPPORTS(WorkerDebuggerEnumerator, nsISimpleEnumerator);
  72. NS_IMETHODIMP
  73. WorkerDebuggerEnumerator::HasMoreElements(bool* aResult)
  74. {
  75. *aResult = mIndex < mDebuggers.Length();
  76. return NS_OK;
  77. };
  78. NS_IMETHODIMP
  79. WorkerDebuggerEnumerator::GetNext(nsISupports** aResult)
  80. {
  81. if (mIndex == mDebuggers.Length()) {
  82. return NS_ERROR_FAILURE;
  83. }
  84. mDebuggers.ElementAt(mIndex++).forget(aResult);
  85. return NS_OK;
  86. };
  87. WorkerDebuggerManager::WorkerDebuggerManager()
  88. : mMutex("WorkerDebuggerManager::mMutex")
  89. {
  90. AssertIsOnMainThread();
  91. }
  92. WorkerDebuggerManager::~WorkerDebuggerManager()
  93. {
  94. AssertIsOnMainThread();
  95. }
  96. // static
  97. already_AddRefed<WorkerDebuggerManager>
  98. WorkerDebuggerManager::GetInstance()
  99. {
  100. RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::GetOrCreate();
  101. return manager.forget();
  102. }
  103. // static
  104. WorkerDebuggerManager*
  105. WorkerDebuggerManager::GetOrCreate()
  106. {
  107. AssertIsOnMainThread();
  108. if (!gWorkerDebuggerManager) {
  109. // The observer service now owns us until shutdown.
  110. gWorkerDebuggerManager = new WorkerDebuggerManager();
  111. if (NS_FAILED(gWorkerDebuggerManager->Init())) {
  112. NS_WARNING("Failed to initialize worker debugger manager!");
  113. gWorkerDebuggerManager = nullptr;
  114. return nullptr;
  115. }
  116. }
  117. return gWorkerDebuggerManager;
  118. }
  119. WorkerDebuggerManager*
  120. WorkerDebuggerManager::Get()
  121. {
  122. MOZ_ASSERT(gWorkerDebuggerManager);
  123. return gWorkerDebuggerManager;
  124. }
  125. NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIObserver, nsIWorkerDebuggerManager);
  126. NS_IMETHODIMP
  127. WorkerDebuggerManager::Observe(nsISupports* aSubject, const char* aTopic,
  128. const char16_t* aData)
  129. {
  130. if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
  131. Shutdown();
  132. return NS_OK;
  133. }
  134. NS_NOTREACHED("Unknown observer topic!");
  135. return NS_OK;
  136. }
  137. NS_IMETHODIMP
  138. WorkerDebuggerManager::GetWorkerDebuggerEnumerator(
  139. nsISimpleEnumerator** aResult)
  140. {
  141. AssertIsOnMainThread();
  142. RefPtr<WorkerDebuggerEnumerator> enumerator =
  143. new WorkerDebuggerEnumerator(mDebuggers);
  144. enumerator.forget(aResult);
  145. return NS_OK;
  146. }
  147. NS_IMETHODIMP
  148. WorkerDebuggerManager::AddListener(nsIWorkerDebuggerManagerListener* aListener)
  149. {
  150. AssertIsOnMainThread();
  151. MutexAutoLock lock(mMutex);
  152. if (mListeners.Contains(aListener)) {
  153. return NS_ERROR_INVALID_ARG;
  154. }
  155. mListeners.AppendElement(aListener);
  156. return NS_OK;
  157. }
  158. NS_IMETHODIMP
  159. WorkerDebuggerManager::RemoveListener(
  160. nsIWorkerDebuggerManagerListener* aListener)
  161. {
  162. AssertIsOnMainThread();
  163. MutexAutoLock lock(mMutex);
  164. if (!mListeners.Contains(aListener)) {
  165. return NS_OK;
  166. }
  167. mListeners.RemoveElement(aListener);
  168. return NS_OK;
  169. }
  170. nsresult
  171. WorkerDebuggerManager::Init()
  172. {
  173. nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
  174. NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
  175. nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
  176. NS_ENSURE_SUCCESS(rv, rv);
  177. return NS_OK;
  178. }
  179. void
  180. WorkerDebuggerManager::Shutdown()
  181. {
  182. AssertIsOnMainThread();
  183. MutexAutoLock lock(mMutex);
  184. mListeners.Clear();
  185. }
  186. void
  187. WorkerDebuggerManager::RegisterDebugger(WorkerPrivate* aWorkerPrivate)
  188. {
  189. aWorkerPrivate->AssertIsOnParentThread();
  190. if (NS_IsMainThread()) {
  191. // When the parent thread is the main thread, it will always block until all
  192. // register liseners have been called, since it cannot continue until the
  193. // call to RegisterDebuggerMainThread returns.
  194. //
  195. // In this case, it is always safe to notify all listeners on the main
  196. // thread, even if there were no listeners at the time this method was
  197. // called, so we can always pass true for the value of aNotifyListeners.
  198. // This avoids having to lock mMutex to check whether mListeners is empty.
  199. RegisterDebuggerMainThread(aWorkerPrivate, true);
  200. } else {
  201. // We guarantee that if any register listeners are called, the worker does
  202. // not start running until all register listeners have been called. To
  203. // guarantee this, the parent thread should block until all register
  204. // listeners have been called.
  205. //
  206. // However, to avoid overhead when the debugger is not being used, the
  207. // parent thread will only block if there were any listeners at the time
  208. // this method was called. As a result, we should not notify any listeners
  209. // on the main thread if there were no listeners at the time this method was
  210. // called, because the parent will not be blocking in that case.
  211. bool hasListeners = false;
  212. {
  213. MutexAutoLock lock(mMutex);
  214. hasListeners = !mListeners.IsEmpty();
  215. }
  216. nsCOMPtr<nsIRunnable> runnable =
  217. new RegisterDebuggerMainThreadRunnable(aWorkerPrivate, hasListeners);
  218. MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
  219. if (hasListeners) {
  220. aWorkerPrivate->WaitForIsDebuggerRegistered(true);
  221. }
  222. }
  223. }
  224. void
  225. WorkerDebuggerManager::UnregisterDebugger(WorkerPrivate* aWorkerPrivate)
  226. {
  227. aWorkerPrivate->AssertIsOnParentThread();
  228. if (NS_IsMainThread()) {
  229. UnregisterDebuggerMainThread(aWorkerPrivate);
  230. } else {
  231. nsCOMPtr<nsIRunnable> runnable =
  232. new UnregisterDebuggerMainThreadRunnable(aWorkerPrivate);
  233. MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
  234. aWorkerPrivate->WaitForIsDebuggerRegistered(false);
  235. }
  236. }
  237. void
  238. WorkerDebuggerManager::RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
  239. bool aNotifyListeners)
  240. {
  241. AssertIsOnMainThread();
  242. RefPtr<WorkerDebugger> debugger = new WorkerDebugger(aWorkerPrivate);
  243. mDebuggers.AppendElement(debugger);
  244. aWorkerPrivate->SetDebugger(debugger);
  245. if (aNotifyListeners) {
  246. nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
  247. {
  248. MutexAutoLock lock(mMutex);
  249. listeners = mListeners;
  250. }
  251. for (size_t index = 0; index < listeners.Length(); ++index) {
  252. listeners[index]->OnRegister(debugger);
  253. }
  254. }
  255. aWorkerPrivate->SetIsDebuggerRegistered(true);
  256. }
  257. void
  258. WorkerDebuggerManager::UnregisterDebuggerMainThread(
  259. WorkerPrivate* aWorkerPrivate)
  260. {
  261. AssertIsOnMainThread();
  262. // There is nothing to do here if the debugger was never succesfully
  263. // registered. We need to check this on the main thread because the worker
  264. // does not wait for the registration to complete if there were no listeners
  265. // installed when it started.
  266. if (!aWorkerPrivate->IsDebuggerRegistered()) {
  267. return;
  268. }
  269. RefPtr<WorkerDebugger> debugger = aWorkerPrivate->Debugger();
  270. mDebuggers.RemoveElement(debugger);
  271. aWorkerPrivate->SetDebugger(nullptr);
  272. nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
  273. {
  274. MutexAutoLock lock(mMutex);
  275. listeners = mListeners;
  276. }
  277. for (size_t index = 0; index < listeners.Length(); ++index) {
  278. listeners[index]->OnUnregister(debugger);
  279. }
  280. debugger->Close();
  281. aWorkerPrivate->SetIsDebuggerRegistered(false);
  282. }
  283. END_WORKERS_NAMESPACE