mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
d1af43433c
- Bug 1187817. Part 1 - Move Set{Volume,PlaybackRate,PreservesPitch} to the audio thread. r=kinetik. (a234a7080)
- Bug 1187817. Part 2 - remove unused code. r=kinetik. (836c52a19)
- Bug 1187763. Part 1 - move while loop out of WaitingForAudioToPlay(). r=kinetik. (a80d70f87)
- Bug 1187763. Part 2 - extract some code of AudioLoop() into its own function. r=kinetik. (275b8bfb6)
- Bug 1187763. Part 3 - refactor AudioSink::AudioLoop into a series of events. r=kinetik (7eb3f506f)
- Bug 1187817. Part 3 - move SetPlaying to the audio thread. r=kinetik. (0fef85968)
- Bug 1187817. Part 4 - move some code in Shutdown to the audio thread. r=kinetik. (8c73fbe6d)
- Bug 1187817. Part 5 - assert some code in the audio thread and don't enter the monitor. r=kinetik. (64de4616f)
- move include (f842b4b1e)
- Bug 1163486 - Update test to use new MP4Demuxer. r=bholley (b853be477)
- Bug 1190496 - Namespace the SharedThreadPool.h include. r=cpearce (196d25d42)
- Bug 1190496 - Hoist SharedThreadPool into xpcom. r=froydnj (3dad8176f)
- Bug 1190492 - Hoist AbstractThread and TaskDispatcher to xpcom. r=froydnj (c3329fa29)
- bug 1166107 documentation of mWaitForInternalDrain thread access r=gerald (f762764b1)
- correct comment (b623b2959)
- revert demuxer check from promise reject to assert like in 1156708 (f540b270c)
- more chekcs back to asserts (7e82a0f99)
- space (733bd85a3)
- Bug 1188220: Allow disabling HW acceleration even when SharedDecoderManager isn't used. r=cpearce (909a86682)
- bug 1161903 ensure pending DrainComplete is not run after Flush() r=cpearce (648cabbb7)
- Bug 1158089 - Fall back to d3d9 DXVA if d3d11 initialization fails. r=cpearce (84b3a8e6d)
297 lines
7.9 KiB
C++
297 lines
7.9 KiB
C++
/* -*- 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 "mozilla/TaskQueue.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
#include "mozilla/SharedThreadPool.h"
|
|
|
|
namespace mozilla {
|
|
|
|
TaskQueue::TaskQueue(already_AddRefed<SharedThreadPool> aPool,
|
|
bool aRequireTailDispatch)
|
|
: AbstractThread(aRequireTailDispatch)
|
|
, mPool(aPool)
|
|
, mQueueMonitor("TaskQueue::Queue")
|
|
, mTailDispatcher(nullptr)
|
|
, mIsRunning(false)
|
|
, mIsShutdown(false)
|
|
, mIsFlushing(false)
|
|
{
|
|
MOZ_COUNT_CTOR(TaskQueue);
|
|
}
|
|
|
|
TaskQueue::~TaskQueue()
|
|
{
|
|
MonitorAutoLock mon(mQueueMonitor);
|
|
MOZ_ASSERT(mIsShutdown);
|
|
MOZ_COUNT_DTOR(TaskQueue);
|
|
}
|
|
|
|
TaskDispatcher&
|
|
TaskQueue::TailDispatcher()
|
|
{
|
|
MOZ_ASSERT(IsCurrentThreadIn());
|
|
MOZ_ASSERT(mTailDispatcher);
|
|
return *mTailDispatcher;
|
|
}
|
|
|
|
nsresult
|
|
TaskQueue::DispatchLocked(already_AddRefed<nsIRunnable> aRunnable,
|
|
DispatchMode aMode, DispatchFailureHandling aFailureHandling,
|
|
DispatchReason aReason)
|
|
{
|
|
nsCOMPtr<nsIRunnable> r = aRunnable;
|
|
AbstractThread* currentThread;
|
|
if (aReason != TailDispatch && (currentThread = GetCurrent()) && RequiresTailDispatch(currentThread)) {
|
|
currentThread->TailDispatcher().AddTask(this, r.forget(), aFailureHandling);
|
|
return NS_OK;
|
|
}
|
|
|
|
mQueueMonitor.AssertCurrentThreadOwns();
|
|
if (mIsFlushing && aMode == AbortIfFlushing) {
|
|
return NS_ERROR_ABORT;
|
|
}
|
|
if (mIsShutdown) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
mTasks.push(r.forget());
|
|
if (mIsRunning) {
|
|
return NS_OK;
|
|
}
|
|
RefPtr<nsIRunnable> runner(new Runner(this));
|
|
nsresult rv = mPool->Dispatch(runner, NS_DISPATCH_NORMAL);
|
|
if (NS_FAILED(rv)) {
|
|
NS_WARNING("Failed to dispatch runnable to run TaskQueue");
|
|
return rv;
|
|
}
|
|
mIsRunning = true;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
class TaskQueueSyncRunnable : public nsRunnable {
|
|
public:
|
|
explicit TaskQueueSyncRunnable(already_AddRefed<nsIRunnable> aRunnable)
|
|
: mRunnable(aRunnable)
|
|
, mMonitor("TaskQueueSyncRunnable")
|
|
, mDone(false)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD Run() {
|
|
nsresult rv = mRunnable->Run();
|
|
{
|
|
MonitorAutoLock mon(mMonitor);
|
|
mDone = true;
|
|
mon.NotifyAll();
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
void WaitUntilDone() {
|
|
MonitorAutoLock mon(mMonitor);
|
|
while (!mDone) {
|
|
mon.Wait();
|
|
}
|
|
}
|
|
private:
|
|
RefPtr<nsIRunnable> mRunnable;
|
|
Monitor mMonitor;
|
|
bool mDone;
|
|
};
|
|
|
|
void
|
|
TaskQueue::SyncDispatch(already_AddRefed<nsIRunnable> aRunnable) {
|
|
NS_WARNING("TaskQueue::SyncDispatch is dangerous and deprecated. Stop using this!");
|
|
nsRefPtr<TaskQueueSyncRunnable> task(new TaskQueueSyncRunnable(Move(aRunnable)));
|
|
|
|
// Tail dispatchers don't interact nicely with sync dispatch. We require that
|
|
// nothing is already in the tail dispatcher, and then sidestep it for this
|
|
// task.
|
|
MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
|
|
!AbstractThread::GetCurrent()->TailDispatcher().HasTasksFor(this));
|
|
nsRefPtr<TaskQueueSyncRunnable> taskRef = task;
|
|
Dispatch(taskRef.forget(), AssertDispatchSuccess, TailDispatch);
|
|
|
|
task->WaitUntilDone();
|
|
}
|
|
|
|
void
|
|
TaskQueue::AwaitIdle()
|
|
{
|
|
MonitorAutoLock mon(mQueueMonitor);
|
|
AwaitIdleLocked();
|
|
}
|
|
|
|
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()->TailDispatcher().HasTasksFor(this));
|
|
|
|
mQueueMonitor.AssertCurrentThreadOwns();
|
|
MOZ_ASSERT(mIsRunning || mTasks.empty());
|
|
while (mIsRunning) {
|
|
mQueueMonitor.Wait();
|
|
}
|
|
}
|
|
|
|
void
|
|
TaskQueue::AwaitShutdownAndIdle()
|
|
{
|
|
// Make sure there are no tasks for this queue waiting in the caller's tail
|
|
// dispatcher.
|
|
MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
|
|
!AbstractThread::GetCurrent()->TailDispatcher().HasTasksFor(this));
|
|
|
|
MonitorAutoLock mon(mQueueMonitor);
|
|
while (!mIsShutdown) {
|
|
mQueueMonitor.Wait();
|
|
}
|
|
AwaitIdleLocked();
|
|
}
|
|
|
|
nsRefPtr<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->TailDispatcher().DispatchTasksFor(this);
|
|
}
|
|
|
|
MonitorAutoLock mon(mQueueMonitor);
|
|
mIsShutdown = true;
|
|
nsRefPtr<ShutdownPromise> p = mShutdownPromise.Ensure(__func__);
|
|
MaybeResolveShutdown();
|
|
mon.NotifyAll();
|
|
return p;
|
|
}
|
|
|
|
void
|
|
FlushableTaskQueue::Flush()
|
|
{
|
|
MonitorAutoLock mon(mQueueMonitor);
|
|
AutoSetFlushing autoFlush(this);
|
|
FlushLocked();
|
|
AwaitIdleLocked();
|
|
}
|
|
|
|
nsresult
|
|
FlushableTaskQueue::FlushAndDispatch(already_AddRefed<nsIRunnable> aRunnable)
|
|
{
|
|
MonitorAutoLock mon(mQueueMonitor);
|
|
AutoSetFlushing autoFlush(this);
|
|
FlushLocked();
|
|
nsCOMPtr<nsIRunnable> r = dont_AddRef(aRunnable.take());
|
|
nsresult rv = DispatchLocked(r.forget(), IgnoreFlushing, AssertDispatchSuccess);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
AwaitIdleLocked();
|
|
return NS_OK;
|
|
}
|
|
|
|
void
|
|
FlushableTaskQueue::FlushLocked()
|
|
{
|
|
// Make sure there are no tasks for this queue waiting in the caller's tail
|
|
// dispatcher.
|
|
MOZ_ASSERT_IF(AbstractThread::GetCurrent(),
|
|
!AbstractThread::GetCurrent()->TailDispatcher().HasTasksFor(this));
|
|
|
|
mQueueMonitor.AssertCurrentThreadOwns();
|
|
MOZ_ASSERT(mIsFlushing);
|
|
|
|
// Clear the tasks. If this strikes you as awful, stop using a
|
|
// FlushableTaskQueue.
|
|
while (!mTasks.empty()) {
|
|
mTasks.pop();
|
|
}
|
|
}
|
|
|
|
bool
|
|
TaskQueue::IsEmpty()
|
|
{
|
|
MonitorAutoLock mon(mQueueMonitor);
|
|
return mTasks.empty();
|
|
}
|
|
|
|
bool
|
|
TaskQueue::IsCurrentThreadIn()
|
|
{
|
|
bool in = NS_GetCurrentThread() == mRunningThread;
|
|
MOZ_ASSERT(in == (GetCurrent() == this));
|
|
return in;
|
|
}
|
|
|
|
nsresult
|
|
TaskQueue::Runner::Run()
|
|
{
|
|
RefPtr<nsIRunnable> event;
|
|
{
|
|
MonitorAutoLock mon(mQueue->mQueueMonitor);
|
|
MOZ_ASSERT(mQueue->mIsRunning);
|
|
if (mQueue->mTasks.size() == 0) {
|
|
mQueue->mIsRunning = false;
|
|
mQueue->MaybeResolveShutdown();
|
|
mon.NotifyAll();
|
|
return NS_OK;
|
|
}
|
|
event = mQueue->mTasks.front();
|
|
mQueue->mTasks.pop();
|
|
}
|
|
MOZ_ASSERT(event);
|
|
|
|
// 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);
|
|
event->Run();
|
|
}
|
|
|
|
// 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 = nullptr;
|
|
|
|
{
|
|
MonitorAutoLock mon(mQueue->mQueueMonitor);
|
|
if (mQueue->mTasks.size() == 0) {
|
|
// 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 thread pool again to ensure it runs again. Note that we don't just
|
|
// run in a loop here so that we don't hog the thread pool. 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 = mQueue->mPool->Dispatch(this, NS_DISPATCH_NORMAL);
|
|
if (NS_FAILED(rv)) {
|
|
// Failed to dispatch, shutdown!
|
|
MonitorAutoLock mon(mQueue->mQueueMonitor);
|
|
mQueue->mIsRunning = false;
|
|
mQueue->mIsShutdown = true;
|
|
mQueue->MaybeResolveShutdown();
|
|
mon.NotifyAll();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
} // namespace mozilla
|