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 #ifndef nsThreadUtils_h__
8 #define nsThreadUtils_h__
9 
10 #include "prthread.h"
11 #include "prinrval.h"
12 #include "MainThreadUtils.h"
13 #include "nsIThreadManager.h"
14 #include "nsIThread.h"
15 #include "nsIRunnable.h"
16 #include "nsICancelableRunnable.h"
17 #include "nsIIdlePeriod.h"
18 #include "nsIIncrementalRunnable.h"
19 #include "nsStringGlue.h"
20 #include "nsCOMPtr.h"
21 #include "nsAutoPtr.h"
22 #include "mozilla/Atomics.h"
23 #include "mozilla/IndexSequence.h"
24 #include "mozilla/Likely.h"
25 #include "mozilla/Move.h"
26 #include "mozilla/TimeStamp.h"
27 #include "mozilla/Tuple.h"
28 #include "mozilla/TypeTraits.h"
29 
30 //-----------------------------------------------------------------------------
31 // These methods are alternatives to the methods on nsIThreadManager, provided
32 // for convenience.
33 
34 /**
35  * Set name of the target thread.  This operation is asynchronous.
36  */
37 extern void NS_SetThreadName(nsIThread* aThread, const nsACString& aName);
38 
39 /**
40  * Static length version of the above function checking length of the
41  * name at compile time.
42  */
43 template<size_t LEN>
44 inline void
NS_SetThreadName(nsIThread * aThread,const char (& aName)[LEN])45 NS_SetThreadName(nsIThread* aThread, const char (&aName)[LEN])
46 {
47   static_assert(LEN <= 16,
48                 "Thread name must be no more than 16 characters");
49   NS_SetThreadName(aThread, nsDependentCString(aName));
50 }
51 
52 /**
53  * Create a new thread, and optionally provide an initial event for the thread.
54  *
55  * @param aResult
56  *   The resulting nsIThread object.
57  * @param aInitialEvent
58  *   The initial event to run on this thread.  This parameter may be null.
59  * @param aStackSize
60  *   The size in bytes to reserve for the thread's stack.
61  *
62  * @returns NS_ERROR_INVALID_ARG
63  *   Indicates that the given name is not unique.
64  */
65 extern nsresult
66 NS_NewThread(nsIThread** aResult,
67              nsIRunnable* aInitialEvent = nullptr,
68              uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE);
69 
70 /**
71  * Creates a named thread, otherwise the same as NS_NewThread
72  */
73 template<size_t LEN>
74 inline nsresult
75 NS_NewNamedThread(const char (&aName)[LEN],
76                   nsIThread** aResult,
77                   nsIRunnable* aInitialEvent = nullptr,
78                   uint32_t aStackSize = nsIThreadManager::DEFAULT_STACK_SIZE)
79 {
80   // Hold a ref while dispatching the initial event to match NS_NewThread()
81   nsCOMPtr<nsIThread> thread;
82   nsresult rv = NS_NewThread(getter_AddRefs(thread), nullptr, aStackSize);
83   if (NS_WARN_IF(NS_FAILED(rv))) {
84     return rv;
85   }
86   NS_SetThreadName<LEN>(thread, aName);
87   if (aInitialEvent) {
88     rv = thread->Dispatch(aInitialEvent, NS_DISPATCH_NORMAL);
89     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Initial event dispatch failed");
90   }
91 
92   *aResult = nullptr;
93   thread.swap(*aResult);
94   return rv;
95 }
96 
97 /**
98  * Get a reference to the current thread.
99  *
100  * @param aResult
101  *   The resulting nsIThread object.
102  */
103 extern nsresult NS_GetCurrentThread(nsIThread** aResult);
104 
105 /**
106  * Dispatch the given event to the current thread.
107  *
108  * @param aEvent
109  *   The event to dispatch.
110  *
111  * @returns NS_ERROR_INVALID_ARG
112  *   If event is null.
113  */
114 extern nsresult NS_DispatchToCurrentThread(nsIRunnable* aEvent);
115 extern nsresult
116 NS_DispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent);
117 
118 /**
119  * Dispatch the given event to the main thread.
120  *
121  * @param aEvent
122  *   The event to dispatch.
123  * @param aDispatchFlags
124  *   The flags to pass to the main thread's dispatch method.
125  *
126  * @returns NS_ERROR_INVALID_ARG
127  *   If event is null.
128  */
129 extern nsresult
130 NS_DispatchToMainThread(nsIRunnable* aEvent,
131                         uint32_t aDispatchFlags = NS_DISPATCH_NORMAL);
132 extern nsresult
133 NS_DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent,
134                         uint32_t aDispatchFlags = NS_DISPATCH_NORMAL);
135 
136 extern nsresult
137 NS_DelayedDispatchToCurrentThread(
138   already_AddRefed<nsIRunnable>&& aEvent, uint32_t aDelayMs);
139 
140 extern nsresult
141 NS_IdleDispatchToCurrentThread(already_AddRefed<nsIRunnable>&& aEvent);
142 
143 #ifndef XPCOM_GLUE_AVOID_NSPR
144 /**
145  * Process all pending events for the given thread before returning.  This
146  * method simply calls ProcessNextEvent on the thread while HasPendingEvents
147  * continues to return true and the time spent in NS_ProcessPendingEvents
148  * does not exceed the given timeout value.
149  *
150  * @param aThread
151  *   The thread object for which to process pending events.  If null, then
152  *   events will be processed for the current thread.
153  * @param aTimeout
154  *   The maximum number of milliseconds to spend processing pending events.
155  *   Events are not pre-empted to honor this timeout.  Rather, the timeout
156  *   value is simply used to determine whether or not to process another event.
157  *   Pass PR_INTERVAL_NO_TIMEOUT to specify no timeout.
158  */
159 extern nsresult
160 NS_ProcessPendingEvents(nsIThread* aThread,
161                         PRIntervalTime aTimeout = PR_INTERVAL_NO_TIMEOUT);
162 #endif
163 
164 /**
165  * Shortcut for nsIThread::HasPendingEvents.
166  *
167  * It is an error to call this function when the given thread is not the
168  * current thread.  This function will return false if called from some
169  * other thread.
170  *
171  * @param aThread
172  *   The current thread or null.
173  *
174  * @returns
175  *   A boolean value that if "true" indicates that there are pending events
176  *   in the current thread's event queue.
177  */
178 extern bool NS_HasPendingEvents(nsIThread* aThread = nullptr);
179 
180 /**
181  * Shortcut for nsIThread::ProcessNextEvent.
182  *
183  * It is an error to call this function when the given thread is not the
184  * current thread.  This function will simply return false if called
185  * from some other thread.
186  *
187  * @param aThread
188  *   The current thread or null.
189  * @param aMayWait
190  *   A boolean parameter that if "true" indicates that the method may block
191  *   the calling thread to wait for a pending event.
192  *
193  * @returns
194  *   A boolean value that if "true" indicates that an event from the current
195  *   thread's event queue was processed.
196  */
197 extern bool NS_ProcessNextEvent(nsIThread* aThread = nullptr,
198                                 bool aMayWait = true);
199 
200 //-----------------------------------------------------------------------------
201 // Helpers that work with nsCOMPtr:
202 
203 inline already_AddRefed<nsIThread>
do_GetCurrentThread()204 do_GetCurrentThread()
205 {
206   nsIThread* thread = nullptr;
207   NS_GetCurrentThread(&thread);
208   return already_AddRefed<nsIThread>(thread);
209 }
210 
211 inline already_AddRefed<nsIThread>
do_GetMainThread()212 do_GetMainThread()
213 {
214   nsIThread* thread = nullptr;
215   NS_GetMainThread(&thread);
216   return already_AddRefed<nsIThread>(thread);
217 }
218 
219 //-----------------------------------------------------------------------------
220 
221 #ifdef MOZILLA_INTERNAL_API
222 // Fast access to the current thread.  Do not release the returned pointer!  If
223 // you want to use this pointer from some other thread, then you will need to
224 // AddRef it.  Otherwise, you should only consider this pointer valid from code
225 // running on the current thread.
226 extern nsIThread* NS_GetCurrentThread();
227 #endif
228 
229 //-----------------------------------------------------------------------------
230 
231 #ifndef XPCOM_GLUE_AVOID_NSPR
232 
233 namespace mozilla {
234 
235 // This class is designed to be subclassed.
236 class IdlePeriod : public nsIIdlePeriod
237 {
238 public:
239   NS_DECL_THREADSAFE_ISUPPORTS
240   NS_DECL_NSIIDLEPERIOD
241 
IdlePeriod()242   IdlePeriod() {}
243 
244 protected:
~IdlePeriod()245   virtual ~IdlePeriod() {}
246 private:
247   IdlePeriod(const IdlePeriod&) = delete;
248   IdlePeriod& operator=(const IdlePeriod&) = delete;
249   IdlePeriod& operator=(const IdlePeriod&&) = delete;
250 };
251 
252 // This class is designed to be subclassed.
253 class Runnable : public nsIRunnable
254 {
255 public:
256   NS_DECL_THREADSAFE_ISUPPORTS
257   NS_DECL_NSIRUNNABLE
258 
Runnable()259   Runnable() {}
260 
261 protected:
~Runnable()262   virtual ~Runnable() {}
263 private:
264   Runnable(const Runnable&) = delete;
265   Runnable& operator=(const Runnable&) = delete;
266   Runnable& operator=(const Runnable&&) = delete;
267 };
268 
269 // This class is designed to be subclassed.
270 class CancelableRunnable : public Runnable,
271                            public nsICancelableRunnable
272 {
273 public:
274   NS_DECL_ISUPPORTS_INHERITED
275   // nsICancelableRunnable
276   virtual nsresult Cancel() override;
277 
CancelableRunnable()278   CancelableRunnable() {}
279 
280 protected:
~CancelableRunnable()281   virtual ~CancelableRunnable() {}
282 private:
283   CancelableRunnable(const CancelableRunnable&) = delete;
284   CancelableRunnable& operator=(const CancelableRunnable&) = delete;
285   CancelableRunnable& operator=(const CancelableRunnable&&) = delete;
286 };
287 
288 // This class is designed to be subclassed.
289 class IncrementalRunnable : public CancelableRunnable,
290                             public nsIIncrementalRunnable
291 {
292 public:
293   NS_DECL_ISUPPORTS_INHERITED
294   // nsIIncrementalRunnable
295   virtual void SetDeadline(TimeStamp aDeadline) override;
296 
IncrementalRunnable()297   IncrementalRunnable() {}
298 
299 protected:
~IncrementalRunnable()300   virtual ~IncrementalRunnable() {}
301 private:
302   IncrementalRunnable(const IncrementalRunnable&) = delete;
303   IncrementalRunnable& operator=(const IncrementalRunnable&) = delete;
304   IncrementalRunnable& operator=(const IncrementalRunnable&&) = delete;
305 };
306 
307 namespace detail {
308 
309 // An event that can be used to call a C++11 functions or function objects,
310 // including lambdas. The function must have no required arguments, and must
311 // return void.
312 template<typename StoredFunction>
313 class RunnableFunction : public Runnable
314 {
315 public:
316   template <typename F>
RunnableFunction(F && aFunction)317   explicit RunnableFunction(F&& aFunction)
318     : mFunction(Forward<F>(aFunction))
319   { }
320 
Run()321   NS_IMETHOD Run() override {
322     static_assert(IsVoid<decltype(mFunction())>::value,
323                   "The lambda must return void!");
324     mFunction();
325     return NS_OK;
326   }
327 private:
328   StoredFunction mFunction;
329 };
330 
331 } // namespace detail
332 
333 } // namespace mozilla
334 
335 template<typename Function>
336 already_AddRefed<mozilla::Runnable>
NS_NewRunnableFunction(Function && aFunction)337 NS_NewRunnableFunction(Function&& aFunction)
338 {
339   return do_AddRef(new mozilla::detail::RunnableFunction
340                    // Make sure we store a non-reference in nsRunnableFunction.
341                    <typename mozilla::RemoveReference<Function>::Type>
342                    // But still forward aFunction to move if possible.
343                    (mozilla::Forward<Function>(aFunction)));
344 }
345 
346 // An event that can be used to call a method on a class.  The class type must
347 // support reference counting. This event supports Revoke for use
348 // with nsRevocableEventPtr.
349 template<class ClassType,
350          typename ReturnType = void,
351          bool Owning = true,
352          bool Cancelable = false>
353 class nsRunnableMethod : public mozilla::Conditional<!Cancelable,
354                                                      mozilla::Runnable,
355                                                      mozilla::CancelableRunnable>::Type
356 {
357 public:
358   virtual void Revoke() = 0;
359 
360   // These ReturnTypeEnforcer classes set up a blacklist for return types that
361   // we know are not safe. The default ReturnTypeEnforcer compiles just fine but
362   // already_AddRefed will not.
363   template<typename OtherReturnType>
364   class ReturnTypeEnforcer
365   {
366   public:
367     typedef int ReturnTypeIsSafe;
368   };
369 
370   template<class T>
371   class ReturnTypeEnforcer<already_AddRefed<T>>
372   {
373     // No ReturnTypeIsSafe makes this illegal!
374   };
375 
376   // Make sure this return type is safe.
377   typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check;
378 };
379 
380 template<class ClassType, bool Owning>
381 struct nsRunnableMethodReceiver
382 {
383   RefPtr<ClassType> mObj;
nsRunnableMethodReceivernsRunnableMethodReceiver384   explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
~nsRunnableMethodReceivernsRunnableMethodReceiver385   ~nsRunnableMethodReceiver() { Revoke(); }
GetnsRunnableMethodReceiver386   ClassType* Get() const { return mObj.get(); }
RevokensRunnableMethodReceiver387   void Revoke() { mObj = nullptr; }
388 };
389 
390 template<class ClassType>
391 struct nsRunnableMethodReceiver<ClassType, false>
392 {
393   ClassType* MOZ_NON_OWNING_REF mObj;
394   explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {}
395   ClassType* Get() const { return mObj; }
396   void Revoke() { mObj = nullptr; }
397 };
398 
399 template<typename Method, bool Owning, bool Cancelable> struct nsRunnableMethodTraits;
400 
401 template<class C, typename R, bool Owning, bool Cancelable, typename... As>
402 struct nsRunnableMethodTraits<R(C::*)(As...), Owning, Cancelable>
403 {
404   typedef C class_type;
405   typedef R return_type;
406   typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
407   static const bool can_cancel = Cancelable;
408 };
409 
410 template<class C, typename R, bool Owning, bool Cancelable, typename... As>
411 struct nsRunnableMethodTraits<R(C::*)(As...) const, Owning, Cancelable>
412 {
413   typedef const C class_type;
414   typedef R return_type;
415   typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
416   static const bool can_cancel = Cancelable;
417 };
418 
419 #ifdef NS_HAVE_STDCALL
420 template<class C, typename R, bool Owning, bool Cancelable, typename... As>
421 struct nsRunnableMethodTraits<R(__stdcall C::*)(As...), Owning, Cancelable>
422 {
423   typedef C class_type;
424   typedef R return_type;
425   typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
426   static const bool can_cancel = Cancelable;
427 };
428 
429 template<class C, typename R, bool Owning, bool Cancelable>
430 struct nsRunnableMethodTraits<R(NS_STDCALL C::*)(), Owning, Cancelable>
431 {
432   typedef C class_type;
433   typedef R return_type;
434   typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
435   static const bool can_cancel = Cancelable;
436 };
437 template<class C, typename R, bool Owning, bool Cancelable, typename... As>
438 struct nsRunnableMethodTraits<R(__stdcall C::*)(As...) const, Owning, Cancelable>
439 {
440   typedef const C class_type;
441   typedef R return_type;
442   typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
443   static const bool can_cancel = Cancelable;
444 };
445 
446 template<class C, typename R, bool Owning, bool Cancelable>
447 struct nsRunnableMethodTraits<R(NS_STDCALL C::*)() const, Owning, Cancelable>
448 {
449   typedef const C class_type;
450   typedef R return_type;
451   typedef nsRunnableMethod<C, R, Owning, Cancelable> base_type;
452   static const bool can_cancel = Cancelable;
453 };
454 #endif
455 
456 
457 // IsParameterStorageClass<T>::value is true if T is a parameter-storage class
458 // that will be recognized by NS_New[NonOwning]RunnableMethodWithArg[s] to
459 // force a specific storage&passing strategy (instead of inferring one,
460 // see ParameterStorage).
461 // When creating a new storage class, add a specialization for it to be
462 // recognized.
463 template<typename T>
464 struct IsParameterStorageClass : public mozilla::FalseType {};
465 
466 // StoreXPassByY structs used to inform nsRunnableMethodArguments how to
467 // store arguments, and how to pass them to the target method.
468 
469 template<typename T>
470 struct StoreCopyPassByValue
471 {
472   typedef T stored_type;
473   typedef T passed_type;
474   stored_type m;
475   template <typename A>
476   MOZ_IMPLICIT StoreCopyPassByValue(A&& a) : m(mozilla::Forward<A>(a)) {}
477   passed_type PassAsParameter() { return m; }
478 };
479 template<typename S>
480 struct IsParameterStorageClass<StoreCopyPassByValue<S>>
481   : public mozilla::TrueType {};
482 
483 template<typename T>
484 struct StoreCopyPassByConstLRef
485 {
486   typedef T stored_type;
487   typedef const T& passed_type;
488   stored_type m;
489   template <typename A>
490   MOZ_IMPLICIT StoreCopyPassByConstLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
491   passed_type PassAsParameter() { return m; }
492 };
493 template<typename S>
494 struct IsParameterStorageClass<StoreCopyPassByConstLRef<S>>
495   : public mozilla::TrueType {};
496 
497 template<typename T>
498 struct StoreCopyPassByLRef
499 {
500   typedef T stored_type;
501   typedef T& passed_type;
502   stored_type m;
503   template <typename A>
504   MOZ_IMPLICIT StoreCopyPassByLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
505   passed_type PassAsParameter() { return m; }
506 };
507 template<typename S>
508 struct IsParameterStorageClass<StoreCopyPassByLRef<S>>
509   : public mozilla::TrueType {};
510 
511 template<typename T>
512 struct StoreCopyPassByRRef
513 {
514   typedef T stored_type;
515   typedef T&& passed_type;
516   stored_type m;
517   template <typename A>
518   MOZ_IMPLICIT StoreCopyPassByRRef(A&& a) : m(mozilla::Forward<A>(a)) {}
519   passed_type PassAsParameter() { return mozilla::Move(m); }
520 };
521 template<typename S>
522 struct IsParameterStorageClass<StoreCopyPassByRRef<S>>
523   : public mozilla::TrueType {};
524 
525 template<typename T>
526 struct StoreRefPassByLRef
527 {
528   typedef T& stored_type;
529   typedef T& passed_type;
530   stored_type m;
531   template <typename A>
532   MOZ_IMPLICIT StoreRefPassByLRef(A& a) : m(a) {}
533   passed_type PassAsParameter() { return m; }
534 };
535 template<typename S>
536 struct IsParameterStorageClass<StoreRefPassByLRef<S>>
537   : public mozilla::TrueType {};
538 
539 template<typename T>
540 struct StoreConstRefPassByConstLRef
541 {
542   typedef const T& stored_type;
543   typedef const T& passed_type;
544   stored_type m;
545   template <typename A>
546   MOZ_IMPLICIT StoreConstRefPassByConstLRef(const A& a) : m(a) {}
547   passed_type PassAsParameter() { return m; }
548 };
549 template<typename S>
550 struct IsParameterStorageClass<StoreConstRefPassByConstLRef<S>>
551   : public mozilla::TrueType {};
552 
553 template<typename T>
554 struct StorensRefPtrPassByPtr
555 {
556   typedef RefPtr<T> stored_type;
557   typedef T* passed_type;
558   stored_type m;
559   template <typename A>
560   MOZ_IMPLICIT StorensRefPtrPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
561   passed_type PassAsParameter() { return m.get(); }
562 };
563 template<typename S>
564 struct IsParameterStorageClass<StorensRefPtrPassByPtr<S>>
565   : public mozilla::TrueType {};
566 
567 template<typename T>
568 struct StorePtrPassByPtr
569 {
570   typedef T* stored_type;
571   typedef T* passed_type;
572   stored_type m;
573   template <typename A>
574   MOZ_IMPLICIT StorePtrPassByPtr(A a) : m(a) {}
575   passed_type PassAsParameter() { return m; }
576 };
577 template<typename S>
578 struct IsParameterStorageClass<StorePtrPassByPtr<S>>
579   : public mozilla::TrueType {};
580 
581 template<typename T>
582 struct StoreConstPtrPassByConstPtr
583 {
584   typedef const T* stored_type;
585   typedef const T* passed_type;
586   stored_type m;
587   template <typename A>
588   MOZ_IMPLICIT StoreConstPtrPassByConstPtr(A a) : m(a) {}
589   passed_type PassAsParameter() { return m; }
590 };
591 template<typename S>
592 struct IsParameterStorageClass<StoreConstPtrPassByConstPtr<S>>
593   : public mozilla::TrueType {};
594 
595 template<typename T>
596 struct StoreCopyPassByConstPtr
597 {
598   typedef T stored_type;
599   typedef const T* passed_type;
600   stored_type m;
601   template <typename A>
602   MOZ_IMPLICIT StoreCopyPassByConstPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
603   passed_type PassAsParameter() { return &m; }
604 };
605 template<typename S>
606 struct IsParameterStorageClass<StoreCopyPassByConstPtr<S>>
607   : public mozilla::TrueType {};
608 
609 template<typename T>
610 struct StoreCopyPassByPtr
611 {
612   typedef T stored_type;
613   typedef T* passed_type;
614   stored_type m;
615   template <typename A>
616   MOZ_IMPLICIT StoreCopyPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
617   passed_type PassAsParameter() { return &m; }
618 };
619 template<typename S>
620 struct IsParameterStorageClass<StoreCopyPassByPtr<S>>
621   : public mozilla::TrueType {};
622 
623 namespace detail {
624 
625 template<typename TWithoutPointer>
626 struct NonnsISupportsPointerStorageClass
627   : mozilla::Conditional<mozilla::IsConst<TWithoutPointer>::value,
628                          StoreConstPtrPassByConstPtr<
629                            typename mozilla::RemoveConst<TWithoutPointer>::Type>,
630                          StorePtrPassByPtr<TWithoutPointer>>
631 {};
632 
633 template<typename>
634 struct SFINAE1True : mozilla::TrueType
635 {};
636 
637 template<class T>
638 static auto HasRefCountMethodsTest(int)
639     -> SFINAE1True<decltype(mozilla::DeclVal<T>().AddRef(),
640                             mozilla::DeclVal<T>().Release())>;
641 template<class>
642 static auto HasRefCountMethodsTest(long) -> mozilla::FalseType;
643 
644 template<class T>
645 struct HasRefCountMethods : decltype(HasRefCountMethodsTest<T>(0))
646 {};
647 
648 template<typename T>
649 struct IsRefcountedSmartPointer : public mozilla::FalseType
650 {};
651 
652 template<typename T>
653 struct IsRefcountedSmartPointer<RefPtr<T>> : public mozilla::TrueType
654 {};
655 
656 template<typename T>
657 struct IsRefcountedSmartPointer<nsCOMPtr<T>> : public mozilla::TrueType
658 {};
659 
660 template<typename T>
661 struct StripSmartPointer
662 {
663   typedef void Type;
664 };
665 
666 template<typename T>
667 struct StripSmartPointer<RefPtr<T>>
668 {
669   typedef T Type;
670 };
671 
672 template<typename T>
673 struct StripSmartPointer<nsCOMPtr<T>>
674 {
675   typedef T Type;
676 };
677 
678 template<typename TWithoutPointer>
679 struct PointerStorageClass
680   : mozilla::Conditional<HasRefCountMethods<TWithoutPointer>::value,
681                          StorensRefPtrPassByPtr<TWithoutPointer>,
682                          typename NonnsISupportsPointerStorageClass<
683                            TWithoutPointer
684                          >::Type>
685 {};
686 
687 template<typename TWithoutRef>
688 struct LValueReferenceStorageClass
689   : mozilla::Conditional<mozilla::IsConst<TWithoutRef>::value,
690                          StoreConstRefPassByConstLRef<
691                            typename mozilla::RemoveConst<TWithoutRef>::Type>,
692                          StoreRefPassByLRef<TWithoutRef>>
693 {};
694 
695 template<typename T>
696 struct SmartPointerStorageClass
697   : mozilla::Conditional<IsRefcountedSmartPointer<T>::value,
698                          StorensRefPtrPassByPtr<
699                            typename StripSmartPointer<T>::Type>,
700                          StoreCopyPassByValue<T>>
701 {};
702 
703 template<typename T>
704 struct NonLValueReferenceStorageClass
705   : mozilla::Conditional<mozilla::IsRvalueReference<T>::value,
706                          StoreCopyPassByRRef<
707                            typename mozilla::RemoveReference<T>::Type>,
708                          typename SmartPointerStorageClass<T>::Type>
709 {};
710 
711 template<typename T>
712 struct NonPointerStorageClass
713   : mozilla::Conditional<mozilla::IsLvalueReference<T>::value,
714                          typename LValueReferenceStorageClass<
715                            typename mozilla::RemoveReference<T>::Type
716                          >::Type,
717                          typename NonLValueReferenceStorageClass<T>::Type>
718 {};
719 
720 template<typename T>
721 struct NonParameterStorageClass
722   : mozilla::Conditional<mozilla::IsPointer<T>::value,
723                          typename PointerStorageClass<
724                            typename mozilla::RemovePointer<T>::Type
725                          >::Type,
726                          typename NonPointerStorageClass<T>::Type>
727 {};
728 
729 // Choose storage&passing strategy based on preferred storage type:
730 // - If IsParameterStorageClass<T>::value is true, use as-is.
731 // - RC*       -> StorensRefPtrPassByPtr<RC>     : Store RefPtr<RC>, pass RC*
732 //   ^^ RC quacks like a ref-counted type (i.e., has AddRef and Release methods)
733 // - const T*  -> StoreConstPtrPassByConstPtr<T> : Store const T*, pass const T*
734 // - T*        -> StorePtrPassByPtr<T>           : Store T*, pass T*.
735 // - const T&  -> StoreConstRefPassByConstLRef<T>: Store const T&, pass const T&.
736 // - T&        -> StoreRefPassByLRef<T>          : Store T&, pass T&.
737 // - T&&       -> StoreCopyPassByRRef<T>         : Store T, pass Move(T).
738 // - RefPtr<T>, nsCOMPtr<T>
739 //             -> StorensRefPtrPassByPtr<T>      : Store RefPtr<T>, pass T*
740 // - Other T   -> StoreCopyPassByValue<T>        : Store T, pass T.
741 // Other available explicit options:
742 // -              StoreCopyPassByConstLRef<T>    : Store T, pass const T&.
743 // -              StoreCopyPassByLRef<T>         : Store T, pass T& (of copy!)
744 // -              StoreCopyPassByConstPtr<T>     : Store T, pass const T*
745 // -              StoreCopyPassByPtr<T>          : Store T, pass T* (of copy!)
746 // Or create your own class with PassAsParameter() method, optional
747 // clean-up in destructor, and with associated IsParameterStorageClass<>.
748 template<typename T>
749 struct ParameterStorage
750   : mozilla::Conditional<IsParameterStorageClass<T>::value,
751                          T,
752                          typename NonParameterStorageClass<T>::Type>
753 {};
754 
755 } /* namespace detail */
756 
757 namespace mozilla {
758 
759 namespace detail {
760 
761 // struct used to store arguments and later apply them to a method.
762 template <typename... Ts>
763 struct RunnableMethodArguments final
764 {
765   Tuple<typename ::detail::ParameterStorage<Ts>::Type...> mArguments;
766   template <typename... As>
767   explicit RunnableMethodArguments(As&&... aArguments)
768     : mArguments(Forward<As>(aArguments)...)
769   {}
770   template<typename C, typename M, typename... Args, size_t... Indices>
771   static auto
772   applyImpl(C* o, M m, Tuple<Args...>& args, IndexSequence<Indices...>)
773       -> decltype(((*o).*m)(Get<Indices>(args).PassAsParameter()...))
774   {
775     return ((*o).*m)(Get<Indices>(args).PassAsParameter()...);
776   }
777   template<class C, typename M> auto apply(C* o, M m)
778       -> decltype(applyImpl(o, m, mArguments,
779                   typename IndexSequenceFor<Ts...>::Type()))
780   {
781     return applyImpl(o, m, mArguments,
782         typename IndexSequenceFor<Ts...>::Type());
783   }
784 };
785 
786 template<typename Method, bool Owning, bool Cancelable, typename... Storages>
787 class RunnableMethodImpl final
788   : public ::nsRunnableMethodTraits<Method, Owning, Cancelable>::base_type
789 {
790   typedef typename ::nsRunnableMethodTraits<Method, Owning, Cancelable>::class_type
791       ClassType;
792   ::nsRunnableMethodReceiver<ClassType, Owning> mReceiver;
793   Method mMethod;
794   RunnableMethodArguments<Storages...> mArgs;
795 private:
796   virtual ~RunnableMethodImpl() { Revoke(); };
797 public:
798   template<typename... Args>
799   explicit RunnableMethodImpl(ClassType* aObj, Method aMethod,
800                               Args&&... aArgs)
801     : mReceiver(aObj)
802     , mMethod(aMethod)
803     , mArgs(Forward<Args>(aArgs)...)
804   {
805     static_assert(sizeof...(Storages) == sizeof...(Args), "Storages and Args should have equal sizes");
806   }
807   NS_IMETHOD Run()
808   {
809     if (MOZ_LIKELY(mReceiver.Get())) {
810       mArgs.apply(mReceiver.Get(), mMethod);
811     }
812     return NS_OK;
813   }
814   nsresult Cancel() {
815     static_assert(Cancelable, "Don't use me!");
816     Revoke();
817     return NS_OK;
818   }
819   void Revoke() { mReceiver.Revoke(); }
820 };
821 
822 } // namespace detail
823 
824 // Use this template function like so:
825 //
826 //   nsCOMPtr<nsIRunnable> event =
827 //     mozilla::NewRunnableMethod(myObject, &MyClass::HandleEvent);
828 //   NS_DispatchToCurrentThread(event);
829 //
830 // Statically enforced constraints:
831 //  - myObject must be of (or implicitly convertible to) type MyClass
832 //  - MyClass must define AddRef and Release methods
833 //
834 
835 template<typename PtrType, typename Method>
836 already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, false>::base_type>
837 NewRunnableMethod(PtrType aPtr, Method aMethod)
838 {
839   return do_AddRef(new detail::RunnableMethodImpl<Method, true, false>(aPtr, aMethod));
840 }
841 
842 template<typename PtrType, typename Method>
843 already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, true>::base_type>
844 NewCancelableRunnableMethod(PtrType aPtr, Method aMethod)
845 {
846   return do_AddRef(new detail::RunnableMethodImpl<Method, true, true>(aPtr, aMethod));
847 }
848 
849 template<typename PtrType, typename Method>
850 already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, false>::base_type>
851 NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod)
852 {
853   return do_AddRef(new detail::RunnableMethodImpl<Method, false, false>(aPtr, aMethod));
854 }
855 
856 template<typename PtrType, typename Method>
857 already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, true>::base_type>
858 NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod)
859 {
860   return do_AddRef(new detail::RunnableMethodImpl<Method, false, true>(aPtr, aMethod));
861 }
862 
863 // Similar to NewRunnableMethod. Call like so:
864 // nsCOMPtr<nsIRunnable> event =
865 //   NewRunnableMethod<Types,...>(myObject, &MyClass::HandleEvent, myArg1,...);
866 // 'Types' are the stored type for each argument, see ParameterStorage for details.
867 template<typename... Storages, typename Method, typename PtrType, typename... Args>
868 already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, false>::base_type>
869 NewRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
870 {
871   static_assert(sizeof...(Storages) == sizeof...(Args),
872                 "<Storages...> size should be equal to number of arguments");
873   return do_AddRef(new detail::RunnableMethodImpl<Method, true, false, Storages...>(
874       aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
875 }
876 
877 template<typename... Storages, typename Method, typename PtrType, typename... Args>
878 already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, false>::base_type>
879 NewNonOwningRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
880 {
881   static_assert(sizeof...(Storages) == sizeof...(Args),
882                 "<Storages...> size should be equal to number of arguments");
883   return do_AddRef(new detail::RunnableMethodImpl<Method, false, false, Storages...>(
884       aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
885 }
886 
887 template<typename... Storages, typename Method, typename PtrType, typename... Args>
888 already_AddRefed<typename ::nsRunnableMethodTraits<Method, true, true>::base_type>
889 NewCancelableRunnableMethod(PtrType&& aPtr, Method aMethod, Args&&... aArgs)
890 {
891   static_assert(sizeof...(Storages) == sizeof...(Args),
892                 "<Storages...> size should be equal to number of arguments");
893   return do_AddRef(new detail::RunnableMethodImpl<Method, true, true, Storages...>(
894       aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
895 }
896 
897 template<typename... Storages, typename Method, typename PtrType, typename... Args>
898 already_AddRefed<typename ::nsRunnableMethodTraits<Method, false, true>::base_type>
899 NewNonOwningCancelableRunnableMethod(PtrType&& aPtr, Method aMethod,
900                                                 Args&&... aArgs)
901 {
902   static_assert(sizeof...(Storages) == sizeof...(Args),
903                 "<Storages...> size should be equal to number of arguments");
904   return do_AddRef(new detail::RunnableMethodImpl<Method, false, true, Storages...>(
905       aPtr, aMethod, mozilla::Forward<Args>(aArgs)...));
906 }
907 
908 } // namespace mozilla
909 
910 #endif  // XPCOM_GLUE_AVOID_NSPR
911 
912 // This class is designed to be used when you have an event class E that has a
913 // pointer back to resource class R.  If R goes away while E is still pending,
914 // then it is important to "revoke" E so that it does not try use R after R has
915 // been destroyed.  nsRevocableEventPtr makes it easy for R to manage such
916 // situations:
917 //
918 //   class R;
919 //
920 //   class E : public mozilla::Runnable {
921 //   public:
922 //     void Revoke() {
923 //       mResource = nullptr;
924 //     }
925 //   private:
926 //     R *mResource;
927 //   };
928 //
929 //   class R {
930 //   public:
931 //     void EventHandled() {
932 //       mEvent.Forget();
933 //     }
934 //   private:
935 //     nsRevocableEventPtr<E> mEvent;
936 //   };
937 //
938 //   void R::PostEvent() {
939 //     // Make sure any pending event is revoked.
940 //     mEvent->Revoke();
941 //
942 //     nsCOMPtr<nsIRunnable> event = new E();
943 //     if (NS_SUCCEEDED(NS_DispatchToCurrentThread(event))) {
944 //       // Keep pointer to event so we can revoke it.
945 //       mEvent = event;
946 //     }
947 //   }
948 //
949 //   NS_IMETHODIMP E::Run() {
950 //     if (!mResource)
951 //       return NS_OK;
952 //     ...
953 //     mResource->EventHandled();
954 //     return NS_OK;
955 //   }
956 //
957 template<class T>
958 class nsRevocableEventPtr
959 {
960 public:
961   nsRevocableEventPtr() : mEvent(nullptr) {}
962   ~nsRevocableEventPtr() { Revoke(); }
963 
964   const nsRevocableEventPtr& operator=(T* aEvent)
965   {
966     if (mEvent != aEvent) {
967       Revoke();
968       mEvent = aEvent;
969     }
970     return *this;
971   }
972 
973   const nsRevocableEventPtr& operator=(already_AddRefed<T> aEvent)
974   {
975     RefPtr<T> event = aEvent;
976     if (mEvent != event) {
977       Revoke();
978       mEvent = event.forget();
979     }
980     return *this;
981   }
982 
983   void Revoke()
984   {
985     if (mEvent) {
986       mEvent->Revoke();
987       mEvent = nullptr;
988     }
989   }
990 
991   void Forget() { mEvent = nullptr; }
992   bool IsPending() { return mEvent != nullptr; }
993   T* get() { return mEvent; }
994 
995 private:
996   // Not implemented
997   nsRevocableEventPtr(const nsRevocableEventPtr&);
998   nsRevocableEventPtr& operator=(const nsRevocableEventPtr&);
999 
1000   RefPtr<T> mEvent;
1001 };
1002 
1003 /**
1004  * A simple helper to suffix thread pool name
1005  * with incremental numbers.
1006  */
1007 class nsThreadPoolNaming
1008 {
1009 public:
1010   nsThreadPoolNaming() : mCounter(0) {}
1011 
1012   /**
1013    * Creates and sets next thread name as "<aPoolName> #<n>"
1014    * on the specified thread.  If no thread is specified (aThread
1015    * is null) then the name is synchronously set on the current thread.
1016    */
1017   void SetThreadPoolName(const nsACString& aPoolName,
1018                          nsIThread* aThread = nullptr);
1019 
1020 private:
1021   mozilla::Atomic<uint32_t> mCounter;
1022 
1023   nsThreadPoolNaming(const nsThreadPoolNaming&) = delete;
1024   void operator=(const nsThreadPoolNaming&) = delete;
1025 };
1026 
1027 /**
1028  * Thread priority in most operating systems affect scheduling, not IO.  This
1029  * helper is used to set the current thread to low IO priority for the lifetime
1030  * of the created object.  You can only use this low priority IO setting within
1031  * the context of the current thread.
1032  */
1033 class MOZ_STACK_CLASS nsAutoLowPriorityIO
1034 {
1035 public:
1036   nsAutoLowPriorityIO();
1037   ~nsAutoLowPriorityIO();
1038 
1039 private:
1040   bool lowIOPrioritySet;
1041 #if defined(XP_MACOSX)
1042   int oldPriority;
1043 #endif
1044 };
1045 
1046 void
1047 NS_SetMainThread();
1048 
1049 #endif  // nsThreadUtils_h__
1050