diff options
Diffstat (limited to '')
-rw-r--r-- | dom/network/ConnectionWorker.cpp | 208 |
1 files changed, 208 insertions, 0 deletions
diff --git a/dom/network/ConnectionWorker.cpp b/dom/network/ConnectionWorker.cpp new file mode 100644 index 0000000000..48e53c8f7f --- /dev/null +++ b/dom/network/ConnectionWorker.cpp @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 <limits> +#include "mozilla/Hal.h" +#include "ConnectionWorker.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/WorkerRef.h" +#include "mozilla/dom/WorkerRunnable.h" + +namespace mozilla::dom::network { + +class ConnectionProxy final : public hal::NetworkObserver { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ConnectionProxy) + + static already_AddRefed<ConnectionProxy> Create( + WorkerPrivate* aWorkerPrivate, ConnectionWorker* aConnection) { + RefPtr<ConnectionProxy> proxy = new ConnectionProxy(aConnection); + + RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( + aWorkerPrivate, "ConnectionProxy", [proxy]() { proxy->Shutdown(); }); + if (NS_WARN_IF(!workerRef)) { + return nullptr; + } + + proxy->mWorkerRef = new ThreadSafeWorkerRef(workerRef); + return proxy.forget(); + } + + ThreadSafeWorkerRef* WorkerRef() const { return mWorkerRef; } + + // For IObserver - main-thread only. + void Notify(const hal::NetworkInformation& aNetworkInfo) override; + + void Shutdown(); + + void Update(ConnectionType aType, bool aIsWifi, uint32_t aDHCPGateway) { + MOZ_ASSERT(mConnection); + MOZ_ASSERT(IsCurrentThreadRunningWorker()); + mConnection->Update(aType, aIsWifi, aDHCPGateway, true); + } + + private: + explicit ConnectionProxy(ConnectionWorker* aConnection) + : mConnection(aConnection) {} + + ~ConnectionProxy() = default; + + // Raw pointer because the ConnectionWorker keeps alive the proxy. + // This is touched only on the worker-thread and it's nullified when the + // shutdown procedure starts. + ConnectionWorker* mConnection; + + RefPtr<ThreadSafeWorkerRef> mWorkerRef; +}; + +namespace { + +// This class initializes the hal observer on the main-thread. +class InitializeRunnable : public WorkerMainThreadRunnable { + private: + // raw pointer because this is a sync runnable. + ConnectionProxy* mProxy; + hal::NetworkInformation& mNetworkInfo; + + public: + InitializeRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy, + hal::NetworkInformation& aNetworkInfo) + : WorkerMainThreadRunnable(aWorkerPrivate, + "ConnectionWorker :: Initialize"_ns), + mProxy(aProxy), + mNetworkInfo(aNetworkInfo) { + MOZ_ASSERT(aProxy); + aWorkerPrivate->AssertIsOnWorkerThread(); + } + + bool MainThreadRun() override { + MOZ_ASSERT(NS_IsMainThread()); + hal::RegisterNetworkObserver(mProxy); + hal::GetCurrentNetworkInformation(&mNetworkInfo); + return true; + } +}; + +// This class turns down the hal observer on the main-thread. +class ShutdownRunnable : public WorkerMainThreadRunnable { + private: + // raw pointer because this is a sync runnable. + ConnectionProxy* mProxy; + + public: + ShutdownRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy) + : WorkerMainThreadRunnable(aWorkerPrivate, + "ConnectionWorker :: Shutdown"_ns), + mProxy(aProxy) { + MOZ_ASSERT(aProxy); + aWorkerPrivate->AssertIsOnWorkerThread(); + } + + bool MainThreadRun() override { + MOZ_ASSERT(NS_IsMainThread()); + hal::UnregisterNetworkObserver(mProxy); + return true; + } +}; + +class NotifyRunnable : public WorkerRunnable { + private: + RefPtr<ConnectionProxy> mProxy; + + const ConnectionType mConnectionType; + const bool mIsWifi; + const uint32_t mDHCPGateway; + + public: + NotifyRunnable(WorkerPrivate* aWorkerPrivate, ConnectionProxy* aProxy, + ConnectionType aType, bool aIsWifi, uint32_t aDHCPGateway) + : WorkerRunnable(aWorkerPrivate), + mProxy(aProxy), + mConnectionType(aType), + mIsWifi(aIsWifi), + mDHCPGateway(aDHCPGateway) { + MOZ_ASSERT(aProxy); + MOZ_ASSERT(NS_IsMainThread()); + } + + bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { + aWorkerPrivate->AssertIsOnWorkerThread(); + mProxy->Update(mConnectionType, mIsWifi, mDHCPGateway); + return true; + } +}; + +} // anonymous namespace + +/* static */ +already_AddRefed<ConnectionWorker> ConnectionWorker::Create( + WorkerPrivate* aWorkerPrivate, ErrorResult& aRv) { + RefPtr<ConnectionWorker> c = new ConnectionWorker(); + c->mProxy = ConnectionProxy::Create(aWorkerPrivate, c); + if (!c->mProxy) { + aRv.ThrowTypeError("The Worker thread is shutting down."); + return nullptr; + } + + hal::NetworkInformation networkInfo; + RefPtr<InitializeRunnable> runnable = + new InitializeRunnable(aWorkerPrivate, c->mProxy, networkInfo); + + runnable->Dispatch(Canceling, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + c->Update(static_cast<ConnectionType>(networkInfo.type()), + networkInfo.isWifi(), networkInfo.dhcpGateway(), false); + return c.forget(); +} + +ConnectionWorker::ConnectionWorker() : Connection(nullptr) { + MOZ_ASSERT(IsCurrentThreadRunningWorker()); +} + +ConnectionWorker::~ConnectionWorker() { Shutdown(); } + +void ConnectionWorker::ShutdownInternal() { + MOZ_ASSERT(IsCurrentThreadRunningWorker()); + mProxy->Shutdown(); +} + +void ConnectionProxy::Notify(const hal::NetworkInformation& aNetworkInfo) { + MOZ_ASSERT(NS_IsMainThread()); + + RefPtr<NotifyRunnable> runnable = + new NotifyRunnable(mWorkerRef->Private(), this, + static_cast<ConnectionType>(aNetworkInfo.type()), + aNetworkInfo.isWifi(), aNetworkInfo.dhcpGateway()); + runnable->Dispatch(); +} + +void ConnectionProxy::Shutdown() { + MOZ_ASSERT(IsCurrentThreadRunningWorker()); + + // Already shut down. + if (!mConnection) { + return; + } + + mConnection = nullptr; + + RefPtr<ShutdownRunnable> runnable = + new ShutdownRunnable(mWorkerRef->Private(), this); + + ErrorResult rv; + // This runnable _must_ be executed. + runnable->Dispatch(Killing, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + } + + mWorkerRef = nullptr; +} + +} // namespace mozilla::dom::network |