1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/xpcom/glue/nsThreadUtils.h Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,574 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#ifndef nsThreadUtils_h__ 1.11 +#define nsThreadUtils_h__ 1.12 + 1.13 +#include "prthread.h" 1.14 +#include "prinrval.h" 1.15 +#include "MainThreadUtils.h" 1.16 +#include "nsIThreadManager.h" 1.17 +#include "nsIThread.h" 1.18 +#include "nsIRunnable.h" 1.19 +#include "nsICancelableRunnable.h" 1.20 +#include "nsStringGlue.h" 1.21 +#include "nsCOMPtr.h" 1.22 +#include "nsAutoPtr.h" 1.23 +#include "mozilla/Likely.h" 1.24 + 1.25 +//----------------------------------------------------------------------------- 1.26 +// These methods are alternatives to the methods on nsIThreadManager, provided 1.27 +// for convenience. 1.28 + 1.29 +/** 1.30 + * Set name of the target thread. This operation is asynchronous. 1.31 + */ 1.32 +extern NS_COM_GLUE void 1.33 +NS_SetThreadName(nsIThread *thread, const nsACString &name); 1.34 + 1.35 +/** 1.36 + * Static length version of the above function checking length of the 1.37 + * name at compile time. 1.38 + */ 1.39 +template <size_t LEN> 1.40 +inline NS_COM_GLUE void 1.41 +NS_SetThreadName(nsIThread *thread, const char (&name)[LEN]) 1.42 +{ 1.43 + static_assert(LEN <= 16, 1.44 + "Thread name must be no more than 16 characters"); 1.45 + NS_SetThreadName(thread, nsDependentCString(name)); 1.46 +} 1.47 + 1.48 +/** 1.49 + * Create a new thread, and optionally provide an initial event for the thread. 1.50 + * 1.51 + * @param result 1.52 + * The resulting nsIThread object. 1.53 + * @param initialEvent 1.54 + * The initial event to run on this thread. This parameter may be null. 1.55 + * @param stackSize 1.56 + * The size in bytes to reserve for the thread's stack. 1.57 + * 1.58 + * @returns NS_ERROR_INVALID_ARG 1.59 + * Indicates that the given name is not unique. 1.60 + */ 1.61 +extern NS_COM_GLUE NS_METHOD 1.62 +NS_NewThread(nsIThread **result, 1.63 + nsIRunnable *initialEvent = nullptr, 1.64 + uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE); 1.65 + 1.66 +/** 1.67 + * Creates a named thread, otherwise the same as NS_NewThread 1.68 + */ 1.69 +template <size_t LEN> 1.70 +inline NS_METHOD 1.71 +NS_NewNamedThread(const char (&name)[LEN], 1.72 + nsIThread **result, 1.73 + nsIRunnable *initialEvent = nullptr, 1.74 + uint32_t stackSize = nsIThreadManager::DEFAULT_STACK_SIZE) 1.75 +{ 1.76 + // Hold a ref while dispatching the initial event to match NS_NewThread() 1.77 + nsCOMPtr<nsIThread> thread; 1.78 + nsresult rv = NS_NewThread(getter_AddRefs(thread), nullptr, stackSize); 1.79 + if (NS_WARN_IF(NS_FAILED(rv))) 1.80 + return rv; 1.81 + NS_SetThreadName<LEN>(thread, name); 1.82 + if (initialEvent) { 1.83 + rv = thread->Dispatch(initialEvent, NS_DISPATCH_NORMAL); 1.84 + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Initial event dispatch failed"); 1.85 + } 1.86 + 1.87 + *result = nullptr; 1.88 + thread.swap(*result); 1.89 + return rv; 1.90 +} 1.91 + 1.92 +/** 1.93 + * Get a reference to the current thread. 1.94 + * 1.95 + * @param result 1.96 + * The resulting nsIThread object. 1.97 + */ 1.98 +extern NS_COM_GLUE NS_METHOD 1.99 +NS_GetCurrentThread(nsIThread **result); 1.100 + 1.101 +/** 1.102 + * Dispatch the given event to the current thread. 1.103 + * 1.104 + * @param event 1.105 + * The event to dispatch. 1.106 + * 1.107 + * @returns NS_ERROR_INVALID_ARG 1.108 + * If event is null. 1.109 + */ 1.110 +extern NS_COM_GLUE NS_METHOD 1.111 +NS_DispatchToCurrentThread(nsIRunnable *event); 1.112 + 1.113 +/** 1.114 + * Dispatch the given event to the main thread. 1.115 + * 1.116 + * @param event 1.117 + * The event to dispatch. 1.118 + * @param dispatchFlags 1.119 + * The flags to pass to the main thread's dispatch method. 1.120 + * 1.121 + * @returns NS_ERROR_INVALID_ARG 1.122 + * If event is null. 1.123 + */ 1.124 +extern NS_COM_GLUE NS_METHOD 1.125 +NS_DispatchToMainThread(nsIRunnable *event, 1.126 + uint32_t dispatchFlags = NS_DISPATCH_NORMAL); 1.127 + 1.128 +#ifndef XPCOM_GLUE_AVOID_NSPR 1.129 +/** 1.130 + * Process all pending events for the given thread before returning. This 1.131 + * method simply calls ProcessNextEvent on the thread while HasPendingEvents 1.132 + * continues to return true and the time spent in NS_ProcessPendingEvents 1.133 + * does not exceed the given timeout value. 1.134 + * 1.135 + * @param thread 1.136 + * The thread object for which to process pending events. If null, then 1.137 + * events will be processed for the current thread. 1.138 + * @param timeout 1.139 + * The maximum number of milliseconds to spend processing pending events. 1.140 + * Events are not pre-empted to honor this timeout. Rather, the timeout 1.141 + * value is simply used to determine whether or not to process another event. 1.142 + * Pass PR_INTERVAL_NO_TIMEOUT to specify no timeout. 1.143 + */ 1.144 +extern NS_COM_GLUE NS_METHOD 1.145 +NS_ProcessPendingEvents(nsIThread *thread, 1.146 + PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT); 1.147 +#endif 1.148 + 1.149 +/** 1.150 + * Shortcut for nsIThread::HasPendingEvents. 1.151 + * 1.152 + * It is an error to call this function when the given thread is not the 1.153 + * current thread. This function will return false if called from some 1.154 + * other thread. 1.155 + * 1.156 + * @param thread 1.157 + * The current thread or null. 1.158 + * 1.159 + * @returns 1.160 + * A boolean value that if "true" indicates that there are pending events 1.161 + * in the current thread's event queue. 1.162 + */ 1.163 +extern NS_COM_GLUE bool 1.164 +NS_HasPendingEvents(nsIThread *thread = nullptr); 1.165 + 1.166 +/** 1.167 + * Shortcut for nsIThread::ProcessNextEvent. 1.168 + * 1.169 + * It is an error to call this function when the given thread is not the 1.170 + * current thread. This function will simply return false if called 1.171 + * from some other thread. 1.172 + * 1.173 + * @param thread 1.174 + * The current thread or null. 1.175 + * @param mayWait 1.176 + * A boolean parameter that if "true" indicates that the method may block 1.177 + * the calling thread to wait for a pending event. 1.178 + * 1.179 + * @returns 1.180 + * A boolean value that if "true" indicates that an event from the current 1.181 + * thread's event queue was processed. 1.182 + */ 1.183 +extern NS_COM_GLUE bool 1.184 +NS_ProcessNextEvent(nsIThread *thread = nullptr, bool mayWait = true); 1.185 + 1.186 +//----------------------------------------------------------------------------- 1.187 +// Helpers that work with nsCOMPtr: 1.188 + 1.189 +inline already_AddRefed<nsIThread> 1.190 +do_GetCurrentThread() { 1.191 + nsIThread *thread = nullptr; 1.192 + NS_GetCurrentThread(&thread); 1.193 + return already_AddRefed<nsIThread>(thread); 1.194 +} 1.195 + 1.196 +inline already_AddRefed<nsIThread> 1.197 +do_GetMainThread() { 1.198 + nsIThread *thread = nullptr; 1.199 + NS_GetMainThread(&thread); 1.200 + return already_AddRefed<nsIThread>(thread); 1.201 +} 1.202 + 1.203 +//----------------------------------------------------------------------------- 1.204 + 1.205 +#ifdef MOZILLA_INTERNAL_API 1.206 +// Fast access to the current thread. Do not release the returned pointer! If 1.207 +// you want to use this pointer from some other thread, then you will need to 1.208 +// AddRef it. Otherwise, you should only consider this pointer valid from code 1.209 +// running on the current thread. 1.210 +extern NS_COM_GLUE nsIThread *NS_GetCurrentThread(); 1.211 +#endif 1.212 + 1.213 +//----------------------------------------------------------------------------- 1.214 + 1.215 +#ifndef XPCOM_GLUE_AVOID_NSPR 1.216 + 1.217 +#undef IMETHOD_VISIBILITY 1.218 +#define IMETHOD_VISIBILITY NS_COM_GLUE 1.219 + 1.220 +// This class is designed to be subclassed. 1.221 +class NS_COM_GLUE nsRunnable : public nsIRunnable 1.222 +{ 1.223 +public: 1.224 + NS_DECL_THREADSAFE_ISUPPORTS 1.225 + NS_DECL_NSIRUNNABLE 1.226 + 1.227 + nsRunnable() { 1.228 + } 1.229 + 1.230 +protected: 1.231 + virtual ~nsRunnable() { 1.232 + } 1.233 +}; 1.234 + 1.235 +// This class is designed to be subclassed. 1.236 +class NS_COM_GLUE nsCancelableRunnable : public nsICancelableRunnable 1.237 +{ 1.238 +public: 1.239 + NS_DECL_THREADSAFE_ISUPPORTS 1.240 + NS_DECL_NSIRUNNABLE 1.241 + NS_DECL_NSICANCELABLERUNNABLE 1.242 + 1.243 + nsCancelableRunnable() { 1.244 + } 1.245 + 1.246 +protected: 1.247 + virtual ~nsCancelableRunnable() { 1.248 + } 1.249 +}; 1.250 + 1.251 +#undef IMETHOD_VISIBILITY 1.252 +#define IMETHOD_VISIBILITY NS_VISIBILITY_HIDDEN 1.253 + 1.254 +// An event that can be used to call a method on a class. The class type must 1.255 +// support reference counting. This event supports Revoke for use 1.256 +// with nsRevocableEventPtr. 1.257 +template <class ClassType, 1.258 + typename ReturnType = void, 1.259 + bool Owning = true> 1.260 +class nsRunnableMethod : public nsRunnable 1.261 +{ 1.262 +public: 1.263 + virtual void Revoke() = 0; 1.264 + 1.265 + // These ReturnTypeEnforcer classes set up a blacklist for return types that 1.266 + // we know are not safe. The default ReturnTypeEnforcer compiles just fine but 1.267 + // already_AddRefed will not. 1.268 + template <typename OtherReturnType> 1.269 + class ReturnTypeEnforcer 1.270 + { 1.271 + public: 1.272 + typedef int ReturnTypeIsSafe; 1.273 + }; 1.274 + 1.275 + template <class T> 1.276 + class ReturnTypeEnforcer<already_AddRefed<T> > 1.277 + { 1.278 + // No ReturnTypeIsSafe makes this illegal! 1.279 + }; 1.280 + 1.281 + // Make sure this return type is safe. 1.282 + typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check; 1.283 +}; 1.284 + 1.285 +template <class ClassType, typename Arg, bool Owning> 1.286 +struct nsRunnableMethodReceiver { 1.287 + ClassType *mObj; 1.288 + Arg mArg; 1.289 + nsRunnableMethodReceiver(ClassType *obj, Arg arg) 1.290 + : mObj(obj) 1.291 + , mArg(arg) 1.292 + { NS_IF_ADDREF(mObj); } 1.293 + ~nsRunnableMethodReceiver() { Revoke(); } 1.294 + void Revoke() { NS_IF_RELEASE(mObj); } 1.295 +}; 1.296 + 1.297 +template <class ClassType, bool Owning> 1.298 +struct nsRunnableMethodReceiver<ClassType, void, Owning> { 1.299 + ClassType *mObj; 1.300 + nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) 1.301 + { NS_IF_ADDREF(mObj); } 1.302 + ~nsRunnableMethodReceiver() { Revoke(); } 1.303 + void Revoke() { NS_IF_RELEASE(mObj); } 1.304 +}; 1.305 + 1.306 +template <class ClassType> 1.307 +struct nsRunnableMethodReceiver<ClassType, void, false> { 1.308 + ClassType *mObj; 1.309 + nsRunnableMethodReceiver(ClassType *obj) : mObj(obj) {} 1.310 + void Revoke() { mObj = nullptr; } 1.311 +}; 1.312 + 1.313 +template <typename Method, bool Owning> struct nsRunnableMethodTraits; 1.314 + 1.315 +template <class C, typename R, typename A, bool Owning> 1.316 +struct nsRunnableMethodTraits<R (C::*)(A), Owning> { 1.317 + typedef C class_type; 1.318 + typedef R return_type; 1.319 + typedef A arg_type; 1.320 + typedef nsRunnableMethod<C, R, Owning> base_type; 1.321 +}; 1.322 + 1.323 +template <class C, typename R, bool Owning> 1.324 +struct nsRunnableMethodTraits<R (C::*)(), Owning> { 1.325 + typedef C class_type; 1.326 + typedef R return_type; 1.327 + typedef void arg_type; 1.328 + typedef nsRunnableMethod<C, R, Owning> base_type; 1.329 +}; 1.330 + 1.331 +#ifdef NS_HAVE_STDCALL 1.332 +template <class C, typename R, typename A, bool Owning> 1.333 +struct nsRunnableMethodTraits<R (__stdcall C::*)(A), Owning> { 1.334 + typedef C class_type; 1.335 + typedef R return_type; 1.336 + typedef A arg_type; 1.337 + typedef nsRunnableMethod<C, R, Owning> base_type; 1.338 +}; 1.339 + 1.340 +template <class C, typename R, bool Owning> 1.341 +struct nsRunnableMethodTraits<R (NS_STDCALL C::*)(), Owning> { 1.342 + typedef C class_type; 1.343 + typedef R return_type; 1.344 + typedef void arg_type; 1.345 + typedef nsRunnableMethod<C, R, Owning> base_type; 1.346 +}; 1.347 +#endif 1.348 + 1.349 +template <typename Method, typename Arg, bool Owning> 1.350 +class nsRunnableMethodImpl 1.351 + : public nsRunnableMethodTraits<Method, Owning>::base_type 1.352 +{ 1.353 + typedef typename nsRunnableMethodTraits<Method, Owning>::class_type ClassType; 1.354 + nsRunnableMethodReceiver<ClassType, Arg, Owning> mReceiver; 1.355 + Method mMethod; 1.356 +public: 1.357 + nsRunnableMethodImpl(ClassType *obj, 1.358 + Method method, 1.359 + Arg arg) 1.360 + : mReceiver(obj, arg) 1.361 + , mMethod(method) 1.362 + {} 1.363 + NS_IMETHOD Run() { 1.364 + if (MOZ_LIKELY(mReceiver.mObj)) 1.365 + ((*mReceiver.mObj).*mMethod)(mReceiver.mArg); 1.366 + return NS_OK; 1.367 + } 1.368 + void Revoke() { 1.369 + mReceiver.Revoke(); 1.370 + } 1.371 +}; 1.372 + 1.373 +template <typename Method, bool Owning> 1.374 +class nsRunnableMethodImpl<Method, void, Owning> 1.375 + : public nsRunnableMethodTraits<Method, Owning>::base_type 1.376 +{ 1.377 + typedef typename nsRunnableMethodTraits<Method, Owning>::class_type ClassType; 1.378 + nsRunnableMethodReceiver<ClassType, void, Owning> mReceiver; 1.379 + Method mMethod; 1.380 + 1.381 +public: 1.382 + nsRunnableMethodImpl(ClassType *obj, 1.383 + Method method) 1.384 + : mReceiver(obj) 1.385 + , mMethod(method) 1.386 + {} 1.387 + 1.388 + NS_IMETHOD Run() { 1.389 + if (MOZ_LIKELY(mReceiver.mObj)) 1.390 + ((*mReceiver.mObj).*mMethod)(); 1.391 + return NS_OK; 1.392 + } 1.393 + 1.394 + void Revoke() { 1.395 + mReceiver.Revoke(); 1.396 + } 1.397 +}; 1.398 + 1.399 +// Use this template function like so: 1.400 +// 1.401 +// nsCOMPtr<nsIRunnable> event = 1.402 +// NS_NewRunnableMethod(myObject, &MyClass::HandleEvent); 1.403 +// NS_DispatchToCurrentThread(event); 1.404 +// 1.405 +// Statically enforced constraints: 1.406 +// - myObject must be of (or implicitly convertible to) type MyClass 1.407 +// - MyClass must defined AddRef and Release methods 1.408 +// 1.409 +template<typename PtrType, typename Method> 1.410 +typename nsRunnableMethodTraits<Method, true>::base_type* 1.411 +NS_NewRunnableMethod(PtrType ptr, Method method) 1.412 +{ 1.413 + return new nsRunnableMethodImpl<Method, void, true>(ptr, method); 1.414 +} 1.415 + 1.416 +template<typename T> 1.417 +struct dependent_type 1.418 +{ 1.419 + typedef T type; 1.420 +}; 1.421 + 1.422 + 1.423 +// Similar to NS_NewRunnableMethod. Call like so: 1.424 +// Type myArg; 1.425 +// nsCOMPtr<nsIRunnable> event = 1.426 +// NS_NewRunnableMethodWithArg<Type>(myObject, &MyClass::HandleEvent, myArg); 1.427 +template<typename Arg, typename Method, typename PtrType> 1.428 +typename nsRunnableMethodTraits<Method, true>::base_type* 1.429 +NS_NewRunnableMethodWithArg(PtrType ptr, Method method, typename dependent_type<Arg>::type arg) 1.430 +{ 1.431 + return new nsRunnableMethodImpl<Method, Arg, true>(ptr, method, arg); 1.432 +} 1.433 + 1.434 +template<typename PtrType, typename Method> 1.435 +typename nsRunnableMethodTraits<Method, false>::base_type* 1.436 +NS_NewNonOwningRunnableMethod(PtrType ptr, Method method) 1.437 +{ 1.438 + return new nsRunnableMethodImpl<Method, void, false>(ptr, method); 1.439 +} 1.440 + 1.441 +#endif // XPCOM_GLUE_AVOID_NSPR 1.442 + 1.443 +// This class is designed to be used when you have an event class E that has a 1.444 +// pointer back to resource class R. If R goes away while E is still pending, 1.445 +// then it is important to "revoke" E so that it does not try use R after R has 1.446 +// been destroyed. nsRevocableEventPtr makes it easy for R to manage such 1.447 +// situations: 1.448 +// 1.449 +// class R; 1.450 +// 1.451 +// class E : public nsRunnable { 1.452 +// public: 1.453 +// void Revoke() { 1.454 +// mResource = nullptr; 1.455 +// } 1.456 +// private: 1.457 +// R *mResource; 1.458 +// }; 1.459 +// 1.460 +// class R { 1.461 +// public: 1.462 +// void EventHandled() { 1.463 +// mEvent.Forget(); 1.464 +// } 1.465 +// private: 1.466 +// nsRevocableEventPtr<E> mEvent; 1.467 +// }; 1.468 +// 1.469 +// void R::PostEvent() { 1.470 +// // Make sure any pending event is revoked. 1.471 +// mEvent->Revoke(); 1.472 +// 1.473 +// nsCOMPtr<nsIRunnable> event = new E(); 1.474 +// if (NS_SUCCEEDED(NS_DispatchToCurrentThread(event))) { 1.475 +// // Keep pointer to event so we can revoke it. 1.476 +// mEvent = event; 1.477 +// } 1.478 +// } 1.479 +// 1.480 +// NS_IMETHODIMP E::Run() { 1.481 +// if (!mResource) 1.482 +// return NS_OK; 1.483 +// ... 1.484 +// mResource->EventHandled(); 1.485 +// return NS_OK; 1.486 +// } 1.487 +// 1.488 +template <class T> 1.489 +class nsRevocableEventPtr { 1.490 +public: 1.491 + nsRevocableEventPtr() 1.492 + : mEvent(nullptr) { 1.493 + } 1.494 + 1.495 + ~nsRevocableEventPtr() { 1.496 + Revoke(); 1.497 + } 1.498 + 1.499 + const nsRevocableEventPtr& operator=(T *event) { 1.500 + if (mEvent != event) { 1.501 + Revoke(); 1.502 + mEvent = event; 1.503 + } 1.504 + return *this; 1.505 + } 1.506 + 1.507 + void Revoke() { 1.508 + if (mEvent) { 1.509 + mEvent->Revoke(); 1.510 + mEvent = nullptr; 1.511 + } 1.512 + } 1.513 + 1.514 + void Forget() { 1.515 + mEvent = nullptr; 1.516 + } 1.517 + 1.518 + bool IsPending() { 1.519 + return mEvent != nullptr; 1.520 + } 1.521 + 1.522 + T *get() { return mEvent; } 1.523 + 1.524 +private: 1.525 + // Not implemented 1.526 + nsRevocableEventPtr(const nsRevocableEventPtr&); 1.527 + nsRevocableEventPtr& operator=(const nsRevocableEventPtr&); 1.528 + 1.529 + nsRefPtr<T> mEvent; 1.530 +}; 1.531 + 1.532 +/** 1.533 + * A simple helper to suffix thread pool name 1.534 + * with incremental numbers. 1.535 + */ 1.536 +class nsThreadPoolNaming 1.537 +{ 1.538 +public: 1.539 + nsThreadPoolNaming() : mCounter(0) {} 1.540 + 1.541 + /** 1.542 + * Creates and sets next thread name as "<aPoolName> #<n>" 1.543 + * on the specified thread. If no thread is specified (aThread 1.544 + * is null) then the name is synchronously set on the current thread. 1.545 + */ 1.546 + void SetThreadPoolName(const nsACString & aPoolName, 1.547 + nsIThread * aThread = nullptr); 1.548 + 1.549 +private: 1.550 + volatile uint32_t mCounter; 1.551 + 1.552 + nsThreadPoolNaming(const nsThreadPoolNaming &) MOZ_DELETE; 1.553 + void operator=(const nsThreadPoolNaming &) MOZ_DELETE; 1.554 +}; 1.555 + 1.556 +/** 1.557 + * Thread priority in most operating systems affect scheduling, not IO. This 1.558 + * helper is used to set the current thread to low IO priority for the lifetime 1.559 + * of the created object. You can only use this low priority IO setting within 1.560 + * the context of the current thread. 1.561 + */ 1.562 +class MOZ_STACK_CLASS nsAutoLowPriorityIO 1.563 +{ 1.564 +public: 1.565 + nsAutoLowPriorityIO(); 1.566 + ~nsAutoLowPriorityIO(); 1.567 + 1.568 +private: 1.569 + bool lowIOPrioritySet; 1.570 +#if defined(XP_MACOSX) 1.571 + int oldPriority; 1.572 +#endif 1.573 +}; 1.574 + 1.575 + 1.576 + 1.577 +#endif // nsThreadUtils_h__