LCOV - code coverage report
Current view: top level - xpcom/threads - SharedThreadPool.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 13 105 12.4 %
Date: 2017-07-14 16:53:18 Functions: 5 21 23.8 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "mozilla/SharedThreadPool.h"
       8             : #include "mozilla/Monitor.h"
       9             : #include "mozilla/ReentrantMonitor.h"
      10             : #include "mozilla/Services.h"
      11             : #include "mozilla/StaticPtr.h"
      12             : #include "nsDataHashtable.h"
      13             : #include "nsXPCOMCIDInternal.h"
      14             : #include "nsComponentManagerUtils.h"
      15             : #include "nsIObserver.h"
      16             : #include "nsIObserverService.h"
      17             : #ifdef XP_WIN
      18             : #include "ThreadPoolCOMListener.h"
      19             : #endif
      20             : 
      21             : namespace mozilla {
      22             : 
      23             : // Created and destroyed on the main thread.
      24           3 : static StaticAutoPtr<ReentrantMonitor> sMonitor;
      25             : 
      26             : // Hashtable, maps thread pool name to SharedThreadPool instance.
      27             : // Modified only on the main thread.
      28           3 : static StaticAutoPtr<nsDataHashtable<nsCStringHashKey, SharedThreadPool*>> sPools;
      29             : 
      30             : static already_AddRefed<nsIThreadPool>
      31             : CreateThreadPool(const nsCString& aName);
      32             : 
      33           3 : class SharedThreadPoolShutdownObserver : public nsIObserver
      34             : {
      35             : public:
      36             :   NS_DECL_ISUPPORTS
      37             :   NS_DECL_NSIOBSERVER
      38             : protected:
      39           0 :   virtual ~SharedThreadPoolShutdownObserver() {}
      40             : };
      41             : 
      42          18 : NS_IMPL_ISUPPORTS(SharedThreadPoolShutdownObserver, nsIObserver, nsISupports)
      43             : 
      44             : NS_IMETHODIMP
      45           0 : SharedThreadPoolShutdownObserver::Observe(nsISupports* aSubject, const char *aTopic,
      46             :                                           const char16_t *aData)
      47             : {
      48           0 :   MOZ_RELEASE_ASSERT(!strcmp(aTopic, "xpcom-shutdown-threads"));
      49           0 :   SharedThreadPool::SpinUntilEmpty();
      50           0 :   sMonitor = nullptr;
      51           0 :   sPools = nullptr;
      52           0 :   return NS_OK;
      53             : }
      54             : 
      55             : void
      56           3 : SharedThreadPool::InitStatics()
      57             : {
      58           3 :   MOZ_ASSERT(NS_IsMainThread());
      59           3 :   MOZ_ASSERT(!sMonitor && !sPools);
      60           3 :   sMonitor = new ReentrantMonitor("SharedThreadPool");
      61           3 :   sPools = new nsDataHashtable<nsCStringHashKey, SharedThreadPool*>();
      62           6 :   nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
      63           6 :   nsCOMPtr<nsIObserver> obs = new SharedThreadPoolShutdownObserver();
      64           3 :   obsService->AddObserver(obs, "xpcom-shutdown-threads", false);
      65           3 : }
      66             : 
      67             : /* static */
      68             : bool
      69           0 : SharedThreadPool::IsEmpty()
      70             : {
      71           0 :   ReentrantMonitorAutoEnter mon(*sMonitor);
      72           0 :   return !sPools->Count();
      73             : }
      74             : 
      75             : /* static */
      76             : void
      77           0 : SharedThreadPool::SpinUntilEmpty()
      78             : {
      79           0 :   MOZ_ASSERT(NS_IsMainThread());
      80           0 :   SpinEventLoopUntil([]() -> bool {
      81           0 :       sMonitor->AssertNotCurrentThreadIn();
      82           0 :       return IsEmpty();
      83           0 :   });
      84           0 : }
      85             : 
      86             : already_AddRefed<SharedThreadPool>
      87           0 : SharedThreadPool::Get(const nsCString& aName, uint32_t aThreadLimit)
      88             : {
      89           0 :   MOZ_ASSERT(sMonitor && sPools);
      90           0 :   ReentrantMonitorAutoEnter mon(*sMonitor);
      91           0 :   SharedThreadPool* pool = nullptr;
      92             :   nsresult rv;
      93             : 
      94           0 :   if (auto entry = sPools->LookupForAdd(aName)) {
      95           0 :     pool = entry.Data();
      96           0 :     if (NS_FAILED(pool->EnsureThreadLimitIsAtLeast(aThreadLimit))) {
      97           0 :       NS_WARNING("Failed to set limits on thread pool");
      98             :     }
      99             :   } else {
     100           0 :     nsCOMPtr<nsIThreadPool> threadPool(CreateThreadPool(aName));
     101           0 :     if (NS_WARN_IF(!threadPool)) {
     102           0 :       sPools->Remove(aName); // XXX entry.Remove()
     103           0 :       return nullptr;
     104             :     }
     105           0 :     pool = new SharedThreadPool(aName, threadPool);
     106             : 
     107             :     // Set the thread and idle limits. Note that we don't rely on the
     108             :     // EnsureThreadLimitIsAtLeast() call below, as the default thread limit
     109             :     // is 4, and if aThreadLimit is less than 4 we'll end up with a pool
     110             :     // with 4 threads rather than what we expected; so we'll have unexpected
     111             :     // behaviour.
     112           0 :     rv = pool->SetThreadLimit(aThreadLimit);
     113           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     114           0 :       sPools->Remove(aName); // XXX entry.Remove()
     115           0 :       return nullptr;
     116             :     }
     117             : 
     118           0 :     rv = pool->SetIdleThreadLimit(aThreadLimit);
     119           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     120           0 :       sPools->Remove(aName); // XXX entry.Remove()
     121           0 :       return nullptr;
     122             :     }
     123             : 
     124           0 :     entry.OrInsert([pool] () { return pool; });
     125             :   }
     126             : 
     127           0 :   MOZ_ASSERT(pool);
     128           0 :   RefPtr<SharedThreadPool> instance(pool);
     129           0 :   return instance.forget();
     130             : }
     131             : 
     132           0 : NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::AddRef(void)
     133             : {
     134           0 :   MOZ_ASSERT(sMonitor);
     135           0 :   ReentrantMonitorAutoEnter mon(*sMonitor);
     136           0 :   MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
     137           0 :   nsrefcnt count = ++mRefCnt;
     138           0 :   NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this));
     139           0 :   return count;
     140             : }
     141             : 
     142           0 : NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::Release(void)
     143             : {
     144           0 :   MOZ_ASSERT(sMonitor);
     145           0 :   ReentrantMonitorAutoEnter mon(*sMonitor);
     146           0 :   nsrefcnt count = --mRefCnt;
     147           0 :   NS_LOG_RELEASE(this, count, "SharedThreadPool");
     148           0 :   if (count) {
     149           0 :     return count;
     150             :   }
     151             : 
     152             :   // Remove SharedThreadPool from table of pools.
     153           0 :   sPools->Remove(mName);
     154           0 :   MOZ_ASSERT(!sPools->Get(mName));
     155             : 
     156             :   // Dispatch an event to the main thread to call Shutdown() on
     157             :   // the nsIThreadPool. The Runnable here will add a refcount to the pool,
     158             :   // and when the Runnable releases the nsIThreadPool it will be deleted.
     159           0 :   NS_DispatchToMainThread(NewRunnableMethod(
     160           0 :     "nsIThreadPool::Shutdown", mPool, &nsIThreadPool::Shutdown));
     161             : 
     162             :   // Stabilize refcount, so that if something in the dtor QIs, it won't explode.
     163           0 :   mRefCnt = 1;
     164           0 :   delete this;
     165           0 :   return 0;
     166             : }
     167             : 
     168           0 : NS_IMPL_QUERY_INTERFACE(SharedThreadPool, nsIThreadPool, nsIEventTarget)
     169             : 
     170           0 : SharedThreadPool::SharedThreadPool(const nsCString& aName,
     171           0 :                                    nsIThreadPool* aPool)
     172             :   : mName(aName)
     173             :   , mPool(aPool)
     174           0 :   , mRefCnt(0)
     175             : {
     176           0 :   mEventTarget = do_QueryInterface(aPool);
     177           0 : }
     178             : 
     179           0 : SharedThreadPool::~SharedThreadPool()
     180             : {
     181           0 : }
     182             : 
     183             : nsresult
     184           0 : SharedThreadPool::EnsureThreadLimitIsAtLeast(uint32_t aLimit)
     185             : {
     186             :   // We limit the number of threads that we use. Note that we
     187             :   // set the thread limit to the same as the idle limit so that we're not
     188             :   // constantly creating and destroying threads (see Bug 881954). When the
     189             :   // thread pool threads shutdown they dispatch an event to the main thread
     190             :   // to call nsIThread::Shutdown(), and if we're very busy that can take a
     191             :   // while to run, and we end up with dozens of extra threads. Note that
     192             :   // threads that are idle for 60 seconds are shutdown naturally.
     193           0 :   uint32_t existingLimit = 0;
     194             :   nsresult rv;
     195             : 
     196           0 :   rv = mPool->GetThreadLimit(&existingLimit);
     197           0 :   NS_ENSURE_SUCCESS(rv, rv);
     198           0 :   if (aLimit > existingLimit) {
     199           0 :     rv = mPool->SetThreadLimit(aLimit);
     200           0 :     NS_ENSURE_SUCCESS(rv, rv);
     201             :   }
     202             : 
     203           0 :   rv = mPool->GetIdleThreadLimit(&existingLimit);
     204           0 :   NS_ENSURE_SUCCESS(rv, rv);
     205           0 :   if (aLimit > existingLimit) {
     206           0 :     rv = mPool->SetIdleThreadLimit(aLimit);
     207           0 :     NS_ENSURE_SUCCESS(rv, rv);
     208             :   }
     209             : 
     210           0 :   return NS_OK;
     211             : }
     212             : 
     213             : static already_AddRefed<nsIThreadPool>
     214           0 : CreateThreadPool(const nsCString& aName)
     215             : {
     216             :   nsresult rv;
     217           0 :   nsCOMPtr<nsIThreadPool> pool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
     218           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
     219             : 
     220           0 :   rv = pool->SetName(aName);
     221           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
     222             : 
     223           0 :   rv = pool->SetThreadStackSize(SharedThreadPool::kStackSize);
     224           0 :   NS_ENSURE_SUCCESS(rv, nullptr);
     225             : 
     226             : #ifdef XP_WIN
     227             :   // Ensure MSCOM is initialized on the thread pools threads.
     228             :   nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener();
     229             :   rv = pool->SetListener(listener);
     230             :   NS_ENSURE_SUCCESS(rv, nullptr);
     231             : #endif
     232             : 
     233           0 :   return pool.forget();
     234             : }
     235             : 
     236             : } // namespace mozilla

Generated by: LCOV version 1.13