Bug 1867190 - Initialise the PHC allocate delay later r=glandium
[gecko.git] / xpcom / threads / SharedThreadPool.cpp
bloba2de1c44959e8a9e56e500a670f596d7e44603da
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 "mozilla/SharedThreadPool.h"
8 #include "mozilla/Monitor.h"
9 #include "mozilla/ReentrantMonitor.h"
10 #include "mozilla/Services.h"
11 #include "mozilla/SpinEventLoopUntil.h"
12 #include "mozilla/StaticPtr.h"
13 #include "nsTHashMap.h"
14 #include "nsXPCOMCIDInternal.h"
15 #include "nsComponentManagerUtils.h"
16 #include "nsIObserver.h"
17 #include "nsIObserverService.h"
18 #include "nsIThreadManager.h"
19 #include "nsThreadPool.h"
21 namespace mozilla {
23 // Created and destroyed on the main thread.
24 static StaticAutoPtr<ReentrantMonitor> sMonitor;
26 // Hashtable, maps thread pool name to SharedThreadPool instance.
27 // Modified only on the main thread.
28 static StaticAutoPtr<nsTHashMap<nsCStringHashKey, SharedThreadPool*>> sPools;
30 static already_AddRefed<nsIThreadPool> CreateThreadPool(const nsCString& aName);
32 class SharedThreadPoolShutdownObserver : public nsIObserver {
33 public:
34 NS_DECL_ISUPPORTS
35 NS_DECL_NSIOBSERVER
36 protected:
37 virtual ~SharedThreadPoolShutdownObserver() = default;
40 NS_IMPL_ISUPPORTS(SharedThreadPoolShutdownObserver, nsIObserver, nsISupports)
42 NS_IMETHODIMP
43 SharedThreadPoolShutdownObserver::Observe(nsISupports* aSubject,
44 const char* aTopic,
45 const char16_t* aData) {
46 MOZ_RELEASE_ASSERT(!strcmp(aTopic, "xpcom-shutdown-threads"));
47 #ifdef EARLY_BETA_OR_EARLIER
49 ReentrantMonitorAutoEnter mon(*sMonitor);
50 if (!sPools->IsEmpty()) {
51 nsAutoCString str;
52 for (const auto& key : sPools->Keys()) {
53 str.AppendPrintf("\"%s\" ", nsAutoCString(key).get());
55 printf_stderr(
56 "SharedThreadPool in xpcom-shutdown-threads. Waiting for "
57 "pools %s\n",
58 str.get());
61 #endif
62 SharedThreadPool::SpinUntilEmpty();
63 sMonitor = nullptr;
64 sPools = nullptr;
65 return NS_OK;
68 void SharedThreadPool::InitStatics() {
69 MOZ_ASSERT(NS_IsMainThread());
70 MOZ_ASSERT(!sMonitor && !sPools);
71 sMonitor = new ReentrantMonitor("SharedThreadPool");
72 sPools = new nsTHashMap<nsCStringHashKey, SharedThreadPool*>();
73 nsCOMPtr<nsIObserverService> obsService =
74 mozilla::services::GetObserverService();
75 nsCOMPtr<nsIObserver> obs = new SharedThreadPoolShutdownObserver();
76 obsService->AddObserver(obs, "xpcom-shutdown-threads", false);
79 /* static */
80 bool SharedThreadPool::IsEmpty() {
81 ReentrantMonitorAutoEnter mon(*sMonitor);
82 return !sPools->Count();
85 /* static */
86 void SharedThreadPool::SpinUntilEmpty() {
87 MOZ_ASSERT(NS_IsMainThread());
88 SpinEventLoopUntil("SharedThreadPool::SpinUntilEmpty"_ns, []() -> bool {
89 sMonitor->AssertNotCurrentThreadIn();
90 return IsEmpty();
91 });
94 already_AddRefed<SharedThreadPool> SharedThreadPool::Get(
95 const nsCString& aName, uint32_t aThreadLimit) {
96 MOZ_ASSERT(sMonitor && sPools);
97 ReentrantMonitorAutoEnter mon(*sMonitor);
98 RefPtr<SharedThreadPool> pool;
100 return sPools->WithEntryHandle(
101 aName, [&](auto&& entry) -> already_AddRefed<SharedThreadPool> {
102 if (entry) {
103 pool = entry.Data();
104 if (NS_FAILED(pool->EnsureThreadLimitIsAtLeast(aThreadLimit))) {
105 NS_WARNING("Failed to set limits on thread pool");
107 } else {
108 nsCOMPtr<nsIThreadPool> threadPool(CreateThreadPool(aName));
109 if (NS_WARN_IF(!threadPool)) {
110 sPools->Remove(aName); // XXX entry.Remove()
111 return nullptr;
113 pool = new SharedThreadPool(aName, threadPool);
115 // Set the thread and idle limits. Note that we don't rely on the
116 // EnsureThreadLimitIsAtLeast() call below, as the default thread
117 // limit is 4, and if aThreadLimit is less than 4 we'll end up with a
118 // pool with 4 threads rather than what we expected; so we'll have
119 // unexpected behaviour.
120 nsresult rv = pool->SetThreadLimit(aThreadLimit);
121 if (NS_WARN_IF(NS_FAILED(rv))) {
122 sPools->Remove(aName); // XXX entry.Remove()
123 return nullptr;
126 rv = pool->SetIdleThreadLimit(aThreadLimit);
127 if (NS_WARN_IF(NS_FAILED(rv))) {
128 sPools->Remove(aName); // XXX entry.Remove()
129 return nullptr;
132 entry.Insert(pool.get());
135 return pool.forget();
139 NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::AddRef(void) {
140 MOZ_ASSERT(sMonitor);
141 ReentrantMonitorAutoEnter mon(*sMonitor);
142 MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
143 nsrefcnt count = ++mRefCnt;
144 NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this));
145 return count;
148 NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::Release(void) {
149 MOZ_ASSERT(sMonitor);
150 ReentrantMonitorAutoEnter mon(*sMonitor);
151 nsrefcnt count = --mRefCnt;
152 NS_LOG_RELEASE(this, count, "SharedThreadPool");
153 if (count) {
154 return count;
157 // Remove SharedThreadPool from table of pools.
158 sPools->Remove(mName);
159 MOZ_ASSERT(!sPools->Get(mName));
161 // Dispatch an event to the main thread to call Shutdown() on
162 // the nsIThreadPool. The Runnable here will add a refcount to the pool,
163 // and when the Runnable releases the nsIThreadPool it will be deleted.
164 NS_DispatchToMainThread(NewRunnableMethod("nsIThreadPool::Shutdown", mPool,
165 &nsIThreadPool::Shutdown));
167 // Stabilize refcount, so that if something in the dtor QIs, it won't explode.
168 mRefCnt = 1;
169 delete this;
170 return 0;
173 NS_IMPL_QUERY_INTERFACE(SharedThreadPool, nsIThreadPool, nsIEventTarget)
175 SharedThreadPool::SharedThreadPool(const nsCString& aName, nsIThreadPool* aPool)
176 : mName(aName), mPool(aPool), mRefCnt(0) {}
178 SharedThreadPool::~SharedThreadPool() = default;
180 nsresult SharedThreadPool::EnsureThreadLimitIsAtLeast(uint32_t aLimit) {
181 // We limit the number of threads that we use. Note that we
182 // set the thread limit to the same as the idle limit so that we're not
183 // constantly creating and destroying threads (see Bug 881954). When the
184 // thread pool threads shutdown they dispatch an event to the main thread
185 // to call nsIThread::Shutdown(), and if we're very busy that can take a
186 // while to run, and we end up with dozens of extra threads. Note that
187 // threads that are idle for 60 seconds are shutdown naturally.
188 uint32_t existingLimit = 0;
189 nsresult rv;
191 rv = mPool->GetThreadLimit(&existingLimit);
192 NS_ENSURE_SUCCESS(rv, rv);
193 if (aLimit > existingLimit) {
194 rv = mPool->SetThreadLimit(aLimit);
195 NS_ENSURE_SUCCESS(rv, rv);
198 rv = mPool->GetIdleThreadLimit(&existingLimit);
199 NS_ENSURE_SUCCESS(rv, rv);
200 if (aLimit > existingLimit) {
201 rv = mPool->SetIdleThreadLimit(aLimit);
202 NS_ENSURE_SUCCESS(rv, rv);
205 return NS_OK;
208 static already_AddRefed<nsIThreadPool> CreateThreadPool(
209 const nsCString& aName) {
210 nsCOMPtr<nsIThreadPool> pool = new nsThreadPool();
212 nsresult rv = pool->SetName(aName);
213 NS_ENSURE_SUCCESS(rv, nullptr);
215 rv = pool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize);
216 NS_ENSURE_SUCCESS(rv, nullptr);
218 return pool.forget();
221 } // namespace mozilla