Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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