|
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/. */ |
|
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 "nsStringGlue.h" |
|
18 #include "nsCOMPtr.h" |
|
19 #include "nsAutoPtr.h" |
|
20 #include "mozilla/Likely.h" |
|
21 |
|
22 //----------------------------------------------------------------------------- |
|
23 // These methods are alternatives to the methods on nsIThreadManager, provided |
|
24 // for convenience. |
|
25 |
|
26 /** |
|
27 * Set name of the target thread. This operation is asynchronous. |
|
28 */ |
|
29 extern NS_COM_GLUE void |
|
30 NS_SetThreadName(nsIThread *thread, const nsACString &name); |
|
31 |
|
32 /** |
|
33 * Static length version of the above function checking length of the |
|
34 * name at compile time. |
|
35 */ |
|
36 template <size_t LEN> |
|
37 inline NS_COM_GLUE void |
|
38 NS_SetThreadName(nsIThread *thread, const char (&name)[LEN]) |
|
39 { |
|
40 static_assert(LEN <= 16, |
|
41 "Thread name must be no more than 16 characters"); |
|
42 NS_SetThreadName(thread, nsDependentCString(name)); |
|
43 } |
|
44 |
|
45 /** |
|
46 * Create a new thread, and optionally provide an initial event for the thread. |
|
47 * |
|
48 * @param result |
|
49 * The resulting nsIThread object. |
|
50 * @param initialEvent |
|
51 * The initial event to run on this thread. This parameter may be null. |
|
52 * @param stackSize |
|
53 * The size in bytes to reserve for the thread's stack. |
|
54 * |
|
55 * @returns NS_ERROR_INVALID_ARG |
|
56 * Indicates that the given name is not unique. |
|
57 */ |
|
58 extern NS_COM_GLUE NS_METHOD |
|
59 NS_NewThread(nsIThread **result, |
|
60 nsIRunnable *initialEvent = nullptr, |
|
61 uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE); |
|
62 |
|
63 /** |
|
64 * Creates a named thread, otherwise the same as NS_NewThread |
|
65 */ |
|
66 template <size_t LEN> |
|
67 inline NS_METHOD |
|
68 NS_NewNamedThread(const char (&name)[LEN], |
|
69 nsIThread **result, |
|
70 nsIRunnable *initialEvent = nullptr, |
|
71 uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE) |
|
72 { |
|
73 // Hold a ref while dispatching the initial event to match NS_NewThread() |
|
74 nsCOMPtr<nsIThread> thread; |
|
75 nsresult rv = NS_NewThread(getter_AddRefs(thread), nullptr, stackSize); |
|
76 if (NS_WARN_IF(NS_FAILED(rv))) |
|
77 return rv; |
|
78 NS_SetThreadName<LEN>(thread, name); |
|
79 if (initialEvent) { |
|
80 rv = thread->Dispatch(initialEvent, NS_DISPATCH_NORMAL); |
|
81 NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Initial event dispatch failed"); |
|
82 } |
|
83 |
|
84 *result = nullptr; |
|
85 thread.swap(*result); |
|
86 return rv; |
|
87 } |
|
88 |
|
89 /** |
|
90 * Get a reference to the current thread. |
|
91 * |
|
92 * @param result |
|
93 * The resulting nsIThread object. |
|
94 */ |
|
95 extern NS_COM_GLUE NS_METHOD |
|
96 NS_GetCurrentThread(nsIThread **result); |
|
97 |
|
98 /** |
|
99 * Dispatch the given event to the current thread. |
|
100 * |
|
101 * @param event |
|
102 * The event to dispatch. |
|
103 * |
|
104 * @returns NS_ERROR_INVALID_ARG |
|
105 * If event is null. |
|
106 */ |
|
107 extern NS_COM_GLUE NS_METHOD |
|
108 NS_DispatchToCurrentThread(nsIRunnable *event); |
|
109 |
|
110 /** |
|
111 * Dispatch the given event to the main thread. |
|
112 * |
|
113 * @param event |
|
114 * The event to dispatch. |
|
115 * @param dispatchFlags |
|
116 * The flags to pass to the main thread's dispatch method. |
|
117 * |
|
118 * @returns NS_ERROR_INVALID_ARG |
|
119 * If event is null. |
|
120 */ |
|
121 extern NS_COM_GLUE NS_METHOD |
|
122 NS_DispatchToMainThread(nsIRunnable *event, |
|
123 uint32_t dispatchFlags = NS_DISPATCH_NORMAL); |
|
124 |
|
125 #ifndef XPCOM_GLUE_AVOID_NSPR |
|
126 /** |
|
127 * Process all pending events for the given thread before returning. This |
|
128 * method simply calls ProcessNextEvent on the thread while HasPendingEvents |
|
129 * continues to return true and the time spent in NS_ProcessPendingEvents |
|
130 * does not exceed the given timeout value. |
|
131 * |
|
132 * @param thread |
|
133 * The thread object for which to process pending events. If null, then |
|
134 * events will be processed for the current thread. |
|
135 * @param timeout |
|
136 * The maximum number of milliseconds to spend processing pending events. |
|
137 * Events are not pre-empted to honor this timeout. Rather, the timeout |
|
138 * value is simply used to determine whether or not to process another event. |
|
139 * Pass PR_INTERVAL_NO_TIMEOUT to specify no timeout. |
|
140 */ |
|
141 extern NS_COM_GLUE NS_METHOD |
|
142 NS_ProcessPendingEvents(nsIThread *thread, |
|
143 PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT); |
|
144 #endif |
|
145 |
|
146 /** |
|
147 * Shortcut for nsIThread::HasPendingEvents. |
|
148 * |
|
149 * It is an error to call this function when the given thread is not the |
|
150 * current thread. This function will return false if called from some |
|
151 * other thread. |
|
152 * |
|
153 * @param thread |
|
154 * The current thread or null. |
|
155 * |
|
156 * @returns |
|
157 * A boolean value that if "true" indicates that there are pending events |
|
158 * in the current thread's event queue. |
|
159 */ |
|
160 extern NS_COM_GLUE bool |
|
161 NS_HasPendingEvents(nsIThread *thread = nullptr); |
|
162 |
|
163 /** |
|
164 * Shortcut for nsIThread::ProcessNextEvent. |
|
165 * |
|
166 * It is an error to call this function when the given thread is not the |
|
167 * current thread. This function will simply return false if called |
|
168 * from some other thread. |
|
169 * |
|
170 * @param thread |
|
171 * The current thread or null. |
|
172 * @param mayWait |
|
173 * A boolean parameter that if "true" indicates that the method may block |
|
174 * the calling thread to wait for a pending event. |
|
175 * |
|
176 * @returns |
|
177 * A boolean value that if "true" indicates that an event from the current |
|
178 * thread's event queue was processed. |
|
179 */ |
|
180 extern NS_COM_GLUE bool |
|
181 NS_ProcessNextEvent(nsIThread *thread = nullptr, bool mayWait = true); |
|
182 |
|
183 //----------------------------------------------------------------------------- |
|
184 // Helpers that work with nsCOMPtr: |
|
185 |
|
186 inline already_AddRefed<nsIThread> |
|
187 do_GetCurrentThread() { |
|
188 nsIThread *thread = nullptr; |
|
189 NS_GetCurrentThread(&thread); |
|
190 return already_AddRefed<nsIThread>(thread); |
|
191 } |
|
192 |
|
193 inline already_AddRefed<nsIThread> |
|
194 do_GetMainThread() { |
|
195 nsIThread *thread = nullptr; |
|
196 NS_GetMainThread(&thread); |
|
197 return already_AddRefed<nsIThread>(thread); |
|
198 } |
|
199 |
|
200 //----------------------------------------------------------------------------- |
|
201 |
|
202 #ifdef MOZILLA_INTERNAL_API |
|
203 // Fast access to the current thread. Do not release the returned pointer! If |
|
204 // you want to use this pointer from some other thread, then you will need to |
|
205 // AddRef it. Otherwise, you should only consider this pointer valid from code |
|
206 // running on the current thread. |
|
207 extern NS_COM_GLUE nsIThread *NS_GetCurrentThread(); |
|
208 #endif |
|
209 |
|
210 //----------------------------------------------------------------------------- |
|
211 |
|
212 #ifndef XPCOM_GLUE_AVOID_NSPR |
|
213 |
|
214 #undef IMETHOD_VISIBILITY |
|
215 #define IMETHOD_VISIBILITY NS_COM_GLUE |
|
216 |
|
217 // This class is designed to be subclassed. |
|
218 class NS_COM_GLUE nsRunnable : public nsIRunnable |
|
219 { |
|
220 public: |
|
221 NS_DECL_THREADSAFE_ISUPPORTS |
|
222 NS_DECL_NSIRUNNABLE |
|
223 |
|
224 nsRunnable() { |
|
225 } |
|
226 |
|
227 protected: |
|
228 virtual ~nsRunnable() { |
|
229 } |
|
230 }; |
|
231 |
|
232 // This class is designed to be subclassed. |
|
233 class NS_COM_GLUE nsCancelableRunnable : public nsICancelableRunnable |
|
234 { |
|
235 public: |
|
236 NS_DECL_THREADSAFE_ISUPPORTS |
|
237 NS_DECL_NSIRUNNABLE |
|
238 NS_DECL_NSICANCELABLERUNNABLE |
|
239 |
|
240 nsCancelableRunnable() { |
|
241 } |
|
242 |
|
243 protected: |
|
244 virtual ~nsCancelableRunnable() { |
|
245 } |
|
246 }; |
|
247 |
|
248 #undef IMETHOD_VISIBILITY |
|
249 #define IMETHOD_VISIBILITY NS_VISIBILITY_HIDDEN |
|
250 |
|
251 // An event that can be used to call a method on a class. The class type must |
|
252 // support reference counting. This event supports Revoke for use |
|
253 // with nsRevocableEventPtr. |
|
254 template <class ClassType, |
|
255 typename ReturnType = void, |
|
256 bool Owning = true> |
|
257 class nsRunnableMethod : public nsRunnable |
|
258 { |
|
259 public: |
|
260 virtual void Revoke() = 0; |
|
261 |
|
262 // These ReturnTypeEnforcer classes set up a blacklist for return types that |
|
263 // we know are not safe. The default ReturnTypeEnforcer compiles just fine but |
|
264 // already_AddRefed will not. |
|
265 template <typename OtherReturnType> |
|
266 class ReturnTypeEnforcer |
|
267 { |
|
268 public: |
|
269 typedef int ReturnTypeIsSafe; |
|
270 }; |
|
271 |
|
272 template <class T> |
|
273 class ReturnTypeEnforcer<already_AddRefed<T> > |
|
274 { |
|
275 // No ReturnTypeIsSafe makes this illegal! |
|
276 }; |
|
277 |
|
278 // Make sure this return type is safe. |
|
279 typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check; |
|
280 }; |
|
281 |
|
282 template <class ClassType, typename Arg, bool Owning> |
|
283 struct nsRunnableMethodReceiver { |
|
284 ClassType *mObj; |
|
285 Arg mArg; |
|
286 nsRunnableMethodReceiver(ClassType *obj, Arg arg) |
|
287 : mObj(obj) |
|
288 , mArg(arg) |
|
289 { NS_IF_ADDREF(mObj); } |
|
290 ~nsRunnableMethodReceiver() { Revoke(); } |
|
291 void Revoke() { NS_IF_RELEASE(mObj); } |
|
292 }; |
|
293 |
|
294 template <class ClassType, bool Owning> |
|
295 struct nsRunnableMethodReceiver<ClassType, void, Owning> { |
|
296 ClassType *mObj; |
|
297 nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) |
|
298 { NS_IF_ADDREF(mObj); } |
|
299 ~nsRunnableMethodReceiver() { Revoke(); } |
|
300 void Revoke() { NS_IF_RELEASE(mObj); } |
|
301 }; |
|
302 |
|
303 template <class ClassType> |
|
304 struct nsRunnableMethodReceiver<ClassType, void, false> { |
|
305 ClassType *mObj; |
|
306 nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) {} |
|
307 void Revoke() { mObj = nullptr; } |
|
308 }; |
|
309 |
|
310 template <typename Method, bool Owning> struct nsRunnableMethodTraits; |
|
311 |
|
312 template <class C, typename R, typename A, bool Owning> |
|
313 struct nsRunnableMethodTraits<R (C::*)(A), Owning> { |
|
314 typedef C class_type; |
|
315 typedef R return_type; |
|
316 typedef A arg_type; |
|
317 typedef nsRunnableMethod<C, R, Owning> base_type; |
|
318 }; |
|
319 |
|
320 template <class C, typename R, bool Owning> |
|
321 struct nsRunnableMethodTraits<R (C::*)(), Owning> { |
|
322 typedef C class_type; |
|
323 typedef R return_type; |
|
324 typedef void arg_type; |
|
325 typedef nsRunnableMethod<C, R, Owning> base_type; |
|
326 }; |
|
327 |
|
328 #ifdef NS_HAVE_STDCALL |
|
329 template <class C, typename R, typename A, bool Owning> |
|
330 struct nsRunnableMethodTraits<R (__stdcall C::*)(A), Owning> { |
|
331 typedef C class_type; |
|
332 typedef R return_type; |
|
333 typedef A arg_type; |
|
334 typedef nsRunnableMethod<C, R, Owning> base_type; |
|
335 }; |
|
336 |
|
337 template <class C, typename R, bool Owning> |
|
338 struct nsRunnableMethodTraits<R (NS_STDCALL C::*)(), Owning> { |
|
339 typedef C class_type; |
|
340 typedef R return_type; |
|
341 typedef void arg_type; |
|
342 typedef nsRunnableMethod<C, R, Owning> base_type; |
|
343 }; |
|
344 #endif |
|
345 |
|
346 template <typename Method, typename Arg, bool Owning> |
|
347 class nsRunnableMethodImpl |
|
348 : public nsRunnableMethodTraits<Method, Owning>::base_type |
|
349 { |
|
350 typedef typename nsRunnableMethodTraits<Method, Owning>::class_type ClassType; |
|
351 nsRunnableMethodReceiver<ClassType, Arg, Owning> mReceiver; |
|
352 Method mMethod; |
|
353 public: |
|
354 nsRunnableMethodImpl(ClassType *obj, |
|
355 Method method, |
|
356 Arg arg) |
|
357 : mReceiver(obj, arg) |
|
358 , mMethod(method) |
|
359 {} |
|
360 NS_IMETHOD Run() { |
|
361 if (MOZ_LIKELY(mReceiver.mObj)) |
|
362 ((*mReceiver.mObj).*mMethod)(mReceiver.mArg); |
|
363 return NS_OK; |
|
364 } |
|
365 void Revoke() { |
|
366 mReceiver.Revoke(); |
|
367 } |
|
368 }; |
|
369 |
|
370 template <typename Method, bool Owning> |
|
371 class nsRunnableMethodImpl<Method, void, Owning> |
|
372 : public nsRunnableMethodTraits<Method, Owning>::base_type |
|
373 { |
|
374 typedef typename nsRunnableMethodTraits<Method, Owning>::class_type ClassType; |
|
375 nsRunnableMethodReceiver<ClassType, void, Owning> mReceiver; |
|
376 Method mMethod; |
|
377 |
|
378 public: |
|
379 nsRunnableMethodImpl(ClassType *obj, |
|
380 Method method) |
|
381 : mReceiver(obj) |
|
382 , mMethod(method) |
|
383 {} |
|
384 |
|
385 NS_IMETHOD Run() { |
|
386 if (MOZ_LIKELY(mReceiver.mObj)) |
|
387 ((*mReceiver.mObj).*mMethod)(); |
|
388 return NS_OK; |
|
389 } |
|
390 |
|
391 void Revoke() { |
|
392 mReceiver.Revoke(); |
|
393 } |
|
394 }; |
|
395 |
|
396 // Use this template function like so: |
|
397 // |
|
398 // nsCOMPtr<nsIRunnable> event = |
|
399 // NS_NewRunnableMethod(myObject, &MyClass::HandleEvent); |
|
400 // NS_DispatchToCurrentThread(event); |
|
401 // |
|
402 // Statically enforced constraints: |
|
403 // - myObject must be of (or implicitly convertible to) type MyClass |
|
404 // - MyClass must defined AddRef and Release methods |
|
405 // |
|
406 template<typename PtrType, typename Method> |
|
407 typename nsRunnableMethodTraits<Method, true>::base_type* |
|
408 NS_NewRunnableMethod(PtrType ptr, Method method) |
|
409 { |
|
410 return new nsRunnableMethodImpl<Method, void, true>(ptr, method); |
|
411 } |
|
412 |
|
413 template<typename T> |
|
414 struct dependent_type |
|
415 { |
|
416 typedef T type; |
|
417 }; |
|
418 |
|
419 |
|
420 // Similar to NS_NewRunnableMethod. Call like so: |
|
421 // Type myArg; |
|
422 // nsCOMPtr<nsIRunnable> event = |
|
423 // NS_NewRunnableMethodWithArg<Type>(myObject, &MyClass::HandleEvent, myArg); |
|
424 template<typename Arg, typename Method, typename PtrType> |
|
425 typename nsRunnableMethodTraits<Method, true>::base_type* |
|
426 NS_NewRunnableMethodWithArg(PtrType ptr, Method method, typename dependent_type<Arg>::type arg) |
|
427 { |
|
428 return new nsRunnableMethodImpl<Method, Arg, true>(ptr, method, arg); |
|
429 } |
|
430 |
|
431 template<typename PtrType, typename Method> |
|
432 typename nsRunnableMethodTraits<Method, false>::base_type* |
|
433 NS_NewNonOwningRunnableMethod(PtrType ptr, Method method) |
|
434 { |
|
435 return new nsRunnableMethodImpl<Method, void, false>(ptr, method); |
|
436 } |
|
437 |
|
438 #endif // XPCOM_GLUE_AVOID_NSPR |
|
439 |
|
440 // This class is designed to be used when you have an event class E that has a |
|
441 // pointer back to resource class R. If R goes away while E is still pending, |
|
442 // then it is important to "revoke" E so that it does not try use R after R has |
|
443 // been destroyed. nsRevocableEventPtr makes it easy for R to manage such |
|
444 // situations: |
|
445 // |
|
446 // class R; |
|
447 // |
|
448 // class E : public nsRunnable { |
|
449 // public: |
|
450 // void Revoke() { |
|
451 // mResource = nullptr; |
|
452 // } |
|
453 // private: |
|
454 // R *mResource; |
|
455 // }; |
|
456 // |
|
457 // class R { |
|
458 // public: |
|
459 // void EventHandled() { |
|
460 // mEvent.Forget(); |
|
461 // } |
|
462 // private: |
|
463 // nsRevocableEventPtr<E> mEvent; |
|
464 // }; |
|
465 // |
|
466 // void R::PostEvent() { |
|
467 // // Make sure any pending event is revoked. |
|
468 // mEvent->Revoke(); |
|
469 // |
|
470 // nsCOMPtr<nsIRunnable> event = new E(); |
|
471 // if (NS_SUCCEEDED(NS_DispatchToCurrentThread(event))) { |
|
472 // // Keep pointer to event so we can revoke it. |
|
473 // mEvent = event; |
|
474 // } |
|
475 // } |
|
476 // |
|
477 // NS_IMETHODIMP E::Run() { |
|
478 // if (!mResource) |
|
479 // return NS_OK; |
|
480 // ... |
|
481 // mResource->EventHandled(); |
|
482 // return NS_OK; |
|
483 // } |
|
484 // |
|
485 template <class T> |
|
486 class nsRevocableEventPtr { |
|
487 public: |
|
488 nsRevocableEventPtr() |
|
489 : mEvent(nullptr) { |
|
490 } |
|
491 |
|
492 ~nsRevocableEventPtr() { |
|
493 Revoke(); |
|
494 } |
|
495 |
|
496 const nsRevocableEventPtr& operator=(T *event) { |
|
497 if (mEvent != event) { |
|
498 Revoke(); |
|
499 mEvent = event; |
|
500 } |
|
501 return *this; |
|
502 } |
|
503 |
|
504 void Revoke() { |
|
505 if (mEvent) { |
|
506 mEvent->Revoke(); |
|
507 mEvent = nullptr; |
|
508 } |
|
509 } |
|
510 |
|
511 void Forget() { |
|
512 mEvent = nullptr; |
|
513 } |
|
514 |
|
515 bool IsPending() { |
|
516 return mEvent != nullptr; |
|
517 } |
|
518 |
|
519 T *get() { return mEvent; } |
|
520 |
|
521 private: |
|
522 // Not implemented |
|
523 nsRevocableEventPtr(const nsRevocableEventPtr&); |
|
524 nsRevocableEventPtr& operator=(const nsRevocableEventPtr&); |
|
525 |
|
526 nsRefPtr<T> mEvent; |
|
527 }; |
|
528 |
|
529 /** |
|
530 * A simple helper to suffix thread pool name |
|
531 * with incremental numbers. |
|
532 */ |
|
533 class nsThreadPoolNaming |
|
534 { |
|
535 public: |
|
536 nsThreadPoolNaming() : mCounter(0) {} |
|
537 |
|
538 /** |
|
539 * Creates and sets next thread name as "<aPoolName> #<n>" |
|
540 * on the specified thread. If no thread is specified (aThread |
|
541 * is null) then the name is synchronously set on the current thread. |
|
542 */ |
|
543 void SetThreadPoolName(const nsACString & aPoolName, |
|
544 nsIThread * aThread = nullptr); |
|
545 |
|
546 private: |
|
547 volatile uint32_t mCounter; |
|
548 |
|
549 nsThreadPoolNaming(const nsThreadPoolNaming &) MOZ_DELETE; |
|
550 void operator=(const nsThreadPoolNaming &) MOZ_DELETE; |
|
551 }; |
|
552 |
|
553 /** |
|
554 * Thread priority in most operating systems affect scheduling, not IO. This |
|
555 * helper is used to set the current thread to low IO priority for the lifetime |
|
556 * of the created object. You can only use this low priority IO setting within |
|
557 * the context of the current thread. |
|
558 */ |
|
559 class MOZ_STACK_CLASS nsAutoLowPriorityIO |
|
560 { |
|
561 public: |
|
562 nsAutoLowPriorityIO(); |
|
563 ~nsAutoLowPriorityIO(); |
|
564 |
|
565 private: |
|
566 bool lowIOPrioritySet; |
|
567 #if defined(XP_MACOSX) |
|
568 int oldPriority; |
|
569 #endif |
|
570 }; |
|
571 |
|
572 |
|
573 |
|
574 #endif // nsThreadUtils_h__ |