/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=8 et ft=cpp : */ /* 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/. */ #ifndef mozilla_ipc_AsyncBlockers_h #define mozilla_ipc_AsyncBlockers_h #include "mozilla/MozPromise.h" #include "mozilla/ThreadSafety.h" #include "nsTArray.h" // FIXME: when bug 1760855 is fixed, it should not be required anymore namespace mozilla::ipc { /** * AsyncBlockers provide a simple registration service that allows to suspend * completion of a particular task until all registered entries have been * cleared. This can be used to implement a similar service to * nsAsyncShutdownService in processes where it wouldn't normally be available. * This class is thread-safe. */ class AsyncBlockers { public: AsyncBlockers() : mLock("AsyncRegistrar"), mPromise(new GenericPromise::Private(__func__)) {} void Register(void* aBlocker) { MutexAutoLock lock(mLock); mBlockers.InsertElementSorted(aBlocker); } void Deregister(void* aBlocker) { MutexAutoLock lock(mLock); MOZ_ASSERT(mBlockers.ContainsSorted(aBlocker)); MOZ_ALWAYS_TRUE(mBlockers.RemoveElementSorted(aBlocker)); MaybeResolve(); } RefPtr WaitUntilClear(uint32_t aTimeOutInMs = 0) { { MutexAutoLock lock(mLock); MaybeResolve(); } if (aTimeOutInMs > 0) { GetCurrentEventTarget()->DelayedDispatch( NS_NewRunnableFunction("AsyncBlockers::WaitUntilClear", [promise = mPromise]() { // The AsyncBlockers object may have been // deleted by now and the object isn't // refcounted (nor do we want it to be). We // can unconditionally resolve the promise // even it has already been resolved as // MozPromise are thread-safe and will just // ignore the action if already resolved. promise->Resolve(true, __func__); }), aTimeOutInMs); } return mPromise; } virtual ~AsyncBlockers() { mPromise->Resolve(true, __func__); } private: void MaybeResolve() MOZ_REQUIRES(mLock) { mLock.AssertCurrentThreadOwns(); if (!mBlockers.IsEmpty()) { return; } mPromise->Resolve(true, __func__); } Mutex mLock; nsTArray mBlockers MOZ_GUARDED_BY(mLock); const RefPtr mPromise; }; } // namespace mozilla::ipc #endif // mozilla_ipc_AsyncBlockers_h