content/media/MediaTaskQueue.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "MediaTaskQueue.h"
     8 #include "nsThreadUtils.h"
     9 #include "SharedThreadPool.h"
    11 namespace mozilla {
    13 MediaTaskQueue::MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool)
    14   : mPool(aPool)
    15   , mQueueMonitor("MediaTaskQueue::Queue")
    16   , mIsRunning(false)
    17   , mIsShutdown(false)
    18 {
    19   MOZ_COUNT_CTOR(MediaTaskQueue);
    20 }
    22 MediaTaskQueue::~MediaTaskQueue()
    23 {
    24   MonitorAutoLock mon(mQueueMonitor);
    25   MOZ_ASSERT(mIsShutdown);
    26   MOZ_COUNT_DTOR(MediaTaskQueue);
    27 }
    29 nsresult
    30 MediaTaskQueue::Dispatch(TemporaryRef<nsIRunnable> aRunnable)
    31 {
    32   MonitorAutoLock mon(mQueueMonitor);
    33   if (mIsShutdown) {
    34     return NS_ERROR_FAILURE;
    35   }
    36   mTasks.push(aRunnable);
    37   if (mIsRunning) {
    38     return NS_OK;
    39   }
    40   RefPtr<nsIRunnable> runner(new Runner(this));
    41   nsresult rv = mPool->Dispatch(runner, NS_DISPATCH_NORMAL);
    42   if (NS_FAILED(rv)) {
    43     NS_WARNING("Failed to dispatch runnable to run MediaTaskQueue");
    44     return rv;
    45   }
    46   mIsRunning = true;
    48   return NS_OK;
    49 }
    51 void
    52 MediaTaskQueue::AwaitIdle()
    53 {
    54   MonitorAutoLock mon(mQueueMonitor);
    55   AwaitIdleLocked();
    56 }
    58 void
    59 MediaTaskQueue::AwaitIdleLocked()
    60 {
    61   mQueueMonitor.AssertCurrentThreadOwns();
    62   MOZ_ASSERT(mIsRunning || mTasks.empty());
    63   while (mIsRunning) {
    64     mQueueMonitor.Wait();
    65   }
    66 }
    68 void
    69 MediaTaskQueue::Shutdown()
    70 {
    71   MonitorAutoLock mon(mQueueMonitor);
    72   mIsShutdown = true;
    73   AwaitIdleLocked();
    74 }
    76 void
    77 MediaTaskQueue::Flush()
    78 {
    79   MonitorAutoLock mon(mQueueMonitor);
    80   while (!mTasks.empty()) {
    81     mTasks.pop();
    82   }
    83   AwaitIdleLocked();
    84 }
    86 bool
    87 MediaTaskQueue::IsEmpty()
    88 {
    89   MonitorAutoLock mon(mQueueMonitor);
    90   return mTasks.empty();
    91 }
    93 bool
    94 MediaTaskQueue::IsCurrentThreadIn()
    95 {
    96 #ifdef DEBUG
    97   MonitorAutoLock mon(mQueueMonitor);
    98   return NS_GetCurrentThread() == mRunningThread;
    99 #else
   100   return false;
   101 #endif
   102 }
   104 nsresult
   105 MediaTaskQueue::Runner::Run()
   106 {
   107   RefPtr<nsIRunnable> event;
   108   {
   109     MonitorAutoLock mon(mQueue->mQueueMonitor);
   110     MOZ_ASSERT(mQueue->mIsRunning);
   111     mQueue->mRunningThread = NS_GetCurrentThread();
   112     if (mQueue->mTasks.size() == 0) {
   113       mQueue->mIsRunning = false;
   114       mon.NotifyAll();
   115       return NS_OK;
   116     }
   117     event = mQueue->mTasks.front();
   118     mQueue->mTasks.pop();
   119   }
   120   MOZ_ASSERT(event);
   122   // Note that dropping the queue monitor before running the task, and
   123   // taking the monitor again after the task has run ensures we have memory
   124   // fences enforced. This means that if the object we're calling wasn't
   125   // designed to be threadsafe, it will be, provided we're only calling it
   126   // in this task queue.
   127   event->Run();
   129   // Drop the reference to event. The event will hold a reference to the
   130   // object it's calling, and we don't want to keep it alive, it may be
   131   // making assumptions what holds references to it. This is especially
   132   // the case if the object is waiting for us to shutdown, so that it
   133   // can shutdown (like in the MediaDecoderStateMachine's SHUTDOWN case).
   134   event = nullptr;
   136   {
   137     MonitorAutoLock mon(mQueue->mQueueMonitor);
   138     if (mQueue->mTasks.size() == 0) {
   139       // No more events to run. Exit the task runner.
   140       mQueue->mIsRunning = false;
   141       mon.NotifyAll();
   142       mQueue->mRunningThread = nullptr;
   143       return NS_OK;
   144     }
   145   }
   147   // There's at least one more event that we can run. Dispatch this Runner
   148   // to the thread pool again to ensure it runs again. Note that we don't just
   149   // run in a loop here so that we don't hog the thread pool. This means we may
   150   // run on another thread next time, but we rely on the memory fences from
   151   // mQueueMonitor for thread safety of non-threadsafe tasks.
   152   {
   153     MonitorAutoLock mon(mQueue->mQueueMonitor);
   154     // Note: Hold the monitor *before* we dispatch, in case we context switch
   155     // to another thread pool in the queue immediately and take the lock in the
   156     // other thread; mRunningThread could be set to the new thread's value and
   157     // then incorrectly anulled below in that case.
   158     nsresult rv = mQueue->mPool->Dispatch(this, NS_DISPATCH_NORMAL);
   159     if (NS_FAILED(rv)) {
   160       // Failed to dispatch, shutdown!
   161       mQueue->mIsRunning = false;
   162       mQueue->mIsShutdown = true;
   163       mon.NotifyAll();
   164     }
   165     mQueue->mRunningThread = nullptr;
   166   }
   168   return NS_OK;
   169 }
   171 } // namespace mozilla

mercurial