/src/mozilla-central/xpcom/threads/nsThreadManager.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 "nsThreadManager.h" |
8 | | #include "nsThread.h" |
9 | | #include "nsThreadUtils.h" |
10 | | #include "nsIClassInfoImpl.h" |
11 | | #include "nsTArray.h" |
12 | | #include "nsAutoPtr.h" |
13 | | #include "nsXULAppAPI.h" |
14 | | #include "LabeledEventQueue.h" |
15 | | #include "MainThreadQueue.h" |
16 | | #include "mozilla/AbstractThread.h" |
17 | | #include "mozilla/ClearOnShutdown.h" |
18 | | #include "mozilla/EventQueue.h" |
19 | | #include "mozilla/Preferences.h" |
20 | | #include "mozilla/Scheduler.h" |
21 | | #include "mozilla/SystemGroup.h" |
22 | | #include "mozilla/StaticPtr.h" |
23 | | #include "mozilla/ThreadEventQueue.h" |
24 | | #include "mozilla/ThreadLocal.h" |
25 | | #include "PrioritizedEventQueue.h" |
26 | | #ifdef MOZ_CANARY |
27 | | #include <fcntl.h> |
28 | | #include <unistd.h> |
29 | | #endif |
30 | | |
31 | | #include "MainThreadIdlePeriod.h" |
32 | | #include "InputEventStatistics.h" |
33 | | |
34 | | using namespace mozilla; |
35 | | |
36 | | static MOZ_THREAD_LOCAL(bool) sTLSIsMainThread; |
37 | | static MOZ_THREAD_LOCAL(PRThread*) gTlsCurrentVirtualThread; |
38 | | |
39 | | bool |
40 | | NS_IsMainThreadTLSInitialized() |
41 | 0 | { |
42 | 0 | return sTLSIsMainThread.initialized(); |
43 | 0 | } |
44 | | |
45 | | bool |
46 | | NS_IsMainThread() |
47 | 50.8M | { |
48 | 50.8M | return sTLSIsMainThread.get(); |
49 | 50.8M | } |
50 | | |
51 | | void |
52 | | NS_SetMainThread() |
53 | 9 | { |
54 | 9 | if (!sTLSIsMainThread.init()) { |
55 | 0 | MOZ_CRASH(); |
56 | 0 | } |
57 | 9 | sTLSIsMainThread.set(true); |
58 | 9 | MOZ_ASSERT(NS_IsMainThread()); |
59 | 9 | } |
60 | | |
61 | | void |
62 | | NS_SetMainThread(PRThread* aVirtualThread) |
63 | 0 | { |
64 | 0 | MOZ_ASSERT(Scheduler::IsCooperativeThread()); |
65 | 0 |
|
66 | 0 | MOZ_ASSERT(!gTlsCurrentVirtualThread.get()); |
67 | 0 | gTlsCurrentVirtualThread.set(aVirtualThread); |
68 | 0 | NS_SetMainThread(); |
69 | 0 | } |
70 | | |
71 | | void |
72 | | NS_UnsetMainThread() |
73 | 0 | { |
74 | 0 | MOZ_ASSERT(Scheduler::IsCooperativeThread()); |
75 | 0 |
|
76 | 0 | sTLSIsMainThread.set(false); |
77 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
78 | 0 | gTlsCurrentVirtualThread.set(nullptr); |
79 | 0 | } |
80 | | |
81 | | #ifdef DEBUG |
82 | | |
83 | | namespace mozilla { |
84 | | |
85 | | void |
86 | | AssertIsOnMainThread() |
87 | | { |
88 | | MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); |
89 | | } |
90 | | |
91 | | } // mozilla namespace |
92 | | |
93 | | #endif |
94 | | |
95 | | typedef nsTArray<NotNull<RefPtr<nsThread>>> nsThreadArray; |
96 | | |
97 | | static bool sShutdownComplete; |
98 | | |
99 | | //----------------------------------------------------------------------------- |
100 | | |
101 | | /* static */ void |
102 | | nsThreadManager::ReleaseThread(void* aData) |
103 | 0 | { |
104 | 0 | if (sShutdownComplete) { |
105 | 0 | // We've already completed shutdown and released the references to all or |
106 | 0 | // our TLS wrappers. Don't try to release them again. |
107 | 0 | return; |
108 | 0 | } |
109 | 0 | |
110 | 0 | auto* thread = static_cast<nsThread*>(aData); |
111 | 0 |
|
112 | 0 | get().UnregisterCurrentThread(*thread, true); |
113 | 0 |
|
114 | 0 | if (thread->mHasTLSEntry) { |
115 | 0 | thread->mHasTLSEntry = false; |
116 | 0 | thread->Release(); |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | | // statically allocated instance |
121 | | NS_IMETHODIMP_(MozExternalRefCountType) |
122 | | nsThreadManager::AddRef() |
123 | 0 | { |
124 | 0 | return 2; |
125 | 0 | } |
126 | | NS_IMETHODIMP_(MozExternalRefCountType) |
127 | | nsThreadManager::Release() |
128 | 0 | { |
129 | 0 | return 1; |
130 | 0 | } |
131 | | NS_IMPL_CLASSINFO(nsThreadManager, nullptr, |
132 | | nsIClassInfo::THREADSAFE | nsIClassInfo::SINGLETON, |
133 | | NS_THREADMANAGER_CID) |
134 | | NS_IMPL_QUERY_INTERFACE_CI(nsThreadManager, nsIThreadManager) |
135 | | NS_IMPL_CI_INTERFACE_GETTER(nsThreadManager, nsIThreadManager) |
136 | | |
137 | | namespace { |
138 | | |
139 | | // Simple observer to monitor the beginning of the shutdown. |
140 | | class ShutdownObserveHelper final : public nsIObserver |
141 | | , public nsSupportsWeakReference |
142 | | { |
143 | | public: |
144 | | NS_DECL_ISUPPORTS |
145 | | |
146 | | static nsresult |
147 | | Create(ShutdownObserveHelper** aObserver) |
148 | 3 | { |
149 | 3 | MOZ_ASSERT(aObserver); |
150 | 3 | |
151 | 3 | RefPtr<ShutdownObserveHelper> observer = new ShutdownObserveHelper(); |
152 | 3 | |
153 | 3 | nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
154 | 3 | if (NS_WARN_IF(!obs)) { |
155 | 0 | return NS_ERROR_FAILURE; |
156 | 0 | } |
157 | 3 | |
158 | 3 | nsresult rv = obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true); |
159 | 3 | if (NS_WARN_IF(NS_FAILED(rv))) { |
160 | 0 | return rv; |
161 | 0 | } |
162 | 3 | |
163 | 3 | rv = obs->AddObserver(observer, "content-child-will-shutdown", true); |
164 | 3 | if (NS_WARN_IF(NS_FAILED(rv))) { |
165 | 0 | return rv; |
166 | 0 | } |
167 | 3 | |
168 | 3 | observer.forget(aObserver); |
169 | 3 | return NS_OK; |
170 | 3 | } |
171 | | |
172 | | NS_IMETHOD |
173 | | Observe(nsISupports* aSubject, const char* aTopic, |
174 | | const char16_t* aData) override |
175 | 0 | { |
176 | 0 | if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) || |
177 | 0 | !strcmp(aTopic, "content-child-will-shutdown")) { |
178 | 0 | mShuttingDown = true; |
179 | 0 | return NS_OK; |
180 | 0 | } |
181 | 0 | |
182 | 0 | return NS_OK; |
183 | 0 | } |
184 | | |
185 | | bool |
186 | | ShuttingDown() const |
187 | 0 | { |
188 | 0 | return mShuttingDown; |
189 | 0 | } |
190 | | |
191 | | private: |
192 | | explicit ShutdownObserveHelper() |
193 | | : mShuttingDown(false) |
194 | 3 | {} |
195 | | |
196 | 0 | ~ShutdownObserveHelper() = default; |
197 | | |
198 | | bool mShuttingDown; |
199 | | }; |
200 | | |
201 | 6 | NS_INTERFACE_MAP_BEGIN(ShutdownObserveHelper) |
202 | 6 | NS_INTERFACE_MAP_ENTRY(nsIObserver) |
203 | 6 | NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) |
204 | 6 | NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) |
205 | 0 | NS_INTERFACE_MAP_END |
206 | | |
207 | | NS_IMPL_ADDREF(ShutdownObserveHelper) |
208 | | NS_IMPL_RELEASE(ShutdownObserveHelper) |
209 | | |
210 | | StaticRefPtr<ShutdownObserveHelper> gShutdownObserveHelper; |
211 | | |
212 | | } // anonymous |
213 | | |
214 | | //----------------------------------------------------------------------------- |
215 | | |
216 | | /*static*/ nsThreadManager& |
217 | | nsThreadManager::get() |
218 | 466 | { |
219 | 466 | static nsThreadManager sInstance; |
220 | 466 | return sInstance; |
221 | 466 | } |
222 | | |
223 | | /* static */ void |
224 | | nsThreadManager::InitializeShutdownObserver() |
225 | 3 | { |
226 | 3 | MOZ_ASSERT(!gShutdownObserveHelper); |
227 | 3 | |
228 | 3 | RefPtr<ShutdownObserveHelper> observer; |
229 | 3 | nsresult rv = ShutdownObserveHelper::Create(getter_AddRefs(observer)); |
230 | 3 | if (NS_WARN_IF(NS_FAILED(rv))) { |
231 | 0 | return; |
232 | 0 | } |
233 | 3 | |
234 | 3 | gShutdownObserveHelper = observer; |
235 | 3 | ClearOnShutdown(&gShutdownObserveHelper); |
236 | 3 | } |
237 | | |
238 | | nsresult |
239 | | nsThreadManager::Init() |
240 | 3 | { |
241 | 3 | // Child processes need to initialize the thread manager before they |
242 | 3 | // initialize XPCOM in order to set up the crash reporter. This leads to |
243 | 3 | // situations where we get initialized twice. |
244 | 3 | if (mInitialized) { |
245 | 0 | return NS_OK; |
246 | 0 | } |
247 | 3 | |
248 | 3 | if (!gTlsCurrentVirtualThread.init()) { |
249 | 0 | return NS_ERROR_UNEXPECTED; |
250 | 0 | } |
251 | 3 | |
252 | 3 | Scheduler::EventLoopActivation::Init(); |
253 | 3 | |
254 | 3 | if (PR_NewThreadPrivateIndex(&mCurThreadIndex, ReleaseThread) == PR_FAILURE) { |
255 | 0 | return NS_ERROR_FAILURE; |
256 | 0 | } |
257 | 3 | |
258 | 3 | |
259 | | #ifdef MOZ_CANARY |
260 | | const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK; |
261 | | const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; |
262 | | char* env_var_flag = getenv("MOZ_KILL_CANARIES"); |
263 | | sCanaryOutputFD = |
264 | | env_var_flag ? (env_var_flag[0] ? open(env_var_flag, flags, mode) : |
265 | | STDERR_FILENO) : |
266 | | 0; |
267 | | #endif |
268 | | |
269 | 3 | nsCOMPtr<nsIIdlePeriod> idlePeriod = new MainThreadIdlePeriod(); |
270 | 3 | |
271 | 3 | bool startScheduler = false; |
272 | 3 | if (XRE_IsContentProcess() && Scheduler::IsSchedulerEnabled()) { |
273 | 0 | mMainThread = Scheduler::Init(idlePeriod); |
274 | 0 | startScheduler = true; |
275 | 3 | } else { |
276 | 3 | if (XRE_IsContentProcess() && Scheduler::UseMultipleQueues()) { |
277 | 0 | mMainThread = CreateMainThread<ThreadEventQueue<PrioritizedEventQueue<LabeledEventQueue>>, LabeledEventQueue>(idlePeriod); |
278 | 3 | } else { |
279 | 3 | mMainThread = CreateMainThread<ThreadEventQueue<PrioritizedEventQueue<EventQueue>>, EventQueue>(idlePeriod); |
280 | 3 | } |
281 | 3 | } |
282 | 3 | |
283 | 3 | nsresult rv = mMainThread->InitCurrentThread(); |
284 | 3 | if (NS_FAILED(rv)) { |
285 | 0 | mMainThread = nullptr; |
286 | 0 | return rv; |
287 | 0 | } |
288 | 3 | |
289 | 3 | // We need to keep a pointer to the current thread, so we can satisfy |
290 | 3 | // GetIsMainThread calls that occur post-Shutdown. |
291 | 3 | mMainThread->GetPRThread(&mMainPRThread); |
292 | 3 | |
293 | 3 | // Init AbstractThread. |
294 | 3 | AbstractThread::InitTLS(); |
295 | 3 | AbstractThread::InitMainThread(); |
296 | 3 | |
297 | 3 | mInitialized = true; |
298 | 3 | |
299 | 3 | if (startScheduler) { |
300 | 0 | Scheduler::Start(); |
301 | 0 | } |
302 | 3 | return NS_OK; |
303 | 3 | } |
304 | | |
305 | | void |
306 | | nsThreadManager::Shutdown() |
307 | 0 | { |
308 | 0 | MOZ_ASSERT(NS_IsMainThread(), "shutdown not called from main thread"); |
309 | 0 |
|
310 | 0 | // Prevent further access to the thread manager (no more new threads!) |
311 | 0 | // |
312 | 0 | // What happens if shutdown happens before NewThread completes? |
313 | 0 | // We Shutdown() the new thread, and return error if we've started Shutdown |
314 | 0 | // between when NewThread started, and when the thread finished initializing |
315 | 0 | // and registering with ThreadManager. |
316 | 0 | // |
317 | 0 | mInitialized = false; |
318 | 0 |
|
319 | 0 | // Empty the main thread event queue before we begin shutting down threads. |
320 | 0 | NS_ProcessPendingEvents(mMainThread); |
321 | 0 |
|
322 | 0 | { |
323 | 0 | // We gather the threads from the hashtable into a list, so that we avoid |
324 | 0 | // holding the hashtable lock while calling nsIThread::Shutdown. |
325 | 0 | nsThreadArray threads; |
326 | 0 | { |
327 | 0 | OffTheBooksMutexAutoLock lock(mLock); |
328 | 0 | for (auto iter = mThreadsByPRThread.Iter(); !iter.Done(); iter.Next()) { |
329 | 0 | RefPtr<nsThread>& thread = iter.Data(); |
330 | 0 | threads.AppendElement(WrapNotNull(thread)); |
331 | 0 | iter.Remove(); |
332 | 0 | } |
333 | 0 | } |
334 | 0 |
|
335 | 0 | // It's tempting to walk the list of threads here and tell them each to stop |
336 | 0 | // accepting new events, but that could lead to badness if one of those |
337 | 0 | // threads is stuck waiting for a response from another thread. To do it |
338 | 0 | // right, we'd need some way to interrupt the threads. |
339 | 0 | // |
340 | 0 | // Instead, we process events on the current thread while waiting for threads |
341 | 0 | // to shutdown. This means that we have to preserve a mostly functioning |
342 | 0 | // world until such time as the threads exit. |
343 | 0 |
|
344 | 0 | // Shutdown all threads that require it (join with threads that we created). |
345 | 0 | for (uint32_t i = 0; i < threads.Length(); ++i) { |
346 | 0 | NotNull<nsThread*> thread = threads[i]; |
347 | 0 | if (thread->ShutdownRequired()) { |
348 | 0 | thread->Shutdown(); |
349 | 0 | } |
350 | 0 | } |
351 | 0 | } |
352 | 0 |
|
353 | 0 | // NB: It's possible that there are events in the queue that want to *start* |
354 | 0 | // an asynchronous shutdown. But we have already shutdown the threads above, |
355 | 0 | // so there's no need to worry about them. We only have to wait for all |
356 | 0 | // in-flight asynchronous thread shutdowns to complete. |
357 | 0 | mMainThread->WaitForAllAsynchronousShutdowns(); |
358 | 0 |
|
359 | 0 | // In case there are any more events somehow... |
360 | 0 | NS_ProcessPendingEvents(mMainThread); |
361 | 0 |
|
362 | 0 | // There are no more background threads at this point. |
363 | 0 |
|
364 | 0 | // Clear the table of threads. |
365 | 0 | { |
366 | 0 | OffTheBooksMutexAutoLock lock(mLock); |
367 | 0 | mThreadsByPRThread.Clear(); |
368 | 0 | } |
369 | 0 |
|
370 | 0 | // Normally thread shutdown clears the observer for the thread, but since the |
371 | 0 | // main thread is special we do it manually here after we're sure all events |
372 | 0 | // have been processed. |
373 | 0 | mMainThread->SetObserver(nullptr); |
374 | 0 |
|
375 | 0 | // Release main thread object. |
376 | 0 | mMainThread = nullptr; |
377 | 0 |
|
378 | 0 | // Remove the TLS entry for the main thread. |
379 | 0 | PR_SetThreadPrivate(mCurThreadIndex, nullptr); |
380 | 0 |
|
381 | 0 | { |
382 | 0 | // Cleanup the last references to any threads which haven't shut down yet. |
383 | 0 | nsTArray<RefPtr<nsThread>> threads; |
384 | 0 | for (auto* thread : nsThread::Enumerate()) { |
385 | 0 | if (thread->mHasTLSEntry) { |
386 | 0 | threads.AppendElement(dont_AddRef(thread)); |
387 | 0 | thread->mHasTLSEntry = false; |
388 | 0 | } |
389 | 0 | } |
390 | 0 | } |
391 | 0 |
|
392 | 0 | // xpcshell tests sometimes leak the main thread. They don't enable leak |
393 | 0 | // checking, so that doesn't cause the test to fail, but leaving the entry in |
394 | 0 | // the thread list triggers an assertion, which does. |
395 | 0 | nsThread::ClearThreadList(); |
396 | 0 |
|
397 | 0 | sShutdownComplete = true; |
398 | 0 | } |
399 | | |
400 | | void |
401 | | nsThreadManager::RegisterCurrentThread(nsThread& aThread) |
402 | 46 | { |
403 | 46 | MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread"); |
404 | 46 | |
405 | 46 | OffTheBooksMutexAutoLock lock(mLock); |
406 | 46 | |
407 | 46 | ++mCurrentNumberOfThreads; |
408 | 46 | if (mCurrentNumberOfThreads > mHighestNumberOfThreads) { |
409 | 46 | mHighestNumberOfThreads = mCurrentNumberOfThreads; |
410 | 46 | } |
411 | 46 | |
412 | 46 | mThreadsByPRThread.Put(aThread.GetPRThread(), &aThread); // XXX check OOM? |
413 | 46 | |
414 | 46 | aThread.AddRef(); // for TLS entry |
415 | 46 | aThread.mHasTLSEntry = true; |
416 | 46 | PR_SetThreadPrivate(mCurThreadIndex, &aThread); |
417 | 46 | } |
418 | | |
419 | | void |
420 | | nsThreadManager::UnregisterCurrentThread(nsThread& aThread, bool aIfExists) |
421 | 0 | { |
422 | 0 | { |
423 | 0 | OffTheBooksMutexAutoLock lock(mLock); |
424 | 0 |
|
425 | 0 | if (aIfExists && !mThreadsByPRThread.GetWeak(aThread.GetPRThread())) { |
426 | 0 | return; |
427 | 0 | } |
428 | 0 | |
429 | 0 | MOZ_ASSERT(aThread.GetPRThread() == PR_GetCurrentThread(), "bad aThread"); |
430 | 0 |
|
431 | 0 | --mCurrentNumberOfThreads; |
432 | 0 | mThreadsByPRThread.Remove(aThread.GetPRThread()); |
433 | 0 | } |
434 | 0 |
|
435 | 0 | PR_SetThreadPrivate(mCurThreadIndex, nullptr); |
436 | 0 | // Ref-count balanced via ReleaseThread |
437 | 0 | } |
438 | | |
439 | | nsThread* |
440 | | nsThreadManager::CreateCurrentThread(SynchronizedEventQueue* aQueue, |
441 | | nsThread::MainThreadFlag aMainThread) |
442 | 0 | { |
443 | 0 | // Make sure we don't have an nsThread yet. |
444 | 0 | MOZ_ASSERT(!PR_GetThreadPrivate(mCurThreadIndex)); |
445 | 0 |
|
446 | 0 | if (!mInitialized) { |
447 | 0 | return nullptr; |
448 | 0 | } |
449 | 0 | |
450 | 0 | // OK, that's fine. We'll dynamically create one :-) |
451 | 0 | RefPtr<nsThread> thread = new nsThread(WrapNotNull(aQueue), aMainThread, 0); |
452 | 0 | if (!thread || NS_FAILED(thread->InitCurrentThread())) { |
453 | 0 | return nullptr; |
454 | 0 | } |
455 | 0 | |
456 | 0 | return thread.get(); // reference held in TLS |
457 | 0 | } |
458 | | |
459 | | nsThread* |
460 | | nsThreadManager::GetCurrentThread() |
461 | 258 | { |
462 | 258 | // read thread local storage |
463 | 258 | void* data = PR_GetThreadPrivate(mCurThreadIndex); |
464 | 258 | if (data) { |
465 | 219 | return static_cast<nsThread*>(data); |
466 | 219 | } |
467 | 39 | |
468 | 39 | if (!mInitialized) { |
469 | 9 | return nullptr; |
470 | 9 | } |
471 | 30 | |
472 | 30 | // OK, that's fine. We'll dynamically create one :-) |
473 | 30 | RefPtr<ThreadEventQueue<EventQueue>> queue = |
474 | 30 | new ThreadEventQueue<EventQueue>(MakeUnique<EventQueue>()); |
475 | 30 | RefPtr<nsThread> thread = new nsThread(WrapNotNull(queue), nsThread::NOT_MAIN_THREAD, 0); |
476 | 30 | if (!thread || NS_FAILED(thread->InitCurrentThread())) { |
477 | 0 | return nullptr; |
478 | 0 | } |
479 | 30 | |
480 | 30 | return thread.get(); // reference held in TLS |
481 | 30 | } |
482 | | |
483 | | bool |
484 | | nsThreadManager::IsNSThread() const |
485 | 49 | { |
486 | 49 | if (!mInitialized) { |
487 | 6 | return false; |
488 | 6 | } |
489 | 43 | if (auto* thread = (nsThread*)PR_GetThreadPrivate(mCurThreadIndex)) { |
490 | 43 | return thread->mShutdownRequired; |
491 | 43 | } |
492 | 0 | return false; |
493 | 0 | } |
494 | | |
495 | | NS_IMETHODIMP |
496 | | nsThreadManager::NewThread(uint32_t aCreationFlags, |
497 | | uint32_t aStackSize, |
498 | | nsIThread** aResult) |
499 | 0 | { |
500 | 0 | return NewNamedThread(NS_LITERAL_CSTRING(""), aStackSize, aResult); |
501 | 0 | } |
502 | | |
503 | | NS_IMETHODIMP |
504 | | nsThreadManager::NewNamedThread(const nsACString& aName, |
505 | | uint32_t aStackSize, |
506 | | nsIThread** aResult) |
507 | 13 | { |
508 | 13 | // Note: can be called from arbitrary threads |
509 | 13 | |
510 | 13 | // No new threads during Shutdown |
511 | 13 | if (NS_WARN_IF(!mInitialized)) { |
512 | 0 | return NS_ERROR_NOT_INITIALIZED; |
513 | 0 | } |
514 | 13 | |
515 | 13 | RefPtr<ThreadEventQueue<EventQueue>> queue = |
516 | 13 | new ThreadEventQueue<EventQueue>(MakeUnique<EventQueue>()); |
517 | 13 | RefPtr<nsThread> thr = new nsThread(WrapNotNull(queue), nsThread::NOT_MAIN_THREAD, aStackSize); |
518 | 13 | nsresult rv = thr->Init(aName); // Note: blocks until the new thread has been set up |
519 | 13 | if (NS_FAILED(rv)) { |
520 | 0 | return rv; |
521 | 0 | } |
522 | 13 | |
523 | 13 | // At this point, we expect that the thread has been registered in mThreadByPRThread; |
524 | 13 | // however, it is possible that it could have also been replaced by now, so |
525 | 13 | // we cannot really assert that it was added. Instead, kill it if we entered |
526 | 13 | // Shutdown() during/before Init() |
527 | 13 | |
528 | 13 | if (NS_WARN_IF(!mInitialized)) { |
529 | 0 | if (thr->ShutdownRequired()) { |
530 | 0 | thr->Shutdown(); // ok if it happens multiple times |
531 | 0 | } |
532 | 0 | return NS_ERROR_NOT_INITIALIZED; |
533 | 0 | } |
534 | 13 | |
535 | 13 | thr.forget(aResult); |
536 | 13 | return NS_OK; |
537 | 13 | } |
538 | | |
539 | | NS_IMETHODIMP |
540 | | nsThreadManager::GetThreadFromPRThread(PRThread* aThread, nsIThread** aResult) |
541 | 0 | { |
542 | 0 | // Keep this functioning during Shutdown |
543 | 0 | if (NS_WARN_IF(!mMainThread)) { |
544 | 0 | return NS_ERROR_NOT_INITIALIZED; |
545 | 0 | } |
546 | 0 | if (NS_WARN_IF(!aThread)) { |
547 | 0 | return NS_ERROR_INVALID_ARG; |
548 | 0 | } |
549 | 0 | |
550 | 0 | RefPtr<nsThread> temp; |
551 | 0 | { |
552 | 0 | OffTheBooksMutexAutoLock lock(mLock); |
553 | 0 | mThreadsByPRThread.Get(aThread, getter_AddRefs(temp)); |
554 | 0 | } |
555 | 0 |
|
556 | 0 | NS_IF_ADDREF(*aResult = temp); |
557 | 0 | return NS_OK; |
558 | 0 | } |
559 | | |
560 | | NS_IMETHODIMP |
561 | | nsThreadManager::GetMainThread(nsIThread** aResult) |
562 | 97 | { |
563 | 97 | // Keep this functioning during Shutdown |
564 | 97 | if (NS_WARN_IF(!mMainThread)) { |
565 | 0 | return NS_ERROR_NOT_INITIALIZED; |
566 | 0 | } |
567 | 97 | NS_ADDREF(*aResult = mMainThread); |
568 | 97 | return NS_OK; |
569 | 97 | } |
570 | | |
571 | | NS_IMETHODIMP |
572 | | nsThreadManager::GetCurrentThread(nsIThread** aResult) |
573 | 128 | { |
574 | 128 | // Keep this functioning during Shutdown |
575 | 128 | if (!mMainThread) { |
576 | 0 | return NS_ERROR_NOT_INITIALIZED; |
577 | 0 | } |
578 | 128 | *aResult = GetCurrentThread(); |
579 | 128 | if (!*aResult) { |
580 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
581 | 0 | } |
582 | 128 | NS_ADDREF(*aResult); |
583 | 128 | return NS_OK; |
584 | 128 | } |
585 | | |
586 | | NS_IMETHODIMP |
587 | | nsThreadManager::SpinEventLoopUntil(nsINestedEventLoopCondition* aCondition) |
588 | 0 | { |
589 | 0 | return SpinEventLoopUntilInternal(aCondition, false); |
590 | 0 | } |
591 | | |
592 | | NS_IMETHODIMP |
593 | | nsThreadManager::SpinEventLoopUntilOrShutdown(nsINestedEventLoopCondition* aCondition) |
594 | 0 | { |
595 | 0 | return SpinEventLoopUntilInternal(aCondition, true); |
596 | 0 | } |
597 | | |
598 | | nsresult |
599 | | nsThreadManager::SpinEventLoopUntilInternal(nsINestedEventLoopCondition* aCondition, |
600 | | bool aCheckingShutdown) |
601 | 0 | { |
602 | 0 | nsCOMPtr<nsINestedEventLoopCondition> condition(aCondition); |
603 | 0 | nsresult rv = NS_OK; |
604 | 0 |
|
605 | 0 | // Nothing to do if already shutting down. Note that gShutdownObserveHelper is |
606 | 0 | // nullified on shutdown. |
607 | 0 | if (aCheckingShutdown && |
608 | 0 | (!gShutdownObserveHelper || gShutdownObserveHelper->ShuttingDown())) { |
609 | 0 | return NS_OK; |
610 | 0 | } |
611 | 0 | |
612 | 0 | if (!mozilla::SpinEventLoopUntil([&]() -> bool { |
613 | 0 | // Shutting down is started. |
614 | 0 | if (aCheckingShutdown && |
615 | 0 | (!gShutdownObserveHelper || |
616 | 0 | gShutdownObserveHelper->ShuttingDown())) { |
617 | 0 | return true; |
618 | 0 | } |
619 | 0 | |
620 | 0 | bool isDone = false; |
621 | 0 | rv = condition->IsDone(&isDone); |
622 | 0 | // JS failure should be unusual, but we need to stop and propagate |
623 | 0 | // the error back to the caller. |
624 | 0 | if (NS_FAILED(rv)) { |
625 | 0 | return true; |
626 | 0 | } |
627 | 0 | |
628 | 0 | return isDone; |
629 | 0 | })) { |
630 | 0 | // We stopped early for some reason, which is unexpected. |
631 | 0 | return NS_ERROR_UNEXPECTED; |
632 | 0 | } |
633 | 0 | |
634 | 0 | // If we exited when the condition told us to, we need to return whether |
635 | 0 | // the condition encountered failure when executing. |
636 | 0 | return rv; |
637 | 0 | } |
638 | | |
639 | | NS_IMETHODIMP |
640 | | nsThreadManager::SpinEventLoopUntilEmpty() |
641 | 0 | { |
642 | 0 | nsIThread* thread = NS_GetCurrentThread(); |
643 | 0 |
|
644 | 0 | while (NS_HasPendingEvents(thread)) { |
645 | 0 | (void)NS_ProcessNextEvent(thread, false); |
646 | 0 | } |
647 | 0 |
|
648 | 0 | return NS_OK; |
649 | 0 | } |
650 | | |
651 | | NS_IMETHODIMP |
652 | | nsThreadManager::GetSystemGroupEventTarget(nsIEventTarget** aTarget) |
653 | 0 | { |
654 | 0 | nsCOMPtr<nsIEventTarget> target = SystemGroup::EventTargetFor(TaskCategory::Other); |
655 | 0 | target.forget(aTarget); |
656 | 0 | return NS_OK; |
657 | 0 | } |
658 | | |
659 | | uint32_t |
660 | | nsThreadManager::GetHighestNumberOfThreads() |
661 | 0 | { |
662 | 0 | OffTheBooksMutexAutoLock lock(mLock); |
663 | 0 | return mHighestNumberOfThreads; |
664 | 0 | } |
665 | | |
666 | | NS_IMETHODIMP |
667 | | nsThreadManager::DispatchToMainThread(nsIRunnable *aEvent, uint32_t aPriority) |
668 | 0 | { |
669 | 0 | // Note: C++ callers should instead use NS_DispatchToMainThread. |
670 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
671 | 0 |
|
672 | 0 | // Keep this functioning during Shutdown |
673 | 0 | if (NS_WARN_IF(!mMainThread)) { |
674 | 0 | return NS_ERROR_NOT_INITIALIZED; |
675 | 0 | } |
676 | 0 | if (aPriority != nsIRunnablePriority::PRIORITY_NORMAL) { |
677 | 0 | nsCOMPtr<nsIRunnable> event(aEvent); |
678 | 0 | return mMainThread->DispatchFromScript( |
679 | 0 | new PrioritizableRunnable(event.forget(), aPriority), 0); |
680 | 0 | } |
681 | 0 | return mMainThread->DispatchFromScript(aEvent, 0); |
682 | 0 | } |
683 | | |
684 | | void |
685 | | nsThreadManager::EnableMainThreadEventPrioritization() |
686 | 0 | { |
687 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
688 | 0 | InputEventStatistics::Get().SetEnable(true); |
689 | 0 | mMainThread->EnableInputEventPrioritization(); |
690 | 0 | } |
691 | | |
692 | | void |
693 | | nsThreadManager::FlushInputEventPrioritization() |
694 | 0 | { |
695 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
696 | 0 | mMainThread->FlushInputEventPrioritization(); |
697 | 0 | } |
698 | | |
699 | | void |
700 | | nsThreadManager::SuspendInputEventPrioritization() |
701 | 0 | { |
702 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
703 | 0 | mMainThread->SuspendInputEventPrioritization(); |
704 | 0 | } |
705 | | |
706 | | void |
707 | | nsThreadManager::ResumeInputEventPrioritization() |
708 | 0 | { |
709 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
710 | 0 | mMainThread->ResumeInputEventPrioritization(); |
711 | 0 | } |
712 | | |
713 | | NS_IMETHODIMP |
714 | | nsThreadManager::IdleDispatchToMainThread(nsIRunnable *aEvent, uint32_t aTimeout) |
715 | 0 | { |
716 | 0 | // Note: C++ callers should instead use NS_IdleDispatchToThread or |
717 | 0 | // NS_IdleDispatchToCurrentThread. |
718 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
719 | 0 |
|
720 | 0 | nsCOMPtr<nsIRunnable> event(aEvent); |
721 | 0 | if (aTimeout) { |
722 | 0 | return NS_IdleDispatchToThread(event.forget(), aTimeout, mMainThread); |
723 | 0 | } |
724 | 0 | |
725 | 0 | return NS_IdleDispatchToThread(event.forget(), mMainThread); |
726 | 0 | } |
727 | | |
728 | | namespace mozilla { |
729 | | |
730 | | PRThread* |
731 | | GetCurrentVirtualThread() |
732 | 118 | { |
733 | 118 | // We call GetCurrentVirtualThread very early in startup, before the TLS is |
734 | 118 | // initialized. Make sure we don't assert in that case. |
735 | 118 | if (gTlsCurrentVirtualThread.initialized()) { |
736 | 118 | if (gTlsCurrentVirtualThread.get()) { |
737 | 0 | return gTlsCurrentVirtualThread.get(); |
738 | 0 | } |
739 | 118 | } |
740 | 118 | return PR_GetCurrentThread(); |
741 | 118 | } |
742 | | |
743 | | PRThread* |
744 | | GetCurrentPhysicalThread() |
745 | 0 | { |
746 | 0 | return PR_GetCurrentThread(); |
747 | 0 | } |
748 | | |
749 | | } // namespace mozilla |