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