Bug 1667008 [wpt PR 25754] - [css-flex] Change some test expectations for image flex...
[gecko.git] / xpcom / threads / SharedThreadPool.cpp
blob5f77a64e010cd44a57f75317e36a32ac433411bb
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/StaticPtr.h"
12 #include "nsDataHashtable.h"
13 #include "nsXPCOMCIDInternal.h"
14 #include "nsComponentManagerUtils.h"
15 #include "nsIObserver.h"
16 #include "nsIObserverService.h"
17 #include "nsIThreadManager.h"
18 #include "nsThreadPool.h"
19 #ifdef XP_WIN
20 # include "ThreadPoolCOMListener.h"
21 #endif
23 namespace mozilla {
25 // Created and destroyed on the main thread.
26 static StaticAutoPtr<ReentrantMonitor> sMonitor;
28 // Hashtable, maps thread pool name to SharedThreadPool instance.
29 // Modified only on the main thread.
30 static StaticAutoPtr<nsDataHashtable<nsCStringHashKey, SharedThreadPool*>>
31 sPools;
33 static already_AddRefed<nsIThreadPool> CreateThreadPool(const nsCString& aName);
35 class SharedThreadPoolShutdownObserver : public nsIObserver {
36 public:
37 NS_DECL_ISUPPORTS
38 NS_DECL_NSIOBSERVER
39 protected:
40 virtual ~SharedThreadPoolShutdownObserver() = default;
43 NS_IMPL_ISUPPORTS(SharedThreadPoolShutdownObserver, nsIObserver, nsISupports)
45 NS_IMETHODIMP
46 SharedThreadPoolShutdownObserver::Observe(nsISupports* aSubject,
47 const char* aTopic,
48 const char16_t* aData) {
49 MOZ_RELEASE_ASSERT(!strcmp(aTopic, "xpcom-shutdown-threads"));
50 #ifdef EARLY_BETA_OR_EARLIER
52 ReentrantMonitorAutoEnter mon(*sMonitor);
53 if (!sPools->Iter().Done()) {
54 nsAutoCString str;
55 for (auto i = sPools->Iter(); !i.Done(); i.Next()) {
56 str.AppendPrintf("\"%s\" ", nsAutoCString(i.Key()).get());
58 printf_stderr(
59 "SharedThreadPool in xpcom-shutdown-threads. Waiting for "
60 "pools %s\n",
61 str.get());
64 #endif
65 SharedThreadPool::SpinUntilEmpty();
66 sMonitor = nullptr;
67 sPools = nullptr;
68 return NS_OK;
71 void SharedThreadPool::InitStatics() {
72 MOZ_ASSERT(NS_IsMainThread());
73 MOZ_ASSERT(!sMonitor && !sPools);
74 sMonitor = new ReentrantMonitor("SharedThreadPool");
75 sPools = new nsDataHashtable<nsCStringHashKey, SharedThreadPool*>();
76 nsCOMPtr<nsIObserverService> obsService =
77 mozilla::services::GetObserverService();
78 nsCOMPtr<nsIObserver> obs = new SharedThreadPoolShutdownObserver();
79 obsService->AddObserver(obs, "xpcom-shutdown-threads", false);
82 /* static */
83 bool SharedThreadPool::IsEmpty() {
84 ReentrantMonitorAutoEnter mon(*sMonitor);
85 return !sPools->Count();
88 /* static */
89 void SharedThreadPool::SpinUntilEmpty() {
90 MOZ_ASSERT(NS_IsMainThread());
91 SpinEventLoopUntil([]() -> bool {
92 sMonitor->AssertNotCurrentThreadIn();
93 return IsEmpty();
94 });
97 already_AddRefed<SharedThreadPool> SharedThreadPool::Get(
98 const nsCString& aName, uint32_t aThreadLimit) {
99 MOZ_ASSERT(sMonitor && sPools);
100 ReentrantMonitorAutoEnter mon(*sMonitor);
101 RefPtr<SharedThreadPool> pool;
102 nsresult rv;
104 if (auto entry = sPools->LookupForAdd(aName)) {
105 pool = entry.Data();
106 if (NS_FAILED(pool->EnsureThreadLimitIsAtLeast(aThreadLimit))) {
107 NS_WARNING("Failed to set limits on thread pool");
109 } else {
110 nsCOMPtr<nsIThreadPool> threadPool(CreateThreadPool(aName));
111 if (NS_WARN_IF(!threadPool)) {
112 sPools->Remove(aName); // XXX entry.Remove()
113 return nullptr;
115 pool = new SharedThreadPool(aName, threadPool);
117 // Set the thread and idle limits. Note that we don't rely on the
118 // EnsureThreadLimitIsAtLeast() call below, as the default thread limit
119 // is 4, and if aThreadLimit is less than 4 we'll end up with a pool
120 // with 4 threads rather than what we expected; so we'll have unexpected
121 // behaviour.
122 rv = pool->SetThreadLimit(aThreadLimit);
123 if (NS_WARN_IF(NS_FAILED(rv))) {
124 sPools->Remove(aName); // XXX entry.Remove()
125 return nullptr;
128 rv = pool->SetIdleThreadLimit(aThreadLimit);
129 if (NS_WARN_IF(NS_FAILED(rv))) {
130 sPools->Remove(aName); // XXX entry.Remove()
131 return nullptr;
134 entry.OrInsert([pool]() { return pool.get(); });
137 return pool.forget();
140 NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::AddRef(void) {
141 MOZ_ASSERT(sMonitor);
142 ReentrantMonitorAutoEnter mon(*sMonitor);
143 MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
144 nsrefcnt count = ++mRefCnt;
145 NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this));
146 return count;
149 NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::Release(void) {
150 MOZ_ASSERT(sMonitor);
151 ReentrantMonitorAutoEnter mon(*sMonitor);
152 nsrefcnt count = --mRefCnt;
153 NS_LOG_RELEASE(this, count, "SharedThreadPool");
154 if (count) {
155 return count;
158 // Remove SharedThreadPool from table of pools.
159 sPools->Remove(mName);
160 MOZ_ASSERT(!sPools->Get(mName));
162 // Dispatch an event to the main thread to call Shutdown() on
163 // the nsIThreadPool. The Runnable here will add a refcount to the pool,
164 // and when the Runnable releases the nsIThreadPool it will be deleted.
165 NS_DispatchToMainThread(NewRunnableMethod("nsIThreadPool::Shutdown", mPool,
166 &nsIThreadPool::Shutdown));
168 // Stabilize refcount, so that if something in the dtor QIs, it won't explode.
169 mRefCnt = 1;
170 delete this;
171 return 0;
174 NS_IMPL_QUERY_INTERFACE(SharedThreadPool, nsIThreadPool, nsIEventTarget)
176 SharedThreadPool::SharedThreadPool(const nsCString& aName, nsIThreadPool* aPool)
177 : mName(aName), mPool(aPool), mRefCnt(0) {
178 mEventTarget = aPool;
181 SharedThreadPool::~SharedThreadPool() = default;
183 nsresult SharedThreadPool::EnsureThreadLimitIsAtLeast(uint32_t aLimit) {
184 // We limit the number of threads that we use. Note that we
185 // set the thread limit to the same as the idle limit so that we're not
186 // constantly creating and destroying threads (see Bug 881954). When the
187 // thread pool threads shutdown they dispatch an event to the main thread
188 // to call nsIThread::Shutdown(), and if we're very busy that can take a
189 // while to run, and we end up with dozens of extra threads. Note that
190 // threads that are idle for 60 seconds are shutdown naturally.
191 uint32_t existingLimit = 0;
192 nsresult rv;
194 rv = mPool->GetThreadLimit(&existingLimit);
195 NS_ENSURE_SUCCESS(rv, rv);
196 if (aLimit > existingLimit) {
197 rv = mPool->SetThreadLimit(aLimit);
198 NS_ENSURE_SUCCESS(rv, rv);
201 rv = mPool->GetIdleThreadLimit(&existingLimit);
202 NS_ENSURE_SUCCESS(rv, rv);
203 if (aLimit > existingLimit) {
204 rv = mPool->SetIdleThreadLimit(aLimit);
205 NS_ENSURE_SUCCESS(rv, rv);
208 return NS_OK;
211 static already_AddRefed<nsIThreadPool> CreateThreadPool(
212 const nsCString& aName) {
213 nsCOMPtr<nsIThreadPool> pool = new nsThreadPool();
215 nsresult rv = pool->SetName(aName);
216 NS_ENSURE_SUCCESS(rv, nullptr);
218 rv = pool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize);
219 NS_ENSURE_SUCCESS(rv, nullptr);
221 #ifdef XP_WIN
222 // Ensure MSCOM is initialized on the thread pools threads.
223 nsCOMPtr<nsIThreadPoolListener> listener = new MSCOMInitThreadPoolListener();
224 rv = pool->SetListener(listener);
225 NS_ENSURE_SUCCESS(rv, nullptr);
226 #endif
228 return pool.forget();
231 } // namespace mozilla