// bslmt_threadutil.h -*-C++-*- // ---------------------------------------------------------------------------- // NOTICE // // This component is not up to date with current BDE coding standards, and // should not be used as an example for new development. // ---------------------------------------------------------------------------- #ifndef INCLUDED_BSLMT_THREADUTIL #define INCLUDED_BSLMT_THREADUTIL #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide platform-independent utilities related to threading. // //@CLASSES: // bslmt::ThreadUtil: namespace for portable thread management utilities // //@SEE_ALSO: bslmt_threadattributes, bslmt_configuration // //@DESCRIPTION: This component defines a utility 'struct', 'bslmt::ThreadUtil', // that serves as a name space for a suite of pure functions to create threads, // join them (make one thread block and wait for another thread to exit), // manipulate thread handles, manipulate the current thread, and (on some // platforms) access thread-local storage. // ///Creating a Simple Thread with Default Attributes ///------------------------------------------------ // Clients call 'bslmt::ThreadUtil::create()' to create threads. Threads may // be started using a "C" linkage function pointer (of a type defined by // 'bslmt::ThreadUtil::ThreadFunction') and a 'void' pointer to 'userData' to // be passed to the function; or an "invokable" object of parameterized type // (any copy-constructible type on which 'operator()' may be invoked). The // invoked function becomes the main driver for the new thread; when it // returns, the thread terminates. // ///Thread Identity ///--------------- // A thread is identified by an object of the opaque type // 'bslmt::ThreadUtil::Handle'. A handle of this type is returned when a // thread is created (using 'bslmt::ThreadUtil::create'). A client can also // retrieve a 'Handle' for the "current" thread via the 'self' method: //.. // bslmt::ThreadUtil::Handle myHandle = bslmt::ThreadUtil::self(); //.. // Several thread manipulation functions in 'bslmt::ThreadUtil' take a thread // handle, or pointer to a thread handle, as an argument. To facilitate // compatibility with existing systems and allow for non-portable operations, // clients also have access to the 'bslmt::ThreadUtil::NativeHandle' type, // which exposes the underlying, platform-specific thread identifier type: //.. // bslmt::ThreadUtil::NativeHandle myNativeHandle; // myNativeHandle = bslmt::ThreadUtil::nativeHandle(); //.. // Note that the returned native handle may not be a globally unique identifier // for the thread, and, e.g., should not be converted to an integer identifier, // or used as a key in a map. // ///Setting Thread Priorities ///------------------------- // 'bslmt::ThreadUtil' allows clients to configure the priority of newly // created threads by setting the 'inheritSchedule', 'schedulingPolicy', and // 'schedulingPriority' of a thread attributes object supplied to the 'create' // method. The range of legal values for 'schedulingPriority' depends on both // the platform and the value of 'schedulingPolicy', and can be obtained from // the 'getMinSchedulingPriority' and 'getMaxSchedulingPriority' methods. Both // 'schedulingPolicy' and 'schedulingPriority' are ignored unless // 'inheritSchedule' is 'false' (the default value is 'true'). Note that not // only is effective setting of thread priorities workable on only some // combinations of platforms and user privileges, but setting the thread policy // and priority appropriately for one platform may cause thread creation to // fail on another platform. Also note that an unset thread priority may be // interpreted as being outside the valid range defined by // '[ getMinSchedulingPriority(policy), getMaxSchedulingPriority(policy) ]'. //.. // Platform Restrictions // ------------ -------------------------------------------------------------- // Solaris 5.10 None. // // Solaris 5.11 Spawning of threads fails if 'schedulingPolicy' is // 'BSLMT_SCHED_FIFO' or 'BSLMT_SCHED_RR'. Thread priorities // should not be used on Solaris 5.11 as it is not clear that // they have any detectable effect. Note that // 'getMinSchedulingPriority' and 'getMaxSchedulingPriority' // return different values than on Solaris 5.10. // // AIX For non-privileged clients, spawning of threads fails if // 'schedulingPolicy' is 'BSLMT_SCHED_FIFO' or 'BSLMT_SCHED_RR'. // // Linux Non-privileged clients *can* *not* make effective use of // thread priorities -- spawning of threads fails if // 'schedulingPolicy' is 'BSLMT_SCHED_FIFO' or 'BSLMT_SCHED_RR', // and 'getMinSchedulingPriority == getMaxSchedulingPriority' if // the policy has any other value. // // Darwin Non-privileged clients *can* *not* make effective use of // thread priorities -- there is no observable difference in // urgency between high priority and low priority threads. // Spawning of threads does succeed, however, for all scheduling // policies. // // Windows Clients *can* *not* make effective use of thread priorities -- // 'schedulingPolicy', 'schedulingPriority', and // 'inheritSchedule' are ignored for all clients. //.. // ///Supported Clock-Types ///--------------------- // 'bsls::SystemClockType' supplies the enumeration indicating the system clock // on which timeouts supplied to other methods should be based. If the clock // type indicated at construction is 'bsls::SystemClockType::e_REALTIME', the // 'absTime' argument passed to the 'timedWait' method of the various // synchronization primitives offered in 'bslmt' should be expressed as an // *absolute* offset since 00:00:00 UTC, January 1, 1970 (which matches the // epoch used in 'bsls::SystemTime::now(bsls::SystemClockType::e_REALTIME)'. // If the clock type indicated at construction is // 'bsls::SystemClockType::e_MONOTONIC', the 'absTime' argument passed to the // 'timedWait' method of the various synchronization primitives offered in // 'bslmt' should be expressed as an *absolute* offset since the epoch of this // clock (which matches the epoch used in // 'bsls::SystemTime::now(bsls::SystemClockType::e_MONOTONIC)'. // ///Usage ///----- // This section illustrates the intended use of this component. // ///Example 1: Creating a Simple Thread with Default Attributes ///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // In this example, we create a thread using the default attribute settings. // Upon creation, the thread executes the user-supplied C-linkage function // 'myThreadFunction' that counts 5 seconds before terminating: // // First, we create a function that will run in the spawned thread: //.. // extern "C" void *myThreadFunction(void *) // // Print to standard output "Another second has passed" every second // // for five seconds, and return 0. // { // for (int i = 0; i < 3; ++i) { // bslmt::ThreadUtil::microSleep(0, 1); // bsl::cout << "Another second has passed." << bsl::endl; // } // return 0; // } //.. // Now, we show how to create and join the thread. // // After creating the thread, the 'main' routine *joins* the thread, which, in // effect, causes 'main' to wait for execution of 'myThreadFunction' to // complete, and guarantees that the output from 'main' will follow the last // output from the user-supplied function: //.. // int main() // { // bslmt::Configuration::setDefaultThreadStackSize( // bslmt::Configuration::recommendedDefaultThreadStackSize()); // // bslmt::ThreadUtil::Handle handle; // // bslmt::ThreadAttributes attr; // attr.setStackSize(1024 * 1024); // // int rc = bslmt::ThreadUtil::create(&handle, attr, myThreadFunction, 0); // assert(0 == rc); // // bslmt::ThreadUtil::yield(); // // rc = bslmt::ThreadUtil::join(handle); // assert(0 == rc); // // bsl::cout << "A three second interval has elapsed\n"; // // return 0; // } //.. // Finally, the output of this program is: //.. // Another second has passed. // Another second has passed. // Another second has passed. // A three second interval has elapsed. //.. // ///Example 2: Creating a Simple Thread with User-Specified Attributes /// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // In this example, we will choose to override the default thread attribute // values. // // The attributes of a thread can be specified explicitly by supplying a // 'bslmt::ThreadAttributes' object to the 'create' method. For instance, we // could specify a smaller stack size for a thread to conserve system resources // if we know that we will require not require the platform's default stack // size. // // First, we define our thread function, noting that it doesn't need much stack // space: //.. // extern "C" void *mySmallStackThreadFunction(void *threadArg) // // Initialize a small object on the stack and do some work. // { // char *initValue = (char *)threadArg; // char Small[8]; // bsl::memset(&Small[0], *initValue, 8); // // do some work ... // return 0; // } //.. // Finally, we show how to create a detached thread running the function just // created with a small stack size: //.. // void createSmallStackSizeThread() // // Create a detached thread with a small stack size and perform some // // work. // { // enum { k_STACK_SIZE = 16384 }; // bslmt::ThreadAttributes attributes; // attributes.setDetachedState( // bslmt::ThreadAttributes::e_CREATE_DETACHED); // attributes.setStackSize(k_STACK_SIZE); // // char initValue = 1; // bslmt::ThreadUtil::Handle handle; // int status = bslmt::ThreadUtil::create(&handle, // attributes, // mySmallStackThreadFunction, // &initValue); // } //.. // ///Example 3: Setting Thread Priorities /// - - - - - - - - - - - - - - - - - - // In this example we demonstrate creating 3 threads with different priorities. // We use the 'convertToSchedulingPriority' function to translate a normalized, // floating-point priority in the range '[ 0.0, 1.0 ]' to an integer priority // in the range '[ getMinSchedulingPriority, getMaxSchedulingPriority ]' to set // the 'schedulingPriority' attribute. //.. // void runSeveralThreads() // // Create 3 threads with different priorities and then wait for them // // all to finish. // { // enum { k_NUM_THREADS = 3 }; // // bslmt::ThreadUtil::Handle handles[k_NUM_THREADS]; // bslmt_ThreadFunction functions[k_NUM_THREADS] = { // MostUrgentThreadFunctor, // FairlyUrgentThreadFunctor, // LeastUrgentThreadFunctor }; // double priorities[k_NUM_THREADS] = { 1.0, 0.5, 0.0 }; // // bslmt::ThreadAttributes attributes; // attributes.setInheritSchedule(false); // const bslmt::ThreadAttributes::SchedulingPolicy policy = // bslmt::ThreadAttributes::e_SCHED_OTHER; // attributes.setSchedulingPolicy(policy); // // for (int i = 0; i < k_NUM_THREADS; ++i) { // attributes.setSchedulingPriority( // bslmt::ThreadUtil::convertToSchedulingPriority(policy, // priorities[i])); // int rc = bslmt::ThreadUtil::create(&handles[i], // attributes, // functions[i], 0); // assert(0 == rc); // } // // for (int i = 0; i < k_NUM_THREADS; ++i) { // int rc = bslmt::ThreadUtil::join(handles[i]); // assert(0 == rc); // } // } //.. #include <bslscm_version.h> #include <bslmt_entrypointfunctoradapter.h> #include <bslmt_platform.h> #include <bslmt_threadattributes.h> #include <bslmt_threadutilimpl_pthread.h> #include <bslmt_threadutilimpl_win32.h> #include <bsla_maybeunused.h> #include <bslma_allocator.h> #include <bslma_default.h> #include <bsls_assert.h> #include <bsls_libraryfeatures.h> #include <bsls_systemclocktype.h> #include <bsls_systemtime.h> #include <bsls_timeinterval.h> #include <bsls_types.h> #include <bsl_string.h> #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY #include <bslmt_chronoutil.h> #include <bsl_chrono.h> #endif namespace BloombergLP { extern "C" { typedef void *(*bslmt_ThreadFunction)(void *); // 'bslmt_ThreadFunction' is an alias for a function type taking a // single 'void' pointer argument and returning 'void *'. Such // functions are suitable to be specified as thread entry-point // functions to 'bslmt::ThreadUtil::create'. Note that 'create' also // accepts any invokable C++ "functor" object. typedef void (*bslmt_KeyDestructorFunction)(void *); // 'bslmt_KeyDestructorFunction' is an alias for a function type taking // a single 'void' pointer argument and returning 'void'. Such // functions are suitable to be specified as thread-specific key // destructor functions to 'bslmt::ThreadUtil::createKey'. } // extern "C" namespace bslmt { template <class THREAD_POLICY> struct ThreadUtilImpl; // ================= // struct ThreadUtil // ================= struct ThreadUtil { // This 'struct' provides a suite of portable utility functions for // managing threads. public: // PUBLIC TYPES typedef ThreadUtilImpl<Platform::ThreadPolicy> Imp; // Platform-specific implementation type. typedef Imp::Handle Handle; // Thread handle type. Use this type to refer to a thread in a // platform-independent way. typedef Imp::NativeHandle NativeHandle; // Platform-specific thread handle type. typedef Imp::Id Id; // Thread identifier type - distinguished from a 'Handle' in that it // does not have any resources associated with it, whereas 'Handle' // may, depending on platform. typedef bslmt_ThreadFunction ThreadFunction; // Prototype for thread entry-point functions. typedef Imp::Key Key; // Thread-specific key type, used to refer to thread-specific storage. typedef bslmt_KeyDestructorFunction Destructor; // Prototype for thread-specific key destructors. public: // PUBLIC CLASS METHODS // *** Thread Management *** static int convertToSchedulingPriority( ThreadAttributes::SchedulingPolicy policy, double normalizedSchedulingPriority); // Return an integer scheduling priority appropriate for the specified // 'normalizedSchedulingPriority' and the specified 'policy'. If // either the minimum or maximum priority for this platform cannot be // determined, return 'ThreadAttributes::e_UNSET_PRIORITY'. Higher // values of 'normalizedSchedulingPriority' are considered to represent // more urgent priorities. The behavior is undefined unless 'policy' // is a valid 'ThreadAttributes::SchedulingPolicy' and // 'normalizedSchedulingPriority' is in the range '[ 0.0, 1.0 ]'. static int create(Handle *handle, ThreadFunction function, void *userData); static int create(Handle *handle, const ThreadAttributes& attributes, ThreadFunction function, void *userData); // Create a new thread of program control whose entry point will be the // specified 'function', and which will be passed the specified // 'userData' as its sole argument, and load into the specified // 'handle' an identifier that may be used to refer to this thread in // calls to other 'ThreadUtil' methods. Optionally specify // 'attributes' describing the properties for the new thread. If // 'attributes' is not supplied, a default 'ThreadAttributes' object is // used. Use the global allocator to supply memory. Return 0 on // success, and a non-zero value otherwise. 'bslmt::Configuration' is // used to determine the created thread's default stack-size if either // 'attributes' is not supplied or if 'attributes.stackSize()' has the // unset value. The behavior is undefined unless 'attributes', if // specified, has a 'stackSize' that is either greater than 0 or // 'e_UNSET_STACK_SIZE'. Note that unless the created thread is // explicitly "detached" (by invoking the 'detach' class method with // 'handle') or the 'k_CREATE_DETACHED' attribute is specified, a call // to 'join' must be made to reclaim any system resources associated // with the newly-created thread. Also note that users are encouraged // to either explicitly provide a stack size attribute, or configure a // 'bslmt'-wide default using 'bslmt::Configuration', because the // default stack size is surprisingly small on some platforms. template <class INVOKABLE> static int create(Handle *handle, const INVOKABLE& function); template <class INVOKABLE> static int create(Handle *handle, const ThreadAttributes& attributes, const INVOKABLE& function); // Create a new thread of program control whose entry point will invoke // the specified 'function' object, and load into the specified // 'handle' an identifier that may be used to refer to this thread in // calls to other 'ThreadUtil' methods. Optionally specify // 'attributes' describing the properties for the new thread. If // 'attributes' is not supplied, a default 'ThreadAttributes' object is // used. Use the global allocator to supply memory. Return 0 on // success, and a non-zero value otherwise. 'function' shall be a // reference to a type, 'INVOKABLE', that can be copy-constructed, and // where the expression '(void)function()' will execute a function call // (i.e., either a 'void()()' function, or a functor object // implementing 'void operator()()'). 'bslmt::Configuration' is used // to determine the created thread's default stack-size if either // 'attributes' is not supplied or if 'attributes.stackSize()' has the // unset value. The behavior is undefined unless 'attributes', if // specified, has a 'stackSize' that is either greater than 0 or // 'e_UNSET_STACK_SIZE'. Note that unless the created thread is // explicitly "detached" (by invoking the 'detach' class method with // 'handle') or the 'k_CREATE_DETACHED' attribute is specified, a call // to 'join' must be made to reclaim any system resources associated // with the newly-created thread. Also note that users are encouraged // to either explicitly provide a stack size attribute, or configure a // 'bslmt'-wide default using 'bslmt::Configuration', because the // default stack size is surprisingly small on some platforms. static int createWithAllocator(Handle *handle, ThreadFunction function, void *userData, bslma::Allocator *allocator); static int createWithAllocator(Handle *handle, const ThreadAttributes& attributes, ThreadFunction function, void *userData, bslma::Allocator *allocator); // Create a new thread of program control whose entry point will be the // specified 'function', and which will be passed the specified // 'userData' as its sole argument, and load into the specified // 'handle' an identifier that may be used to refer to this thread in // calls to other 'ThreadUtil' methods. Optionally specify // 'attributes' describing the properties for the new thread. If // 'attributes' is not supplied, a default 'ThreadAttributes' object is // used. Use the specified 'allocator' to supply memory. Return 0 on // success, and a non-zero value otherwise. 'bslmt::Configuration' is // used to determine the created thread's default stack-size if either // 'attributes' is not supplied or if 'attributes.stackSize()' has the // unset value. The behavior is undefined unless 'attributes', if // specified, has a 'stackSize' that is either greater than 0 or // 'e_UNSET_STACK_SIZE'. Note that unless the created thread is // explicitly "detached" (by invoking the 'detach' class method with // 'handle') or the 'k_CREATE_DETACHED' attribute is specified, a call // to 'join' must be made to reclaim any system resources associated // with the newly-created thread. Also note that users are encouraged // to either explicitly provide a stack size attribute, or configure a // 'bslmt'-wide default using 'bslmt::Configuration', because the // default stack size is surprisingly small on some platforms. template <class INVOKABLE> static int createWithAllocator(Handle *handle, const INVOKABLE& function, bslma::Allocator *allocator); template <class INVOKABLE> static int createWithAllocator(Handle *handle, const ThreadAttributes& attributes, const INVOKABLE& function, bslma::Allocator *allocator); // Create a new thread of program control whose entry point will invoke // the specified 'function' object (using the specified 'allocator' to // supply memory to copy 'function'), and load into the specified // 'handle' an identifier that may be used to refer to this thread in // calls to other 'ThreadUtil' methods. Optionally specify // 'attributes' describing the properties for the new thread. If // 'attributes' is not supplied, a default 'ThreadAttributes' object is // used. Return 0 on success, and a non-zero value otherwise. // 'function' shall be a reference to a type, 'INVOKABLE', that can be // copy-constructed, and where the expression '(void)function()' will // execute a function call (i.e., either a 'void()()' function, or a // functor object implementing 'void operator()()'). // 'bslmt::Configuration' is used to determine the created thread's // default stack-size if either 'attributes' is not supplied or if // 'attributes.stackSize()' has the unset value. The behavior is // undefined unless 'attributes', if specified, has a 'stackSize' that // is either greater than 0 or 'e_UNSET_STACK_SIZE'. Note that unless // the created thread is explicitly "detached" (by invoking the // 'detach' class method with 'handle') or the 'k_CREATE_DETACHED' // attribute is specified, a call to 'join' must be made to reclaim any // system resources associated with the newly-created thread. Also // note that the lifetime of 'allocator' must exceed the lifetime of // the thread. Also note that users are encouraged to either // explicitly provide a stack size attribute, or configure a // 'bslmt'-wide default using 'bslmt::Configuration', because the // default stack size is surprisingly small on some platforms. static int detach(Handle& handle); // "Detach" the thread identified by the specified 'handle' such that // when it terminates, the resources associated with that thread will // automatically be reclaimed. The behavior is undefined unless // 'handle' was obtained by a call to 'create' or 'self'. Note that // once a thread is "detached", it is no longer possible to 'join' the // thread to retrieve its exit status. static void exit(void *status); // Exit the current thread and return the specified 'status'. If the // current thread is not "detached", then a call to 'join' must be made // to reclaim any resources used by the thread, and to retrieve the // exit status. Note that the preferred method of exiting a thread is // to return from the entry point function. static int getMinSchedulingPriority( ThreadAttributes::SchedulingPolicy policy); // Return the minimum available priority for the specified 'policy', // where 'policy' is of type 'ThreadAttributes::SchedulingPolicy'. // Return 'ThreadAttributes::e_UNSET_PRIORITY' if the minimum // scheduling priority cannot be determined. Note that, for some // platform / policy combinations, 'getMinSchedulingPriority(policy)' // and 'getMaxSchedulingPriority(policy)' return the same value. static int getMaxSchedulingPriority( ThreadAttributes::SchedulingPolicy policy); // Return the maximum available priority for the specified 'policy', // where 'policy' is of type 'ThreadAttributes::SchedulingPolicy'. // Return 'ThreadAttributes::e_UNSET_PRIORITY' if the maximum // scheduling priority cannot be determined. Note that, for some // platform / policy combinations, 'getMinSchedulingPriority(policy)' // and 'getMaxSchedulingPriority(policy)' return the same value. static void getThreadName(bsl::string *threadName); // Load the name of the current thread into the specified // '*threadName'. Note that this method clears '*threadName' on // platforms other than Linux, Solaris, Darwin, and Windows. static int join(Handle& threadHandle, void **status = 0); // Suspend execution of the current thread until the thread referred to // by the specified 'threadHandle' terminates, and reclaim any system // resources associated with 'threadHandle'. Return 0 on success, and // a non-zero value otherwise. If the optionally specified 'status' is // not 0, load into '*status' the value returned by the function // supplied at the creation of the thread identified by 'threadHandle'. // The behavior is undefined unless 'threadHandle' was obtained by a // call to 'create'. static void microSleep(int microseconds, int seconds = 0); // Suspend execution of the current thread for a period of at least the // specified 'microseconds' and the optionally specified 'seconds' // (relative time), or an interrupting signal is received. Note that // the actual time suspended depends on many factors including system // scheduling and system timer resolution, and may be significantly // longer than the time requested. static void setThreadName(const bslstl::StringRef& threadName); // Set the name of the current thread to the specified 'threadName'. // On platforms other than Linux, Solaris, Darwin and Windows this // method has no effect. Note that on those two platforms 'threadName' // will be truncated to a length of 15 bytes, not including the // terminating '\0'. static void sleep(const bsls::TimeInterval& sleepTime); // Suspend execution of the current thread for a period of at least the // specified (relative) 'sleepTime', or an interrupting signal is // received. Note that the actual time suspended depends on many // factors including system scheduling and system timer resolution. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class REP_TYPE, class PERIOD_TYPE> static void sleep( const bsl::chrono::duration<REP_TYPE, PERIOD_TYPE>& sleepTime); // Suspend execution of the current thread for a period of at least the // specified (relative) 'sleepTime', or an interrupting signal is // received. Note that the actual time suspended depends on many // factors including system scheduling and system timer resolution. #endif static void sleepUntil(const bsls::TimeInterval& absoluteTime, bsls::SystemClockType::Enum clockType = bsls::SystemClockType::e_REALTIME); // Suspend execution of the current thread until the specified // 'absoluteTime', or an interrupting signal is received. Optionally // specify 'clockType' which determines the epoch from which the // interval 'absoluteTime' is measured (see {Supported Clock-Types} in // the component documentation). The behavior is undefined unless // 'absoluteTime' represents a time after January 1, 1970 and before // the end of December 31, 9999 (i.e., a time interval greater than or // equal to 0, and less than 253,402,300,800 seconds). Note that the // actual time suspended depends on many factors including system // scheduling and system timer resolution. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class CLOCK, class DURATION> static void sleepUntil( const bsl::chrono::time_point<CLOCK, DURATION>& absoluteTime); // Suspend execution of the current thread until the specified // 'absoluteTime', which is an *absolute* time represented as an // interval from some epoch, determined by the clock associated with // the time point. The behavior is undefined unless 'absoluteTime' // represents a time after January 1, 1970 and before the end of // December 31, 9999. Note that the actual time suspended depends on // many factors including system scheduling and system timer // resolution. #endif static void yield(); // Move the current thread to the end of the scheduler's queue and // schedule another thread to run. Note that this allows cooperating // threads of the same priority to share CPU resources equally. // *** Thread Identification *** static bool areEqual(const Handle& a, const Handle& b); // Return 'true' if the specified 'a' and 'b' thread handles identify // the same thread, or if both 'a' and 'b' are invalid handles, and // 'false' otherwise. Note that if *either* of 'a' or 'b' is an // invalid handle, but not both, this method returns 'false'. static bool areEqualId(const Id& a, const Id& b); // Return 'true' if the specified 'a' thread identifier is associated // with the same thread as the specified 'b' thread identifier, and // 'false' otherwise. static Id handleToId(const Handle& threadHandle); // Return the unique identifier of the thread having the specified // 'threadHandle' within the current process. The behavior is // undefined unless 'handle' was obtained by a call to 'create' or // 'self'. Note that this value is valid only until the thread // terminates, and may be reused thereafter. static bsls::Types::Uint64 idAsUint64(const Id& threadId); // Return the unique integral identifier of a thread uniquely // identified by the specified 'threadId' within the current process. // Note that this representation is particularly useful for logging // purposes. Also note that this value is only valid until the thread // terminates and may be reused thereafter. static int idAsInt(const Id& threadId); // Return the unique integral identifier of a thread uniquely // identified by the specified 'threadId' within the current process. // Note that this representation is particularly useful for logging // purposes. Also note that this value is only valid until the thread // terminates and may be reused thereafter. // // DEPRECATED: use 'idAsUint64'. static const Handle& invalidHandle(); // Return a reference to the non-modifiable 'Handle' object that is // guaranteed never to be a valid thread handle. static bool isEqual(const Handle& a, const Handle& b); // Return 'true' if the specified 'a' and 'b' thread handles identify // the same thread, or if both 'a' and 'b' are invalid handles, and // 'false' otherwise. Note that if *either* of 'a' or 'b' is an // invalid handle, but not both, this method returns 'false'. // // DEPRECATED: use 'areEqual' instead. static bool isEqualId(const Id& a, const Id& b); // Return 'true' if the specified 'lhs' thread identifier is associated // with the same thread as the specified 'rhs' thread identifier, and // 'false' otherwise. // // DEPRECATED: use 'areEqualId' instead. static NativeHandle nativeHandle(const Handle& handle); // Return the platform-specific identifier associated with the thread // referred to by the specified 'handle'. The behavior is undefined // unless 'handle' was obtained by a call to 'create' or 'self'. Note // that the returned native handle may not be a globally unique // identifier for the thread (see 'selfIdAsUint'). static Handle self(); // Return an opaque thread identifier that can be used to refer to the // current thread in calls to other 'ThreadUtil' methods. Note that // identifier may only be used to refer to the current thread from the // current thread (the handle returned is not valid in other threads). static Id selfId(); // Return an identifier that can be used to uniquely identify the // current thread within the current process. Note that the identifier // is only valid until the thread terminates and may be reused // thereafter. static bsls::Types::Uint64 selfIdAsInt(); // Return an integral identifier that can be used to uniquely identify // the current thread within the current process. Note that this // representation is particularly useful for logging purposes. Also // note that this value is only valid until the thread terminates and // may be reused thereafter. // // DEPRECATED: use 'selfIdAsUint64' instead. static bsls::Types::Uint64 selfIdAsUint64(); // Return an integral identifier that can be used to uniquely identify // the current thread within the current process. Note that this // representation is particularly useful for logging purposes. Also // note that this value is valid only until the thread terminates, and // may be reused thereafter. // *** Thread-Specific (Local) Storage (TSS or TLS) *** static int createKey(Key *key, Destructor threadKeyCleanupFunction); // Load into the specified 'key' a new process-wide identifier that can // be used to store (via 'setSpecific') and retrieve (via // 'getSpecific') a pointer value local to each thread, and associate // with the new key the specified 'threadKeyCleanupFunction', which // will be called by each thread, if 'threadKeyCleanupFunction' is // non-zero and the value associated with 'key' for that thread is // non-zero, with the associated value as an argument, after the // function passed to 'create' has returned and before the thread // terminates. Return 0 on success, and a non-zero value otherwise. // Note that multiple keys can be defined, which can result in multiple // thread key cleanup functions being called for a given thread. static int deleteKey(Key& key); // Delete the specified 'key' from the calling process, and // disassociate all threads from the thread key cleanup function // supplied when 'key' was created (see 'createKey'). Return 0 on // success, and a non-zero value otherwise. The behavior is undefined // unless 'key' was obtained from a successful call to 'createKey' and // has not already been deleted. Note that deleting a key does not // delete any data referred to by the pointer values associated with // that key in any thread. static void *getSpecific(const Key& key); // Return the thread-local value associated with the specified 'key'. // A 'key' is shared among all threads and the value associated with // 'key' for each thread is 0 until it is set by that thread using // 'setSpecific'. The behavior is undefined unless this method is // called outside any thread key cleanup function associated with any // key by 'createKey', 'key' was obtained from a successful call to // 'createKey', and 'key' has not been deleted. static int setSpecific(const Key& key, const void *value); // Associate the specified thread-local 'value' with the specified // process-wide 'key'. Return 0 on success, and a non-zero value // otherwise. The value associated with a thread for a given key is 0 // until it has been set by that thread using 'setSpecific'. The // behavior is undefined unless this method is called outside any // thread key cleanup function associated with any key by 'createKey', // 'key' was obtained from a successful call to 'createKey', and 'key' // has not been deleted. static unsigned int hardwareConcurrency(); // Return a *hint* at the number of concurrent threads supported by // this platform on success, and 0 otherwise. }; } // close package namespace // ============================================================================ // INLINE DEFINITIONS // ============================================================================ // ----------------- // struct ThreadUtil // ----------------- // *** Thread Management *** // CLASS METHODS inline int bslmt::ThreadUtil::create(Handle *handle, ThreadFunction function, void *userData) { BSLS_ASSERT_SAFE(handle); return Imp::create(handle, function, userData); } template <class INVOKABLE> inline int bslmt::ThreadUtil::create(Handle *handle, const INVOKABLE& function) { BSLS_ASSERT_SAFE(handle); return createWithAllocator(handle, function, bslma::Default::globalAllocator()); } template <class INVOKABLE> inline int bslmt::ThreadUtil::create(Handle *handle, const ThreadAttributes& attributes, const INVOKABLE& function) { BSLS_ASSERT_SAFE(handle); return createWithAllocator(handle, attributes, function, bslma::Default::globalAllocator()); } inline int bslmt::ThreadUtil::createWithAllocator( Handle *handle, ThreadFunction function, void *userData, BSLA_MAYBE_UNUSED bslma::Allocator *allocator) { BSLS_ASSERT_SAFE(handle); BSLS_ASSERT_OPT(allocator); // 'allocator' is unused in this function, which is provided for symmetry // and in case this function comes to need an allocator at sometime in the // future. return Imp::create(handle, function, userData); } template <class INVOKABLE> inline int bslmt::ThreadUtil::createWithAllocator(Handle *handle, const ThreadAttributes& attributes, const INVOKABLE& function, bslma::Allocator *allocator) { BSLS_ASSERT_SAFE(handle); BSLS_ASSERT_OPT(allocator); bslma::ManagedPtr<EntryPointFunctorAdapter<INVOKABLE> > threadData; EntryPointFunctorAdapterUtil::allocateAdapter(&threadData, function, attributes.threadName(), allocator); int rc = Imp::create(handle, attributes, bslmt_EntryPointFunctorAdapter_invoker, threadData.ptr()); if (0 == rc) { threadData.release(); } return rc; } template <class INVOKABLE> inline int bslmt::ThreadUtil::createWithAllocator(Handle *handle, const INVOKABLE& function, bslma::Allocator *allocator) { BSLS_ASSERT_SAFE(handle); BSLS_ASSERT_OPT(allocator); bslma::ManagedPtr<EntryPointFunctorAdapter<INVOKABLE> > threadData; EntryPointFunctorAdapterUtil::allocateAdapter(&threadData, function, bslstl::StringRef(), allocator); int rc = Imp::create(handle, bslmt_EntryPointFunctorAdapter_invoker, threadData.ptr()); if (0 == rc) { threadData.release(); } return rc; } inline int bslmt::ThreadUtil::detach(Handle& handle) { return Imp::detach(handle); } inline void bslmt::ThreadUtil::exit(void *status) { Imp::exit(status); } inline int bslmt::ThreadUtil::getMinSchedulingPriority( ThreadAttributes::SchedulingPolicy policy) { return Imp::getMinSchedulingPriority(policy); } inline int bslmt::ThreadUtil::getMaxSchedulingPriority( ThreadAttributes::SchedulingPolicy policy) { return Imp::getMaxSchedulingPriority(policy); } inline void bslmt::ThreadUtil::getThreadName(bsl::string *threadName) { BSLS_ASSERT_SAFE(threadName); return Imp::getThreadName(threadName); } inline int bslmt::ThreadUtil::join(Handle& threadHandle, void **status) { return Imp::join(threadHandle, status); } inline void bslmt::ThreadUtil::microSleep(int microseconds, int seconds) { Imp::microSleep(microseconds, seconds); } inline void bslmt::ThreadUtil::setThreadName(const bslstl::StringRef& threadName) { Imp::setThreadName(threadName); } inline void bslmt::ThreadUtil::sleep(const bsls::TimeInterval& sleepTime) { Imp::sleep(sleepTime); } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class REP_TYPE, class PERIOD_TYPE> inline void bslmt::ThreadUtil::sleep( const bsl::chrono::duration<REP_TYPE, PERIOD_TYPE>& sleepTime) { bslmt::ThreadUtil::sleep( bslmt::ChronoUtil::durationToTimeInterval(sleepTime)); } #endif inline void bslmt::ThreadUtil::sleepUntil(const bsls::TimeInterval& absoluteTime, bsls::SystemClockType::Enum clockType) { int status = Imp::sleepUntil(absoluteTime, clockType); (void) status; // Suppress an unused variable error. BSLS_ASSERT(0 == status); } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class CLOCK, class DURATION> void bslmt::ThreadUtil::sleepUntil( const bsl::chrono::time_point<CLOCK, DURATION>& absoluteTime) { typename CLOCK::time_point now = CLOCK::now(); const bsls::SystemClockType::Enum bslsClockType = bsls::SystemClockType::e_REALTIME; // Iteration is necessary because the specified 'CLOCK' type may not // progress at the same rate as the realtime system clock. while (absoluteTime > now) { bsls::TimeInterval ti = bsls::SystemTime::now(bslsClockType) .addDuration(absoluteTime - now); bslmt::ThreadUtil::sleepUntil(ti); now = CLOCK::now(); } } #endif inline void bslmt::ThreadUtil::yield() { Imp::yield(); } // *** Thread Identification *** inline bool bslmt::ThreadUtil::areEqual(const Handle& a, const Handle& b) { // Some implementations (notably pthreads) do not define the result of // comparing invalid handles. We avoid undefined behavior by explicitly // checking for invalid handles. return Imp::INVALID_HANDLE == a ? (Imp::INVALID_HANDLE == b) : (Imp::INVALID_HANDLE == b ? false : Imp::areEqual(a, b)); } inline bool bslmt::ThreadUtil::areEqualId(const Id& a, const Id& b) { return Imp::areEqualId(a, b); } inline bslmt::ThreadUtil::Id bslmt::ThreadUtil::handleToId(const Handle& threadHandle) { return Imp::handleToId(threadHandle); } inline bsls::Types::Uint64 bslmt::ThreadUtil::idAsUint64(const Id& threadId) { return Imp::idAsUint64(threadId); } inline int bslmt::ThreadUtil::idAsInt(const Id& threadId) { return Imp::idAsInt(threadId); } inline const bslmt::ThreadUtil::Handle& bslmt::ThreadUtil::invalidHandle() { return Imp::INVALID_HANDLE; } inline bool bslmt::ThreadUtil::isEqual(const Handle& a, const Handle& b) { return Imp::areEqual(a, b); } inline bool bslmt::ThreadUtil::isEqualId(const Id& a, const Id& b) { return Imp::areEqualId(a, b); } inline bslmt::ThreadUtil::NativeHandle bslmt::ThreadUtil::nativeHandle(const Handle& handle) { return Imp::nativeHandle(handle); } inline bslmt::ThreadUtil::Handle bslmt::ThreadUtil::self() { return Imp::self(); } inline bslmt::ThreadUtil::Id bslmt::ThreadUtil::selfId() { return Imp::selfId(); } inline bsls::Types::Uint64 bslmt::ThreadUtil::selfIdAsInt() { return Imp::selfIdAsInt(); } inline bsls::Types::Uint64 bslmt::ThreadUtil::selfIdAsUint64() { return Imp::selfIdAsUint64(); } // *** Thread-Specific (Local) Storage (TSS or TLS) *** inline int bslmt::ThreadUtil::createKey(Key *key, Destructor threadKeyCleanupFunction) { return Imp::createKey(key, threadKeyCleanupFunction); } inline int bslmt::ThreadUtil::deleteKey(Key& key) { return Imp::deleteKey(key); } inline void *bslmt::ThreadUtil::getSpecific(const Key& key) { return Imp::getSpecific(key); } inline int bslmt::ThreadUtil::setSpecific(const Key& key, const void *value) { return Imp::setSpecific(key, value); } inline unsigned int bslmt::ThreadUtil::hardwareConcurrency() { return Imp::hardwareConcurrency(); } } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2015 Bloomberg Finance L.P. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ----------------------------- END-OF-FILE ----------------------------------