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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla code.
17 : *
18 : * The Initial Developer of the Original Code is Google Inc.
19 : * Portions created by the Initial Developer are Copyright (C) 2006
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Darin Fisher <darin@meer.net>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #ifndef nsThreadUtils_h__
40 : #define nsThreadUtils_h__
41 :
42 : #include "prthread.h"
43 : #include "prinrval.h"
44 : #include "nsIThreadManager.h"
45 : #include "nsIThread.h"
46 : #include "nsIRunnable.h"
47 : #include "nsStringGlue.h"
48 : #include "nsCOMPtr.h"
49 : #include "nsAutoPtr.h"
50 : #include "mozilla/threads/nsThreadIDs.h"
51 :
52 : // This is needed on some systems to prevent collisions between the symbols
53 : // appearing in xpcom_core and xpcomglue. It may be unnecessary in the future
54 : // with better toolchain support.
55 : #ifdef MOZILLA_INTERNAL_API
56 : # define NS_NewThread NS_NewThread_P
57 : # define NS_GetCurrentThread NS_GetCurrentThread_P
58 : # define NS_GetMainThread NS_GetMainThread_P
59 : # define NS_IsMainThread NS_IsMainThread_P
60 : # define NS_DispatchToCurrentThread NS_DispatchToCurrentThread_P
61 : # define NS_DispatchToMainThread NS_DispatchToMainThread_P
62 : # define NS_ProcessPendingEvents NS_ProcessPendingEvents_P
63 : # define NS_HasPendingEvents NS_HasPendingEvents_P
64 : # define NS_ProcessNextEvent NS_ProcessNextEvent_P
65 : #endif
66 :
67 : //-----------------------------------------------------------------------------
68 : // These methods are alternatives to the methods on nsIThreadManager, provided
69 : // for convenience.
70 :
71 : /**
72 : * Create a new thread, and optionally provide an initial event for the thread.
73 : *
74 : * @param result
75 : * The resulting nsIThread object.
76 : * @param initialEvent
77 : * The initial event to run on this thread. This parameter may be null.
78 : * @param stackSize
79 : * The size in bytes to reserve for the thread's stack.
80 : *
81 : * @returns NS_ERROR_INVALID_ARG
82 : * Indicates that the given name is not unique.
83 : */
84 : extern NS_COM_GLUE NS_METHOD
85 : NS_NewThread(nsIThread **result,
86 : nsIRunnable *initialEvent = nsnull,
87 : PRUint32 stackSize = nsIThreadManager::DEFAULT_STACK_SIZE);
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 : * Get a reference to the main thread.
100 : *
101 : * @param result
102 : * The resulting nsIThread object.
103 : */
104 : extern NS_COM_GLUE NS_METHOD
105 : NS_GetMainThread(nsIThread **result);
106 :
107 : #if defined(MOZILLA_INTERNAL_API) && defined(XP_WIN)
108 : bool NS_IsMainThread();
109 : #elif defined(MOZILLA_INTERNAL_API) && defined(NS_TLS)
110 : // This is defined in nsThreadManager.cpp and initialized to `Main` for the
111 : // main thread by nsThreadManager::Init.
112 : extern NS_TLS mozilla::threads::ID gTLSThreadID;
113 55815729 : inline bool NS_IsMainThread()
114 : {
115 55815729 : return gTLSThreadID == mozilla::threads::Main;
116 : }
117 : #else
118 : /**
119 : * Test to see if the current thread is the main thread.
120 : *
121 : * @returns true if the current thread is the main thread, and false
122 : * otherwise.
123 : */
124 : extern NS_COM_GLUE bool NS_IsMainThread();
125 : #endif
126 :
127 : /**
128 : * Dispatch the given event to the current thread.
129 : *
130 : * @param event
131 : * The event to dispatch.
132 : *
133 : * @returns NS_ERROR_INVALID_ARG
134 : * If event is null.
135 : */
136 : extern NS_COM_GLUE NS_METHOD
137 : NS_DispatchToCurrentThread(nsIRunnable *event);
138 :
139 : /**
140 : * Dispatch the given event to the main thread.
141 : *
142 : * @param event
143 : * The event to dispatch.
144 : * @param dispatchFlags
145 : * The flags to pass to the main thread's dispatch method.
146 : *
147 : * @returns NS_ERROR_INVALID_ARG
148 : * If event is null.
149 : */
150 : extern NS_COM_GLUE NS_METHOD
151 : NS_DispatchToMainThread(nsIRunnable *event,
152 : PRUint32 dispatchFlags = NS_DISPATCH_NORMAL);
153 :
154 : #ifndef XPCOM_GLUE_AVOID_NSPR
155 : /**
156 : * Process all pending events for the given thread before returning. This
157 : * method simply calls ProcessNextEvent on the thread while HasPendingEvents
158 : * continues to return true and the time spent in NS_ProcessPendingEvents
159 : * does not exceed the given timeout value.
160 : *
161 : * @param thread
162 : * The thread object for which to process pending events. If null, then
163 : * events will be processed for the current thread.
164 : * @param timeout
165 : * The maximum number of milliseconds to spend processing pending events.
166 : * Events are not pre-empted to honor this timeout. Rather, the timeout
167 : * value is simply used to determine whether or not to process another event.
168 : * Pass PR_INTERVAL_NO_TIMEOUT to specify no timeout.
169 : */
170 : extern NS_COM_GLUE NS_METHOD
171 : NS_ProcessPendingEvents(nsIThread *thread,
172 : PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT);
173 : #endif
174 :
175 : /**
176 : * Shortcut for nsIThread::HasPendingEvents.
177 : *
178 : * It is an error to call this function when the given thread is not the
179 : * current thread. This function will return false if called from some
180 : * other thread.
181 : *
182 : * @param thread
183 : * The current thread or null.
184 : *
185 : * @returns
186 : * A boolean value that if "true" indicates that there are pending events
187 : * in the current thread's event queue.
188 : */
189 : extern NS_COM_GLUE bool
190 : NS_HasPendingEvents(nsIThread *thread = nsnull);
191 :
192 : /**
193 : * Shortcut for nsIThread::ProcessNextEvent.
194 : *
195 : * It is an error to call this function when the given thread is not the
196 : * current thread. This function will simply return false if called
197 : * from some other thread.
198 : *
199 : * @param thread
200 : * The current thread or null.
201 : * @param mayWait
202 : * A boolean parameter that if "true" indicates that the method may block
203 : * the calling thread to wait for a pending event.
204 : *
205 : * @returns
206 : * A boolean value that if "true" indicates that an event from the current
207 : * thread's event queue was processed.
208 : */
209 : extern NS_COM_GLUE bool
210 : NS_ProcessNextEvent(nsIThread *thread = nsnull, bool mayWait = true);
211 :
212 : //-----------------------------------------------------------------------------
213 : // Helpers that work with nsCOMPtr:
214 :
215 : inline already_AddRefed<nsIThread>
216 55473 : do_GetCurrentThread() {
217 55473 : nsIThread *thread = nsnull;
218 55473 : NS_GetCurrentThread(&thread);
219 55473 : return already_AddRefed<nsIThread>(thread);
220 : }
221 :
222 : inline already_AddRefed<nsIThread>
223 3780 : do_GetMainThread() {
224 3780 : nsIThread *thread = nsnull;
225 3780 : NS_GetMainThread(&thread);
226 3780 : return already_AddRefed<nsIThread>(thread);
227 : }
228 :
229 : //-----------------------------------------------------------------------------
230 :
231 : #ifdef MOZILLA_INTERNAL_API
232 : // Fast access to the current thread. Do not release the returned pointer! If
233 : // you want to use this pointer from some other thread, then you will need to
234 : // AddRef it. Otherwise, you should only consider this pointer valid from code
235 : // running on the current thread.
236 : extern NS_COM_GLUE nsIThread *NS_GetCurrentThread();
237 : #endif
238 :
239 : //-----------------------------------------------------------------------------
240 :
241 : #ifndef XPCOM_GLUE_AVOID_NSPR
242 :
243 : #undef IMETHOD_VISIBILITY
244 : #define IMETHOD_VISIBILITY NS_COM_GLUE
245 :
246 : // This class is designed to be subclassed.
247 : class NS_COM_GLUE nsRunnable : public nsIRunnable
248 : {
249 : public:
250 : NS_DECL_ISUPPORTS
251 : NS_DECL_NSIRUNNABLE
252 :
253 182978 : nsRunnable() {
254 182977 : }
255 :
256 : protected:
257 183272 : virtual ~nsRunnable() {
258 366544 : }
259 : };
260 :
261 : #undef IMETHOD_VISIBILITY
262 : #define IMETHOD_VISIBILITY NS_VISIBILITY_HIDDEN
263 :
264 : // An event that can be used to call a method on a class. The class type must
265 : // support reference counting. This event supports Revoke for use
266 : // with nsRevocableEventPtr.
267 : template <class ClassType,
268 : typename ReturnType = void,
269 : bool Owning = true>
270 : class nsRunnableMethod : public nsRunnable
271 11448 : {
272 : public:
273 : virtual void Revoke() = 0;
274 :
275 : // These ReturnTypeEnforcer classes set up a blacklist for return types that
276 : // we know are not safe. The default ReturnTypeEnforcer compiles just fine but
277 : // already_AddRefed will not.
278 : template <typename OtherReturnType>
279 : class ReturnTypeEnforcer
280 : {
281 : public:
282 : typedef int ReturnTypeIsSafe;
283 : };
284 :
285 : template <class T>
286 : class ReturnTypeEnforcer<already_AddRefed<T> >
287 : {
288 : // No ReturnTypeIsSafe makes this illegal!
289 : };
290 :
291 : // Make sure this return type is safe.
292 : typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check;
293 : };
294 :
295 : template <class ClassType, bool Owning>
296 : struct nsRunnableMethodReceiver {
297 : ClassType *mObj;
298 2724 : nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) { NS_IF_ADDREF(mObj); }
299 2724 : ~nsRunnableMethodReceiver() { Revoke(); }
300 2724 : void Revoke() { NS_IF_RELEASE(mObj); }
301 : };
302 :
303 : template <class ClassType>
304 : struct nsRunnableMethodReceiver<ClassType, false> {
305 : ClassType *mObj;
306 1092 : nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) {}
307 0 : void Revoke() { mObj = nsnull; }
308 : };
309 :
310 : template <typename Method, bool Owning> struct nsRunnableMethodTraits;
311 :
312 : template <class C, typename R, bool Owning>
313 : struct nsRunnableMethodTraits<R (C::*)(), Owning> {
314 : typedef C class_type;
315 : typedef R return_type;
316 : typedef nsRunnableMethod<C, R, Owning> base_type;
317 : };
318 :
319 : #ifdef HAVE_STDCALL
320 : template <class C, typename R, bool Owning>
321 : struct nsRunnableMethodTraits<R (__stdcall C::*)(), Owning> {
322 : typedef C class_type;
323 : typedef R return_type;
324 : typedef nsRunnableMethod<C, R, Owning> base_type;
325 : };
326 : #endif
327 :
328 : template <typename Method, bool Owning>
329 : class nsRunnableMethodImpl
330 : : public nsRunnableMethodTraits<Method, Owning>::base_type
331 15264 : {
332 : typedef typename nsRunnableMethodTraits<Method, Owning>::class_type ClassType;
333 : nsRunnableMethodReceiver<ClassType, Owning> mReceiver;
334 : Method mMethod;
335 :
336 : public:
337 3816 : nsRunnableMethodImpl(ClassType *obj,
338 : Method method)
339 : : mReceiver(obj)
340 3816 : , mMethod(method)
341 3816 : {}
342 :
343 3762 : NS_IMETHOD Run() {
344 3762 : if (NS_LIKELY(mReceiver.mObj))
345 3762 : ((*mReceiver.mObj).*mMethod)();
346 3762 : return NS_OK;
347 : }
348 :
349 0 : void Revoke() {
350 0 : mReceiver.Revoke();
351 0 : }
352 : };
353 :
354 : // Use this template function like so:
355 : //
356 : // nsCOMPtr<nsIRunnable> event =
357 : // NS_NewRunnableMethod(myObject, &MyClass::HandleEvent);
358 : // NS_DispatchToCurrentThread(event);
359 : //
360 : // Statically enforced constraints:
361 : // - myObject must be of (or implicitly convertible to) type MyClass
362 : // - MyClass must defined AddRef and Release methods
363 : //
364 : template<typename PtrType, typename Method>
365 : typename nsRunnableMethodTraits<Method, true>::base_type*
366 2724 : NS_NewRunnableMethod(PtrType ptr, Method method)
367 : {
368 3993 : return new nsRunnableMethodImpl<Method, true>(ptr, method);
369 : }
370 :
371 : template<typename PtrType, typename Method>
372 : typename nsRunnableMethodTraits<Method, false>::base_type*
373 1092 : NS_NewNonOwningRunnableMethod(PtrType ptr, Method method)
374 : {
375 1092 : return new nsRunnableMethodImpl<Method, false>(ptr, method);
376 : }
377 :
378 : #endif // XPCOM_GLUE_AVOID_NSPR
379 :
380 : // This class is designed to be used when you have an event class E that has a
381 : // pointer back to resource class R. If R goes away while E is still pending,
382 : // then it is important to "revoke" E so that it does not try use R after R has
383 : // been destroyed. nsRevocableEventPtr makes it easy for R to manage such
384 : // situations:
385 : //
386 : // class R;
387 : //
388 : // class E : public nsRunnable {
389 : // public:
390 : // void Revoke() {
391 : // mResource = nsnull;
392 : // }
393 : // private:
394 : // R *mResource;
395 : // };
396 : //
397 : // class R {
398 : // public:
399 : // void EventHandled() {
400 : // mEvent.Forget();
401 : // }
402 : // private:
403 : // nsRevocableEventPtr<E> mEvent;
404 : // };
405 : //
406 : // void R::PostEvent() {
407 : // // Make sure any pending event is revoked.
408 : // mEvent->Revoke();
409 : //
410 : // nsCOMPtr<nsIRunnable> event = new E();
411 : // if (NS_SUCCEEDED(NS_DispatchToCurrentThread(event))) {
412 : // // Keep pointer to event so we can revoke it.
413 : // mEvent = event;
414 : // }
415 : // }
416 : //
417 : // NS_IMETHODIMP E::Run() {
418 : // if (!mResource)
419 : // return NS_OK;
420 : // ...
421 : // mResource->EventHandled();
422 : // return NS_OK;
423 : // }
424 : //
425 : template <class T>
426 : class nsRevocableEventPtr {
427 : public:
428 2324 : nsRevocableEventPtr()
429 2324 : : mEvent(nsnull) {
430 2324 : }
431 :
432 2322 : ~nsRevocableEventPtr() {
433 2322 : Revoke();
434 2322 : }
435 :
436 1038 : const nsRevocableEventPtr& operator=(T *event) {
437 1038 : if (mEvent != event) {
438 1038 : Revoke();
439 1038 : mEvent = event;
440 : }
441 1038 : return *this;
442 : }
443 :
444 5902 : void Revoke() {
445 5902 : if (mEvent) {
446 0 : mEvent->Revoke();
447 0 : mEvent = nsnull;
448 : }
449 5902 : }
450 :
451 1038 : void Forget() {
452 1038 : mEvent = nsnull;
453 1038 : }
454 :
455 1038 : bool IsPending() {
456 1038 : return mEvent != nsnull;
457 : }
458 :
459 1038 : T *get() { return mEvent; }
460 :
461 : private:
462 : // Not implemented
463 : nsRevocableEventPtr(const nsRevocableEventPtr&);
464 : nsRevocableEventPtr& operator=(const nsRevocableEventPtr&);
465 :
466 : nsRefPtr<T> mEvent;
467 : };
468 :
469 : #endif // nsThreadUtils_h__
|