content/media/SharedThreadPool.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
     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 "SharedThreadPool.h"
     8 #include "mozilla/Monitor.h"
     9 #include "mozilla/StaticPtr.h"
    10 #include "nsDataHashtable.h"
    11 #include "VideoUtils.h"
    12 #include "nsXPCOMCIDInternal.h"
    13 #include "nsComponentManagerUtils.h"
    15 #ifdef XP_WIN
    16 // Required to init MSCOM by MSCOMInitThreadPoolListener.
    17 #include <objbase.h>
    18 #endif
    20 namespace mozilla {
    22 // Created and destroyed on the main thread.
    23 static StaticAutoPtr<ReentrantMonitor> sMonitor;
    25 // Hashtable, maps thread pool name to SharedThreadPool instance.
    26 // Modified only on the main thread.
    27 static StaticAutoPtr<nsDataHashtable<nsCStringHashKey, SharedThreadPool*>> sPools;
    29 static already_AddRefed<nsIThreadPool>
    30 CreateThreadPool(const nsCString& aName);
    32 static void
    33 DestroySharedThreadPoolHashTable();
    35 void
    36 SharedThreadPool::EnsureInitialized()
    37 {
    38   MOZ_ASSERT(NS_IsMainThread());
    39   if (sMonitor || sPools) {
    40     // Already initalized.
    41     return;
    42   }
    43   sMonitor = new ReentrantMonitor("SharedThreadPool");
    44   sPools = new nsDataHashtable<nsCStringHashKey, SharedThreadPool*>();
    45 }
    47 class ShutdownPoolsEvent : public nsRunnable {
    48 public:
    49   NS_IMETHODIMP Run() {
    50     MOZ_ASSERT(NS_IsMainThread());
    51     DestroySharedThreadPoolHashTable();
    52     return NS_OK;
    53   }
    54 };
    56 static void
    57 DestroySharedThreadPoolHashTable()
    58 {
    59   MOZ_ASSERT(NS_IsMainThread());
    60   MOZ_ASSERT(sMonitor && sPools);
    61   if (!sPools->Count()) {
    62     // No more SharedThreadPool singletons. Delete the hash table.
    63     // Note we don't need to lock sMonitor, since we only modify the
    64     // hash table on the main thread, and if the hash table is empty
    65     // there are no external references into its contents.
    66     sPools = nullptr;
    67     sMonitor = nullptr;
    68   }
    69 }
    71 /* static */
    72 void
    73 SharedThreadPool::SpinUntilShutdown()
    74 {
    75   MOZ_ASSERT(NS_IsMainThread());
    76   // Wait until the ShutdownPoolsEvent has been run and shutdown the pool.
    77   while (sPools) {
    78     if (!NS_ProcessNextEvent(NS_GetCurrentThread(), true)) {
    79       break;
    80     }
    81   }
    82   MOZ_ASSERT(!sPools);
    83   MOZ_ASSERT(!sMonitor);
    84 }
    86 TemporaryRef<SharedThreadPool>
    87 SharedThreadPool::Get(const nsCString& aName, uint32_t aThreadLimit)
    88 {
    89   MOZ_ASSERT(NS_IsMainThread());
    90   EnsureInitialized();
    91   MOZ_ASSERT(sMonitor);
    92   ReentrantMonitorAutoEnter mon(*sMonitor);
    93   SharedThreadPool* pool = nullptr;
    94   nsresult rv;
    95   if (!sPools->Get(aName, &pool)) {
    96     nsCOMPtr<nsIThreadPool> threadPool(CreateThreadPool(aName));
    97     NS_ENSURE_TRUE(threadPool, nullptr);
    98     pool = new SharedThreadPool(aName, threadPool);
   100     // Set the thread and idle limits. Note that we don't rely on the
   101     // EnsureThreadLimitIsAtLeast() call below, as the default thread limit
   102     // is 4, and if aThreadLimit is less than 4 we'll end up with a pool
   103     // with 4 threads rather than what we expected; so we'll have unexpected
   104     // behaviour.
   105     rv = pool->SetThreadLimit(aThreadLimit);
   106     NS_ENSURE_SUCCESS(rv, nullptr);
   108     rv = pool->SetIdleThreadLimit(aThreadLimit);
   109     NS_ENSURE_SUCCESS(rv, nullptr);
   111     sPools->Put(aName, pool);
   112   } else if (NS_FAILED(pool->EnsureThreadLimitIsAtLeast(aThreadLimit))) {
   113     NS_WARNING("Failed to set limits on thread pool");
   114   }
   116   MOZ_ASSERT(pool);
   117   RefPtr<SharedThreadPool> instance(pool);
   118   return instance.forget();
   119 }
   121 NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::AddRef(void)
   122 {
   123   MOZ_ASSERT(sMonitor);
   124   ReentrantMonitorAutoEnter mon(*sMonitor);
   125   MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
   126   nsrefcnt count = ++mRefCnt;
   127   NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this));
   128   return count;
   129 }
   131 NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::Release(void)
   132 {
   133   MOZ_ASSERT(sMonitor);
   134   bool dispatchShutdownEvent;
   135   {
   136     ReentrantMonitorAutoEnter mon(*sMonitor);
   137     nsrefcnt count = --mRefCnt;
   138     NS_LOG_RELEASE(this, count, "SharedThreadPool");
   139     if (count) {
   140       return count;
   141     }
   143     // Zero refcount. Must shutdown and then delete the thread pool.
   145     // First, dispatch an event to the main thread to call Shutdown() on
   146     // the nsIThreadPool. The Runnable here  will add a refcount to the pool,
   147     // and when the Runnable releases the nsIThreadPool it will be deleted.
   148     RefPtr<nsIRunnable> r = NS_NewRunnableMethod(mPool, &nsIThreadPool::Shutdown);
   149     NS_DispatchToMainThread(r);
   151     // Remove SharedThreadPool from table of pools.
   152     sPools->Remove(mName);
   153     MOZ_ASSERT(!sPools->Get(mName));
   155     // Stabilize refcount, so that if something in the dtor QIs,
   156     // it won't explode.
   157     mRefCnt = 1;
   159     delete this;
   161     dispatchShutdownEvent = sPools->Count() == 0;
   162   }
   163   if (dispatchShutdownEvent) {
   164     // No more SharedThreadPools alive. Destroy the hash table.
   165     // Ensure that we only run on the main thread.
   166     // Do this in an event so that if something holds the monitor we won't
   167     // be deleting the monitor while it's held.
   168     NS_DispatchToMainThread(new ShutdownPoolsEvent(), NS_DISPATCH_NORMAL);
   169   }
   170   return 0;
   171 }
   173 NS_IMPL_QUERY_INTERFACE(SharedThreadPool, nsIThreadPool, nsIEventTarget)
   175 SharedThreadPool::SharedThreadPool(const nsCString& aName,
   176                                    nsIThreadPool* aPool)
   177   : mName(aName)
   178   , mPool(aPool)
   179   , mRefCnt(0)
   180 {
   181   MOZ_COUNT_CTOR(SharedThreadPool);
   182   mEventTarget = do_QueryInterface(aPool);
   183 }
   185 SharedThreadPool::~SharedThreadPool()
   186 {
   187   MOZ_COUNT_DTOR(SharedThreadPool);
   188 }
   190 #ifdef XP_WIN
   192 // Thread pool listener which ensures that MSCOM is initialized and
   193 // deinitialized on the thread pool thread. We may call into WMF or
   194 // DirectShow on this thread, so we need MSCOM working.
   195 class MSCOMInitThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener {
   196 public:
   197   NS_DECL_THREADSAFE_ISUPPORTS
   198   NS_DECL_NSITHREADPOOLLISTENER
   199 };
   201 NS_IMPL_ISUPPORTS(MSCOMInitThreadPoolListener, nsIThreadPoolListener)
   203 NS_IMETHODIMP
   204 MSCOMInitThreadPoolListener::OnThreadCreated()
   205 {
   206   HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED);
   207   if (FAILED(hr)) {
   208     NS_WARNING("Failed to initialize MSCOM on WMFByteStream thread.");
   209   }
   210   return NS_OK;
   211 }
   213 NS_IMETHODIMP
   214 MSCOMInitThreadPoolListener::OnThreadShuttingDown()
   215 {
   216   CoUninitialize();
   217   return NS_OK;
   218 }
   220 #endif // XP_WIN
   222 nsresult
   223 SharedThreadPool::EnsureThreadLimitIsAtLeast(uint32_t aLimit)
   224 {
   225   // We limit the number of threads that we use for media. Note that we
   226   // set the thread limit to the same as the idle limit so that we're not
   227   // constantly creating and destroying threads (see Bug 881954). When the
   228   // thread pool threads shutdown they dispatch an event to the main thread
   229   // to call nsIThread::Shutdown(), and if we're very busy that can take a
   230   // while to run, and we end up with dozens of extra threads. Note that
   231   // threads that are idle for 60 seconds are shutdown naturally.
   232   uint32_t existingLimit = 0;
   233   nsresult rv;
   235   rv = mPool->GetThreadLimit(&existingLimit);
   236   NS_ENSURE_SUCCESS(rv, rv);
   237   if (aLimit > existingLimit) {
   238     rv = mPool->SetThreadLimit(aLimit);
   239     NS_ENSURE_SUCCESS(rv, rv);
   240   }
   242   rv = mPool->GetIdleThreadLimit(&existingLimit);
   243   NS_ENSURE_SUCCESS(rv, rv);
   244   if (aLimit > existingLimit) {
   245     rv = mPool->SetIdleThreadLimit(aLimit);
   246     NS_ENSURE_SUCCESS(rv, rv);
   247   }
   249   return NS_OK;
   250 }
   252 static already_AddRefed<nsIThreadPool>
   253 CreateThreadPool(const nsCString& aName)
   254 {
   255   MOZ_ASSERT(NS_IsMainThread());
   257   nsresult rv;
   258   nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
   259   NS_ENSURE_SUCCESS(rv, nullptr);
   261   rv = pool->SetName(aName);
   262   NS_ENSURE_SUCCESS(rv, nullptr);
   264   rv = pool->SetThreadStackSize(MEDIA_THREAD_STACK_SIZE);
   265   NS_ENSURE_SUCCESS(rv, nullptr);
   267 #ifdef XP_WIN
   268   // Ensure MSCOM is initialized on the thread pools threads.
   269   nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener();
   270   rv = pool->SetListener(listener);
   271   NS_ENSURE_SUCCESS(rv, nullptr);
   272 #endif
   274   return pool.forget();
   275 }
   277 } // namespace mozilla

mercurial