123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "WorkerDebuggerManager.h"
- #include "nsISimpleEnumerator.h"
- #include "mozilla/ClearOnShutdown.h"
- #include "WorkerPrivate.h"
- USING_WORKERS_NAMESPACE
- namespace {
- class RegisterDebuggerMainThreadRunnable final : public mozilla::Runnable
- {
- WorkerPrivate* mWorkerPrivate;
- bool mNotifyListeners;
- public:
- RegisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
- bool aNotifyListeners)
- : mWorkerPrivate(aWorkerPrivate),
- mNotifyListeners(aNotifyListeners)
- { }
- private:
- ~RegisterDebuggerMainThreadRunnable()
- { }
- NS_IMETHOD
- Run() override
- {
- WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
- MOZ_ASSERT(manager);
- manager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners);
- return NS_OK;
- }
- };
- class UnregisterDebuggerMainThreadRunnable final : public mozilla::Runnable
- {
- WorkerPrivate* mWorkerPrivate;
- public:
- explicit UnregisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
- : mWorkerPrivate(aWorkerPrivate)
- { }
- private:
- ~UnregisterDebuggerMainThreadRunnable()
- { }
- NS_IMETHOD
- Run() override
- {
- WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
- MOZ_ASSERT(manager);
- manager->UnregisterDebuggerMainThread(mWorkerPrivate);
- return NS_OK;
- }
- };
- // Does not hold an owning reference.
- static WorkerDebuggerManager* gWorkerDebuggerManager;
- } /* anonymous namespace */
- BEGIN_WORKERS_NAMESPACE
- class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
- {
- nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
- uint32_t mIndex;
- public:
- explicit WorkerDebuggerEnumerator(
- const nsTArray<RefPtr<WorkerDebugger>>& aDebuggers)
- : mDebuggers(aDebuggers), mIndex(0)
- {
- }
- NS_DECL_ISUPPORTS
- NS_DECL_NSISIMPLEENUMERATOR
- private:
- ~WorkerDebuggerEnumerator() {}
- };
- NS_IMPL_ISUPPORTS(WorkerDebuggerEnumerator, nsISimpleEnumerator);
- NS_IMETHODIMP
- WorkerDebuggerEnumerator::HasMoreElements(bool* aResult)
- {
- *aResult = mIndex < mDebuggers.Length();
- return NS_OK;
- };
- NS_IMETHODIMP
- WorkerDebuggerEnumerator::GetNext(nsISupports** aResult)
- {
- if (mIndex == mDebuggers.Length()) {
- return NS_ERROR_FAILURE;
- }
- mDebuggers.ElementAt(mIndex++).forget(aResult);
- return NS_OK;
- };
- WorkerDebuggerManager::WorkerDebuggerManager()
- : mMutex("WorkerDebuggerManager::mMutex")
- {
- AssertIsOnMainThread();
- }
- WorkerDebuggerManager::~WorkerDebuggerManager()
- {
- AssertIsOnMainThread();
- }
- // static
- already_AddRefed<WorkerDebuggerManager>
- WorkerDebuggerManager::GetInstance()
- {
- RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::GetOrCreate();
- return manager.forget();
- }
- // static
- WorkerDebuggerManager*
- WorkerDebuggerManager::GetOrCreate()
- {
- AssertIsOnMainThread();
- if (!gWorkerDebuggerManager) {
- // The observer service now owns us until shutdown.
- gWorkerDebuggerManager = new WorkerDebuggerManager();
- if (NS_FAILED(gWorkerDebuggerManager->Init())) {
- NS_WARNING("Failed to initialize worker debugger manager!");
- gWorkerDebuggerManager = nullptr;
- return nullptr;
- }
- }
- return gWorkerDebuggerManager;
- }
- WorkerDebuggerManager*
- WorkerDebuggerManager::Get()
- {
- MOZ_ASSERT(gWorkerDebuggerManager);
- return gWorkerDebuggerManager;
- }
- NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIObserver, nsIWorkerDebuggerManager);
- NS_IMETHODIMP
- WorkerDebuggerManager::Observe(nsISupports* aSubject, const char* aTopic,
- const char16_t* aData)
- {
- if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
- Shutdown();
- return NS_OK;
- }
- NS_NOTREACHED("Unknown observer topic!");
- return NS_OK;
- }
- NS_IMETHODIMP
- WorkerDebuggerManager::GetWorkerDebuggerEnumerator(
- nsISimpleEnumerator** aResult)
- {
- AssertIsOnMainThread();
- RefPtr<WorkerDebuggerEnumerator> enumerator =
- new WorkerDebuggerEnumerator(mDebuggers);
- enumerator.forget(aResult);
- return NS_OK;
- }
- NS_IMETHODIMP
- WorkerDebuggerManager::AddListener(nsIWorkerDebuggerManagerListener* aListener)
- {
- AssertIsOnMainThread();
- MutexAutoLock lock(mMutex);
- if (mListeners.Contains(aListener)) {
- return NS_ERROR_INVALID_ARG;
- }
- mListeners.AppendElement(aListener);
- return NS_OK;
- }
- NS_IMETHODIMP
- WorkerDebuggerManager::RemoveListener(
- nsIWorkerDebuggerManagerListener* aListener)
- {
- AssertIsOnMainThread();
- MutexAutoLock lock(mMutex);
- if (!mListeners.Contains(aListener)) {
- return NS_OK;
- }
- mListeners.RemoveElement(aListener);
- return NS_OK;
- }
- nsresult
- WorkerDebuggerManager::Init()
- {
- nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
- nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- void
- WorkerDebuggerManager::Shutdown()
- {
- AssertIsOnMainThread();
- MutexAutoLock lock(mMutex);
- mListeners.Clear();
- }
- void
- WorkerDebuggerManager::RegisterDebugger(WorkerPrivate* aWorkerPrivate)
- {
- aWorkerPrivate->AssertIsOnParentThread();
- if (NS_IsMainThread()) {
- // When the parent thread is the main thread, it will always block until all
- // register liseners have been called, since it cannot continue until the
- // call to RegisterDebuggerMainThread returns.
- //
- // In this case, it is always safe to notify all listeners on the main
- // thread, even if there were no listeners at the time this method was
- // called, so we can always pass true for the value of aNotifyListeners.
- // This avoids having to lock mMutex to check whether mListeners is empty.
- RegisterDebuggerMainThread(aWorkerPrivate, true);
- } else {
- // We guarantee that if any register listeners are called, the worker does
- // not start running until all register listeners have been called. To
- // guarantee this, the parent thread should block until all register
- // listeners have been called.
- //
- // However, to avoid overhead when the debugger is not being used, the
- // parent thread will only block if there were any listeners at the time
- // this method was called. As a result, we should not notify any listeners
- // on the main thread if there were no listeners at the time this method was
- // called, because the parent will not be blocking in that case.
- bool hasListeners = false;
- {
- MutexAutoLock lock(mMutex);
- hasListeners = !mListeners.IsEmpty();
- }
- nsCOMPtr<nsIRunnable> runnable =
- new RegisterDebuggerMainThreadRunnable(aWorkerPrivate, hasListeners);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
- if (hasListeners) {
- aWorkerPrivate->WaitForIsDebuggerRegistered(true);
- }
- }
- }
- void
- WorkerDebuggerManager::UnregisterDebugger(WorkerPrivate* aWorkerPrivate)
- {
- aWorkerPrivate->AssertIsOnParentThread();
- if (NS_IsMainThread()) {
- UnregisterDebuggerMainThread(aWorkerPrivate);
- } else {
- nsCOMPtr<nsIRunnable> runnable =
- new UnregisterDebuggerMainThreadRunnable(aWorkerPrivate);
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
- aWorkerPrivate->WaitForIsDebuggerRegistered(false);
- }
- }
- void
- WorkerDebuggerManager::RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
- bool aNotifyListeners)
- {
- AssertIsOnMainThread();
- RefPtr<WorkerDebugger> debugger = new WorkerDebugger(aWorkerPrivate);
- mDebuggers.AppendElement(debugger);
- aWorkerPrivate->SetDebugger(debugger);
- if (aNotifyListeners) {
- nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
- {
- MutexAutoLock lock(mMutex);
- listeners = mListeners;
- }
- for (size_t index = 0; index < listeners.Length(); ++index) {
- listeners[index]->OnRegister(debugger);
- }
- }
- aWorkerPrivate->SetIsDebuggerRegistered(true);
- }
- void
- WorkerDebuggerManager::UnregisterDebuggerMainThread(
- WorkerPrivate* aWorkerPrivate)
- {
- AssertIsOnMainThread();
- // There is nothing to do here if the debugger was never succesfully
- // registered. We need to check this on the main thread because the worker
- // does not wait for the registration to complete if there were no listeners
- // installed when it started.
- if (!aWorkerPrivate->IsDebuggerRegistered()) {
- return;
- }
- RefPtr<WorkerDebugger> debugger = aWorkerPrivate->Debugger();
- mDebuggers.RemoveElement(debugger);
- aWorkerPrivate->SetDebugger(nullptr);
- nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
- {
- MutexAutoLock lock(mMutex);
- listeners = mListeners;
- }
- for (size_t index = 0; index < listeners.Length(); ++index) {
- listeners[index]->OnUnregister(debugger);
- }
- debugger->Close();
- aWorkerPrivate->SetIsDebuggerRegistered(false);
- }
- END_WORKERS_NAMESPACE
|