/* -*- 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 "IPCBlobInputStreamThread.h" #include "mozilla/StaticMutex.h" #include "mozilla/SystemGroup.h" #include "mozilla/TaskCategory.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/PBackgroundChild.h" #include "nsXPCOMPrivate.h" namespace mozilla { using namespace ipc; namespace dom { namespace { StaticMutex gIPCBlobThreadMutex; StaticRefPtr gIPCBlobThread; bool gShutdownHasStarted = false; class ThreadInitializeRunnable final : public Runnable { public: ThreadInitializeRunnable() : Runnable("dom::ThreadInitializeRunnable") {} NS_IMETHOD Run() override { mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex); MOZ_ASSERT(gIPCBlobThread); gIPCBlobThread->InitializeOnMainThread(); return NS_OK; } }; class MigrateActorRunnable final : public Runnable { public: explicit MigrateActorRunnable(IPCBlobInputStreamChild* aActor) : Runnable("dom::MigrateActorRunnable") , mActor(aActor) { MOZ_ASSERT(mActor); } NS_IMETHOD Run() override { MOZ_ASSERT(mActor->State() == IPCBlobInputStreamChild::eInactiveMigrating); PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread(); if (!actorChild) { return NS_OK; } if (actorChild->SendPIPCBlobInputStreamConstructor(mActor, mActor->ID(), mActor->Size())) { // We need manually to increase the reference for this actor because the // IPC allocator method is not triggered. The Release() is called by IPDL // when the actor is deleted. mActor.get()->AddRef(); mActor->Migrated(); } return NS_OK; } private: ~MigrateActorRunnable() = default; RefPtr mActor; }; } // anonymous NS_IMPL_ISUPPORTS(IPCBlobInputStreamThread, nsIObserver, nsIEventTarget) /* static */ bool IPCBlobInputStreamThread::IsOnFileEventTarget(nsIEventTarget* aEventTarget) { MOZ_ASSERT(aEventTarget); mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex); return gIPCBlobThread && aEventTarget == gIPCBlobThread->mThread; } /* static */ IPCBlobInputStreamThread* IPCBlobInputStreamThread::GetOrCreate() { mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex); if (gShutdownHasStarted) { return nullptr; } if (!gIPCBlobThread) { gIPCBlobThread = new IPCBlobInputStreamThread(); if (!gIPCBlobThread->Initialize()) { return nullptr; } } return gIPCBlobThread; } bool IPCBlobInputStreamThread::Initialize() { nsCOMPtr thread; nsresult rv = NS_NewNamedThread("DOM File", getter_AddRefs(thread)); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } mThread = thread; if (!mPendingActors.IsEmpty()) { for (uint32_t i = 0; i < mPendingActors.Length(); ++i) { MigrateActorInternal(mPendingActors[i]); } mPendingActors.Clear(); } if (!NS_IsMainThread()) { RefPtr runnable = new ThreadInitializeRunnable(); SystemGroup::Dispatch(TaskCategory::Other, runnable.forget()); return true; } InitializeOnMainThread(); return true; } void IPCBlobInputStreamThread::InitializeOnMainThread() { MOZ_ASSERT(NS_IsMainThread()); nsCOMPtr obs = services::GetObserverService(); if (NS_WARN_IF(!obs)) { return; } nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false); if (NS_WARN_IF(NS_FAILED(rv))) { return; } } NS_IMETHODIMP IPCBlobInputStreamThread::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID)); mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex); if (mThread) { mThread->Shutdown(); mThread = nullptr; } gShutdownHasStarted = true; gIPCBlobThread = nullptr; return NS_OK; } void IPCBlobInputStreamThread::MigrateActor(IPCBlobInputStreamChild* aActor) { MOZ_ASSERT(aActor->State() == IPCBlobInputStreamChild::eInactiveMigrating); mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex); if (gShutdownHasStarted) { return; } if (!mThread) { // The thread is not initialized yet. mPendingActors.AppendElement(aActor); return; } MigrateActorInternal(aActor); } void IPCBlobInputStreamThread::MigrateActorInternal(IPCBlobInputStreamChild* aActor) { RefPtr runnable = new MigrateActorRunnable(aActor); mThread->Dispatch(runnable, NS_DISPATCH_NORMAL); } // nsIEventTarget NS_IMETHODIMP_(bool) IPCBlobInputStreamThread::IsOnCurrentThreadInfallible() { return mThread->IsOnCurrentThread(); } NS_IMETHODIMP IPCBlobInputStreamThread::IsOnCurrentThread(bool* aRetval) { return mThread->IsOnCurrentThread(aRetval); } NS_IMETHODIMP IPCBlobInputStreamThread::Dispatch(already_AddRefed aRunnable, uint32_t aFlags) { nsCOMPtr runnable(aRunnable); mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex); if (gShutdownHasStarted) { return NS_ERROR_NOT_INITIALIZED; } return mThread->Dispatch(runnable.forget(), aFlags); } NS_IMETHODIMP IPCBlobInputStreamThread::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) { nsCOMPtr runnable(aRunnable); return Dispatch(runnable.forget(), aFlags); } NS_IMETHODIMP IPCBlobInputStreamThread::DelayedDispatch(already_AddRefed, uint32_t) { return NS_ERROR_NOT_IMPLEMENTED; } } // dom namespace } // mozilla namespace