Line data Source code
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 "nsThreadUtils.h"
8 : #include "mozilla/Attributes.h"
9 : #include "mozilla/Likely.h"
10 : #include "mozilla/TimeStamp.h"
11 : #include "LeakRefPtr.h"
12 :
13 : #ifdef MOZILLA_INTERNAL_API
14 : # include "nsThreadManager.h"
15 : #else
16 : # include "nsXPCOMCIDInternal.h"
17 : # include "nsIThreadManager.h"
18 : # include "nsServiceManagerUtils.h"
19 : #endif
20 :
21 : #ifdef XP_WIN
22 : #include <windows.h>
23 : #elif defined(XP_MACOSX)
24 : #include <sys/resource.h>
25 : #endif
26 :
27 : #ifdef MOZ_CRASHREPORTER
28 : #include "nsExceptionHandler.h"
29 : #endif
30 :
31 : using namespace mozilla;
32 :
33 : #ifndef XPCOM_GLUE_AVOID_NSPR
34 :
35 289 : NS_IMPL_ISUPPORTS(IdlePeriod, nsIIdlePeriod)
36 :
37 : NS_IMETHODIMP
38 144 : IdlePeriod::GetIdlePeriodHint(TimeStamp* aIdleDeadline)
39 : {
40 144 : *aIdleDeadline = TimeStamp();
41 144 : return NS_OK;
42 : }
43 :
44 63668 : NS_IMPL_ISUPPORTS(Runnable, nsIRunnable, nsINamed)
45 :
46 : NS_IMETHODIMP
47 0 : Runnable::Run()
48 : {
49 : // Do nothing
50 0 : return NS_OK;
51 : }
52 :
53 : NS_IMETHODIMP
54 1106 : Runnable::GetName(nsACString& aName)
55 : {
56 : #ifdef RELEASE_OR_BETA
57 : aName.Truncate();
58 : #else
59 1106 : if (mName) {
60 1106 : aName.AssignASCII(mName);
61 : } else {
62 0 : aName.Truncate();
63 : }
64 : #endif
65 1106 : return NS_OK;
66 : }
67 :
68 : nsresult
69 1947 : Runnable::SetName(const char* aName)
70 : {
71 : #ifndef RELEASE_OR_BETA
72 1947 : mName = aName;
73 : #endif
74 1947 : return NS_OK;
75 : }
76 :
77 33380 : NS_IMPL_ISUPPORTS_INHERITED(CancelableRunnable, Runnable,
78 : nsICancelableRunnable)
79 :
80 : nsresult
81 0 : CancelableRunnable::Cancel()
82 : {
83 : // Do nothing
84 0 : return NS_OK;
85 : }
86 :
87 178 : NS_IMPL_ISUPPORTS_INHERITED(IdleRunnable, CancelableRunnable,
88 : nsIIdleRunnable)
89 :
90 : namespace mozilla {
91 : namespace detail {
92 0 : already_AddRefed<nsITimer> CreateTimer()
93 : {
94 0 : nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
95 0 : return timer.forget();
96 : }
97 : } // namespace detail
98 : } // namespace mozilla
99 :
100 : #endif // XPCOM_GLUE_AVOID_NSPR
101 :
102 : //-----------------------------------------------------------------------------
103 :
104 : nsresult
105 55 : NS_NewNamedThread(const nsACString& aName,
106 : nsIThread** aResult,
107 : nsIRunnable* aEvent,
108 : uint32_t aStackSize)
109 : {
110 110 : nsCOMPtr<nsIThread> thread;
111 : #ifdef MOZILLA_INTERNAL_API
112 : nsresult rv =
113 55 : nsThreadManager::get().nsThreadManager::NewNamedThread(aName, aStackSize,
114 110 : getter_AddRefs(thread));
115 : #else
116 : nsresult rv;
117 : nsCOMPtr<nsIThreadManager> mgr =
118 : do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
119 : if (NS_WARN_IF(NS_FAILED(rv))) {
120 : return rv;
121 : }
122 :
123 : rv = mgr->NewNamedThread(aName, aStackSize, getter_AddRefs(thread));
124 : #endif
125 55 : if (NS_WARN_IF(NS_FAILED(rv))) {
126 0 : return rv;
127 : }
128 :
129 55 : if (aEvent) {
130 26 : rv = thread->Dispatch(aEvent, NS_DISPATCH_NORMAL);
131 26 : if (NS_WARN_IF(NS_FAILED(rv))) {
132 0 : return rv;
133 : }
134 : }
135 :
136 55 : *aResult = nullptr;
137 55 : thread.swap(*aResult);
138 55 : return NS_OK;
139 : }
140 :
141 : nsresult
142 0 : NS_NewThread(nsIThread** aResult, nsIRunnable* aEvent, uint32_t aStackSize)
143 : {
144 0 : return NS_NewNamedThread(NS_LITERAL_CSTRING(""), aResult, aEvent, aStackSize);
145 : }
146 :
147 : nsresult
148 719 : NS_GetCurrentThread(nsIThread** aResult)
149 : {
150 : #ifdef MOZILLA_INTERNAL_API
151 719 : return nsThreadManager::get().nsThreadManager::GetCurrentThread(aResult);
152 : #else
153 : nsresult rv;
154 : nsCOMPtr<nsIThreadManager> mgr =
155 : do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
156 : if (NS_WARN_IF(NS_FAILED(rv))) {
157 : return rv;
158 : }
159 : return mgr->GetCurrentThread(aResult);
160 : #endif
161 : }
162 :
163 : nsresult
164 798 : NS_GetMainThread(nsIThread** aResult)
165 : {
166 : #ifdef MOZILLA_INTERNAL_API
167 798 : return nsThreadManager::get().nsThreadManager::GetMainThread(aResult);
168 : #else
169 : nsresult rv;
170 : nsCOMPtr<nsIThreadManager> mgr =
171 : do_GetService(NS_THREADMANAGER_CONTRACTID, &rv);
172 : if (NS_WARN_IF(NS_FAILED(rv))) {
173 : return rv;
174 : }
175 : return mgr->GetMainThread(aResult);
176 : #endif
177 : }
178 :
179 : nsresult
180 286 : NS_DispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent)
181 : {
182 : nsresult rv;
183 572 : nsCOMPtr<nsIRunnable> event(aEvent);
184 : #ifdef MOZILLA_INTERNAL_API
185 286 : nsIEventTarget* thread = GetCurrentThreadEventTarget();
186 286 : if (!thread) {
187 0 : return NS_ERROR_UNEXPECTED;
188 : }
189 : #else
190 : nsCOMPtr<nsIThread> thread;
191 : rv = NS_GetCurrentThread(getter_AddRefs(thread));
192 : if (NS_WARN_IF(NS_FAILED(rv))) {
193 : return rv;
194 : }
195 : #endif
196 : // To keep us from leaking the runnable if dispatch method fails,
197 : // we grab the reference on failures and release it.
198 286 : nsIRunnable* temp = event.get();
199 286 : rv = thread->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
200 286 : if (NS_WARN_IF(NS_FAILED(rv))) {
201 : // Dispatch() leaked the reference to the event, but due to caller's
202 : // assumptions, we shouldn't leak here. And given we are on the same
203 : // thread as the dispatch target, it's mostly safe to do it here.
204 0 : NS_RELEASE(temp);
205 : }
206 286 : return rv;
207 : }
208 :
209 : // It is common to call NS_DispatchToCurrentThread with a newly
210 : // allocated runnable with a refcount of zero. To keep us from leaking
211 : // the runnable if the dispatch method fails, we take a death grip.
212 : nsresult
213 19 : NS_DispatchToCurrentThread(nsIRunnable* aEvent)
214 : {
215 38 : nsCOMPtr<nsIRunnable> event(aEvent);
216 38 : return NS_DispatchToCurrentThread(event.forget());
217 : }
218 :
219 : nsresult
220 425 : NS_DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDispatchFlags)
221 : {
222 425 : LeakRefPtr<nsIRunnable> event(Move(aEvent));
223 850 : nsCOMPtr<nsIThread> thread;
224 425 : nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
225 425 : if (NS_WARN_IF(NS_FAILED(rv))) {
226 0 : NS_ASSERTION(false, "Failed NS_DispatchToMainThread() in shutdown; leaking");
227 : // NOTE: if you stop leaking here, adjust Promise::MaybeReportRejected(),
228 : // which assumes a leak here, or split into leaks and no-leaks versions
229 0 : return rv;
230 : }
231 425 : return thread->Dispatch(event.take(), aDispatchFlags);
232 : }
233 :
234 : // In the case of failure with a newly allocated runnable with a
235 : // refcount of zero, we intentionally leak the runnable, because it is
236 : // likely that the runnable is being dispatched to the main thread
237 : // because it owns main thread only objects, so it is not safe to
238 : // release them here.
239 : nsresult
240 130 : NS_DispatchToMainThread(nsIRunnable* aEvent, uint32_t aDispatchFlags)
241 : {
242 260 : nsCOMPtr<nsIRunnable> event(aEvent);
243 260 : return NS_DispatchToMainThread(event.forget(), aDispatchFlags);
244 : }
245 :
246 : nsresult
247 2 : NS_DelayedDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDelayMs)
248 : {
249 4 : nsCOMPtr<nsIRunnable> event(aEvent);
250 : #ifdef MOZILLA_INTERNAL_API
251 2 : nsIEventTarget* thread = GetCurrentThreadEventTarget();
252 2 : if (!thread) {
253 0 : return NS_ERROR_UNEXPECTED;
254 : }
255 : #else
256 : nsresult rv;
257 : nsCOMPtr<nsIThread> thread;
258 : rv = NS_GetCurrentThread(getter_AddRefs(thread));
259 : if (NS_WARN_IF(NS_FAILED(rv))) {
260 : return rv;
261 : }
262 : #endif
263 :
264 2 : return thread->DelayedDispatch(event.forget(), aDelayMs);
265 : }
266 :
267 : nsresult
268 14 : NS_IdleDispatchToThread(already_AddRefed<nsIRunnable>&& aEvent,
269 : nsIThread* aThread)
270 : {
271 : nsresult rv;
272 28 : nsCOMPtr<nsIRunnable> event(aEvent);
273 14 : NS_ENSURE_TRUE(event, NS_ERROR_INVALID_ARG);
274 14 : if (!aThread) {
275 0 : return NS_ERROR_UNEXPECTED;
276 : }
277 : // To keep us from leaking the runnable if dispatch method fails,
278 : // we grab the reference on failures and release it.
279 14 : nsIRunnable* temp = event.get();
280 14 : rv = aThread->IdleDispatch(event.forget());
281 14 : if (NS_WARN_IF(NS_FAILED(rv))) {
282 : // Dispatch() leaked the reference to the event, but due to caller's
283 : // assumptions, we shouldn't leak here. And given we are on the same
284 : // thread as the dispatch target, it's mostly safe to do it here.
285 0 : NS_RELEASE(temp);
286 : }
287 :
288 14 : return rv;
289 : }
290 :
291 : nsresult
292 4 : NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent)
293 : {
294 4 : return NS_IdleDispatchToThread(Move(aEvent),
295 4 : NS_GetCurrentThread());
296 : }
297 :
298 : class IdleRunnableWrapper : public IdleRunnable
299 : {
300 : public:
301 6 : explicit IdleRunnableWrapper(already_AddRefed<nsIRunnable>&& aEvent)
302 6 : : mRunnable(Move(aEvent))
303 : {
304 6 : }
305 :
306 8 : NS_IMETHOD Run() override
307 : {
308 8 : if (!mRunnable) {
309 3 : return NS_OK;
310 : }
311 5 : CancelTimer();
312 10 : nsCOMPtr<nsIRunnable> runnable = mRunnable.forget();
313 5 : return runnable->Run();
314 : }
315 :
316 : static void
317 4 : TimedOut(nsITimer* aTimer, void* aClosure)
318 : {
319 : RefPtr<IdleRunnableWrapper> runnable =
320 8 : static_cast<IdleRunnableWrapper*>(aClosure);
321 4 : runnable->Run();
322 4 : }
323 :
324 6 : void SetTimer(uint32_t aDelay, nsIEventTarget* aTarget) override
325 : {
326 6 : MOZ_ASSERT(aTarget);
327 6 : MOZ_ASSERT(!mTimer);
328 6 : mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
329 6 : if (mTimer) {
330 6 : mTimer->SetTarget(aTarget);
331 6 : mTimer->InitWithNamedFuncCallback(TimedOut,
332 : this,
333 : aDelay,
334 : nsITimer::TYPE_ONE_SHOT,
335 6 : "IdleRunnableWrapper::SetTimer");
336 : }
337 6 : }
338 :
339 4 : NS_IMETHOD GetName(nsACString& aName) override
340 : {
341 4 : aName.AssignLiteral("IdleRunnableWrapper");
342 8 : if (nsCOMPtr<nsINamed> named = do_QueryInterface(mRunnable)) {
343 2 : nsAutoCString name;
344 1 : named->GetName(name);
345 1 : if (!name.IsEmpty()) {
346 1 : aName.AppendLiteral(" for ");
347 1 : aName.Append(name);
348 : }
349 : }
350 4 : return NS_OK;
351 : }
352 :
353 : private:
354 8 : ~IdleRunnableWrapper()
355 8 : {
356 4 : CancelTimer();
357 12 : }
358 :
359 9 : void CancelTimer()
360 : {
361 9 : if (mTimer) {
362 9 : mTimer->Cancel();
363 : }
364 9 : }
365 :
366 : nsCOMPtr<nsITimer> mTimer;
367 : nsCOMPtr<nsIRunnable> mRunnable;
368 : };
369 :
370 : extern nsresult
371 9 : NS_IdleDispatchToThread(already_AddRefed<nsIRunnable>&& aEvent,
372 : uint32_t aTimeout,
373 : nsIThread* aThread)
374 : {
375 18 : nsCOMPtr<nsIRunnable> event(Move(aEvent));
376 9 : NS_ENSURE_TRUE(event, NS_ERROR_INVALID_ARG);
377 :
378 : //XXX Using current thread for now as the nsIEventTarget.
379 9 : nsIEventTarget* target = mozilla::GetCurrentThreadEventTarget();
380 9 : if (!target) {
381 0 : return NS_ERROR_UNEXPECTED;
382 : }
383 :
384 18 : nsCOMPtr<nsIIdleRunnable> idleEvent = do_QueryInterface(event);
385 :
386 9 : if (!idleEvent) {
387 12 : idleEvent = new IdleRunnableWrapper(event.forget());
388 6 : event = do_QueryInterface(idleEvent);
389 6 : MOZ_DIAGNOSTIC_ASSERT(event);
390 : }
391 9 : idleEvent->SetTimer(aTimeout, target);
392 :
393 9 : return NS_IdleDispatchToThread(event.forget(), aThread);
394 : }
395 :
396 : extern nsresult
397 9 : NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent,
398 : uint32_t aTimeout)
399 : {
400 9 : return NS_IdleDispatchToThread(Move(aEvent), aTimeout,
401 9 : NS_GetCurrentThread());
402 : }
403 :
404 : #ifndef XPCOM_GLUE_AVOID_NSPR
405 : nsresult
406 0 : NS_ProcessPendingEvents(nsIThread* aThread, PRIntervalTime aTimeout)
407 : {
408 0 : nsresult rv = NS_OK;
409 :
410 : #ifdef MOZILLA_INTERNAL_API
411 0 : if (!aThread) {
412 0 : aThread = NS_GetCurrentThread();
413 0 : if (NS_WARN_IF(!aThread)) {
414 0 : return NS_ERROR_UNEXPECTED;
415 : }
416 : }
417 : #else
418 : nsCOMPtr<nsIThread> current;
419 : if (!aThread) {
420 : rv = NS_GetCurrentThread(getter_AddRefs(current));
421 : if (NS_WARN_IF(NS_FAILED(rv))) {
422 : return rv;
423 : }
424 : aThread = current.get();
425 : }
426 : #endif
427 :
428 0 : PRIntervalTime start = PR_IntervalNow();
429 : for (;;) {
430 : bool processedEvent;
431 0 : rv = aThread->ProcessNextEvent(false, &processedEvent);
432 0 : if (NS_FAILED(rv) || !processedEvent) {
433 0 : break;
434 : }
435 0 : if (PR_IntervalNow() - start > aTimeout) {
436 0 : break;
437 : }
438 0 : }
439 0 : return rv;
440 : }
441 : #endif // XPCOM_GLUE_AVOID_NSPR
442 :
443 : inline bool
444 1485 : hasPendingEvents(nsIThread* aThread)
445 : {
446 : bool val;
447 1485 : return NS_SUCCEEDED(aThread->HasPendingEvents(&val)) && val;
448 : }
449 :
450 : bool
451 1485 : NS_HasPendingEvents(nsIThread* aThread)
452 : {
453 1485 : if (!aThread) {
454 : #ifndef MOZILLA_INTERNAL_API
455 : nsCOMPtr<nsIThread> current;
456 : NS_GetCurrentThread(getter_AddRefs(current));
457 : return hasPendingEvents(current);
458 : #else
459 0 : aThread = NS_GetCurrentThread();
460 0 : if (NS_WARN_IF(!aThread)) {
461 0 : return false;
462 : }
463 : #endif
464 : }
465 1485 : return hasPendingEvents(aThread);
466 : }
467 :
468 : bool
469 1742 : NS_ProcessNextEvent(nsIThread* aThread, bool aMayWait)
470 : {
471 : #ifdef MOZILLA_INTERNAL_API
472 1742 : if (!aThread) {
473 0 : aThread = NS_GetCurrentThread();
474 0 : if (NS_WARN_IF(!aThread)) {
475 0 : return false;
476 : }
477 : }
478 : #else
479 : nsCOMPtr<nsIThread> current;
480 : if (!aThread) {
481 : NS_GetCurrentThread(getter_AddRefs(current));
482 : if (NS_WARN_IF(!current)) {
483 : return false;
484 : }
485 : aThread = current.get();
486 : }
487 : #endif
488 : bool val;
489 1742 : return NS_SUCCEEDED(aThread->ProcessNextEvent(aMayWait, &val)) && val;
490 : }
491 :
492 : void
493 76 : NS_SetCurrentThreadName(const char* aName)
494 : {
495 76 : PR_SetCurrentThreadName(aName);
496 : #ifdef MOZ_CRASHREPORTER
497 76 : CrashReporter::SetCurrentThreadName(aName);
498 : #endif
499 76 : }
500 :
501 : #ifdef MOZILLA_INTERNAL_API
502 : nsIThread*
503 169 : NS_GetCurrentThread()
504 : {
505 169 : return nsThreadManager::get().GetCurrentThread();
506 : }
507 : #endif
508 :
509 : // nsThreadPoolNaming
510 : nsCString
511 26 : nsThreadPoolNaming::GetNextThreadName(const nsACString& aPoolName)
512 : {
513 26 : nsCString name(aPoolName);
514 26 : name.AppendLiteral(" #");
515 26 : name.AppendInt(++mCounter, 10); // The counter is declared as atomic
516 26 : return name;
517 : }
518 :
519 : // nsAutoLowPriorityIO
520 0 : nsAutoLowPriorityIO::nsAutoLowPriorityIO()
521 : {
522 : #if defined(XP_WIN)
523 : lowIOPrioritySet = SetThreadPriority(GetCurrentThread(),
524 : THREAD_MODE_BACKGROUND_BEGIN);
525 : #elif defined(XP_MACOSX)
526 : oldPriority = getiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD);
527 : lowIOPrioritySet = oldPriority != -1 &&
528 : setiopolicy_np(IOPOL_TYPE_DISK,
529 : IOPOL_SCOPE_THREAD,
530 : IOPOL_THROTTLE) != -1;
531 : #else
532 0 : lowIOPrioritySet = false;
533 : #endif
534 0 : }
535 :
536 0 : nsAutoLowPriorityIO::~nsAutoLowPriorityIO()
537 : {
538 : #if defined(XP_WIN)
539 : if (MOZ_LIKELY(lowIOPrioritySet)) {
540 : // On Windows the old thread priority is automatically restored
541 : SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END);
542 : }
543 : #elif defined(XP_MACOSX)
544 : if (MOZ_LIKELY(lowIOPrioritySet)) {
545 : setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, oldPriority);
546 : }
547 : #endif
548 0 : }
549 :
550 : namespace mozilla {
551 :
552 : PRThread*
553 1431015 : GetCurrentVirtualThread()
554 : {
555 1431015 : return PR_GetCurrentThread();
556 : }
557 :
558 : PRThread*
559 0 : GetCurrentPhysicalThread()
560 : {
561 0 : return PR_GetCurrentThread();
562 : }
563 :
564 : nsIEventTarget*
565 684 : GetCurrentThreadEventTarget()
566 : {
567 1368 : nsCOMPtr<nsIThread> thread;
568 684 : nsresult rv = NS_GetCurrentThread(getter_AddRefs(thread));
569 684 : if (NS_FAILED(rv)) {
570 0 : return nullptr;
571 : }
572 :
573 684 : return thread->EventTarget();
574 : }
575 :
576 : nsIEventTarget*
577 18 : GetMainThreadEventTarget()
578 : {
579 36 : nsCOMPtr<nsIThread> thread;
580 18 : nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
581 18 : if (NS_FAILED(rv)) {
582 0 : return nullptr;
583 : }
584 :
585 18 : return thread->EventTarget();
586 : }
587 :
588 : nsISerialEventTarget*
589 12 : GetCurrentThreadSerialEventTarget()
590 : {
591 24 : nsCOMPtr<nsIThread> thread;
592 12 : nsresult rv = NS_GetCurrentThread(getter_AddRefs(thread));
593 12 : if (NS_FAILED(rv)) {
594 0 : return nullptr;
595 : }
596 :
597 12 : return thread->SerialEventTarget();
598 : }
599 :
600 : nsISerialEventTarget*
601 37 : GetMainThreadSerialEventTarget()
602 : {
603 74 : nsCOMPtr<nsIThread> thread;
604 37 : nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
605 37 : if (NS_FAILED(rv)) {
606 0 : return nullptr;
607 : }
608 :
609 37 : return thread->SerialEventTarget();
610 : }
611 :
612 : } // namespace mozilla
613 :
614 : bool
615 24959 : nsIEventTarget::IsOnCurrentThread()
616 : {
617 24959 : if (mVirtualThread) {
618 24956 : return mVirtualThread == GetCurrentVirtualThread();
619 : }
620 3 : return IsOnCurrentThreadInfallible();
621 : }
|