/* -*- 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/. */
// Handle for a TaskQueue being tracked by a TaskQueueTracker. When created, // it is registered with the TaskQueueTracker, and when destroyed it is // unregistered. Holds a threadsafe weak reference to the TaskQueue. class TaskQueueTrackerEntry final
: private LinkedListElement<TaskQueueTrackerEntry> { public:
TaskQueueTrackerEntry(TaskQueueTracker* aTracker, const RefPtr<TaskQueue>& aQueue)
: mTracker(aTracker), mQueue(aQueue) {
MutexAutoLock lock(mTracker->mMutex);
mTracker->mEntries.insertFront(this);
}
~TaskQueueTrackerEntry() {
MutexAutoLock lock(mTracker->mMutex);
removeFrom(mTracker->mEntries);
}
// If |target| is a TaskQueueTracker, register this TaskQueue with it. It will // be unregistered when the TaskQueue is destroyed or shut down. if (RefPtr<TaskQueueTracker> tracker = do_QueryObject(target)) {
MonitorAutoLock lock(queue->mQueueMonitor);
queue->mTrackerEntry = MakeUnique<TaskQueueTrackerEntry>(tracker, queue);
}
TaskQueue::~TaskQueue() { // We should never free the TaskQueue if it was destroyed abnormally, meaning // that all cleanup tasks should be complete if we do.
MOZ_ASSERT(mShutdownTasks.IsEmpty());
}
// Note aRunnable is passed by ref to support conditional ownership transfer. // See Dispatch() in TaskQueue.h for more details.
nsresult TaskQueue::DispatchLocked(nsCOMPtr<nsIRunnable>& aRunnable,
uint32_t aFlags, DispatchReason aReason) {
mQueueMonitor.AssertCurrentThreadOwns();
// Continue to allow dispatches after shutdown until the last message has been // processed, at which point no more messages will be accepted. if (mIsShutdown && !mIsRunning) { return NS_ERROR_UNEXPECTED;
}
void TaskQueue::AwaitIdleLocked() { // Make sure there are no tasks for this queue waiting in the caller's tail // dispatcher.
MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
!AbstractThread::GetCurrent()->HasTailTasksFor(this));
mQueueMonitor.AssertCurrentThreadOwns();
MOZ_ASSERT(mIsRunning || mTasks.IsEmpty()); while (mIsRunning) {
mQueueMonitor.Wait();
}
}
void TaskQueue::AwaitShutdownAndIdle() {
MOZ_ASSERT(!IsCurrentThreadIn()); // Make sure there are no tasks for this queue waiting in the caller's tail // dispatcher.
MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
!AbstractThread::GetCurrent()->HasTailTasksFor(this));
MonitorAutoLock mon(mQueueMonitor); while (!mIsShutdown) {
mQueueMonitor.Wait();
}
AwaitIdleLocked();
}
RefPtr<ShutdownPromise> TaskQueue::BeginShutdown() { // Dispatch any tasks for this queue waiting in the caller's tail dispatcher, // since this is the last opportunity to do so. if (AbstractThread* currentThread = AbstractThread::GetCurrent()) {
currentThread->TailDispatchTasksFor(this);
}
MonitorAutoLock mon(mQueueMonitor); // Dispatch any cleanup tasks to the queue before we put it into full // shutdown. for (auto& task : mShutdownTasks) {
nsCOMPtr runnable{task->AsRunnable()};
MOZ_ALWAYS_SUCCEEDS(
DispatchLocked(runnable, NS_DISPATCH_NORMAL, TailDispatch));
}
mShutdownTasks.Clear();
mIsShutdown = true;
RefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
MaybeResolveShutdown();
mon.NotifyAll(); return p;
}
void TaskQueue::MaybeResolveShutdown() {
mQueueMonitor.AssertCurrentThreadOwns(); if (mIsShutdown && !mIsRunning) {
mShutdownPromise.ResolveIfExists(true, __func__); // Disconnect from our target as we won't try to dispatch any more events.
mTrackerEntry = nullptr;
mTarget = nullptr;
}
}
// Note that dropping the queue monitor before running the task, and // taking the monitor again after the task has run ensures we have memory // fences enforced. This means that if the object we're calling wasn't // designed to be threadsafe, it will be, provided we're only calling it // in this task queue.
{
AutoTaskGuard g(mQueue);
SerialEventTargetGuard tg(mQueue);
{
LogRunnable::Run log(event.event);
// Drop the reference to event. The event will hold a reference to the // object it's calling, and we don't want to keep it alive, it may be // making assumptions what holds references to it. This is especially // the case if the object is waiting for us to shutdown, so that it // can shutdown (like in the MediaDecoderStateMachine's SHUTDOWN case).
event.event = nullptr;
}
}
{
MonitorAutoLock mon(mQueue->mQueueMonitor); if (mQueue->mTasks.IsEmpty()) { // No more events to run. Exit the task runner.
mQueue->mIsRunning = false;
mQueue->MaybeResolveShutdown();
mon.NotifyAll(); return NS_OK;
}
}
// There's at least one more event that we can run. Dispatch this Runner // to the target again to ensure it runs again. Note that we don't just // run in a loop here so that we don't hog the target. This means we may // run on another thread next time, but we rely on the memory fences from // mQueueMonitor for thread safety of non-threadsafe tasks.
nsresult rv;
{
MonitorAutoLock mon(mQueue->mQueueMonitor);
rv = mQueue->mTarget->Dispatch( this, mQueue->mTasks.FirstElement().flags | NS_DISPATCH_AT_END);
} if (NS_FAILED(rv)) { // Failed to dispatch, shutdown!
MonitorAutoLock mon(mQueue->mQueueMonitor);
mQueue->mIsRunning = false;
mQueue->mIsShutdown = true;
mQueue->MaybeResolveShutdown();
mon.NotifyAll();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.