Coverage Report

Created: 2018-09-25 14:53

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