// bslmt_threadutilimpl_pthread.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_THREADUTILIMPL_PTHREAD
#define INCLUDED_BSLMT_THREADUTILIMPL_PTHREAD

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a POSIX implementation of 'bslmt::ThreadUtil'.
//
//@CLASSES:
//  bslmt::ThreadUtilImpl<PosixThreads>: POSIX specialization
//
//@SEE_ALSO: bslmt_threadutil
//
//@DESCRIPTION: This component provides an implementation of
// 'bslmt::ThreadUtil' for POSIX threads ("pthreads"),
// 'bslmt::ThreadUtilImpl<PosixThreads>', via the template specialization:
//..
//  bslmt::ThreadUtilImpl<Platform::PosixThreads>
//..
// This template class should not be used (directly) by client code.  Clients
// should instead use 'bslmt::ThreadUtil'.
//
///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 component is an implementation detail of 'bslmt' and is *not* intended
// for direct client use.  It is subject to change without notice.  As such, a
// usage example is not provided.

#include <bslscm_version.h>

#include <bslmt_platform.h>

#ifdef BSLMT_PLATFORM_POSIX_THREADS

// Platform-specific implementation starts here.

#include <bslmt_threadattributes.h>

#include <bsls_platform.h>
#include <bsls_systemclocktype.h>
#include <bsls_timeinterval.h>
#include <bsls_types.h>

#include <bsl_string.h>

#include <pthread.h>

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'.

    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'.
}

namespace bslmt {

template <class THREAD_POLICY>
struct ThreadUtilImpl;

               // ============================================
               // class ThreadUtilImpl<Platform::PosixThreads>
               // ============================================

template <>
struct ThreadUtilImpl<Platform::PosixThreads> {
    // This class provides a full specialization of 'ThreadUtilImpl' for
    // pthreads.

    // TYPES
    typedef pthread_t Handle;        // thread handle type
    typedef pthread_t NativeHandle;  // native thread handle type
    typedef pthread_t Id;            // thread Id type
    typedef pthread_key_t Key;       // thread-specific storage key type

    // CLASS DATA
    static const pthread_t INVALID_HANDLE;

    // CLASS METHODS
                          // *** Thread Management ***

    static int create(Handle                  *threadHandle,
                      const ThreadAttributes&  attributes,
                      bslmt_ThreadFunction     function,
                      void                    *userData);
        // Create a new thread of program control having the specified
        // 'attributes' that invokes the specified 'function' with a single
        // argument specified by 'userData', and load into the specified
        // 'threadHandle' an identifier that may be used to refer to the thread
        // in future calls to this utility.  Return 0 on success, and a
        // non-zero value otherwise.  The behavior is undefined if the
        // specified 'thread' is 0 or if 'attributes.stackSize()' has been set
        // to a negative value other than the unset value.  Note that unless
        // explicitly "detached" (by 'detach'), or unless the
        // 'BSLMT_CREATE_DETACHED' attribute is specified, a call to 'join'
        // must be made once the thread terminates to reclaim any system
        // resources associated with the newly created identifier.

    static int create(Handle               *threadHandle,
                      bslmt_ThreadFunction  function,
                      void                 *userData);
        // Create a new thread of program control having platform specific
        // default attributes (i.e., "stack size", "scheduling priority"), that
        // invokes the specified 'function' with a single argument specified by
        // 'userData', and load into the specified 'threadHandle', an
        // identifier that may be used to refer to the thread in future calls
        // to this utility.  Return 0 on success, and a non-zero value
        // otherwise.  The behavior is undefined if the 'threadHandle' is 0.
        // Note that unless explicitly "detached" ('detach'), a call to 'join'
        // must be made once the thread terminates to reclaim any system
        // resources associated with the newly created identifier.

    static int detach(Handle& threadHandle);
        // "Detach" the thread identified by the specified 'threadHandle', such
        // that when it terminates, the resources associated the thread will
        // automatically be reclaimed.  Note that once a thread is "detached",
        // it is no longer possible to 'join' the thread to retrieve the 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 generally, the preferred method of exiting a
        // thread is to return form the entry point function.

    static int getMaxSchedulingPriority(
                                    ThreadAttributes::SchedulingPolicy policy);
        // Return the maximum available priority for the specified 'policy',
        // where 'policy' is of type 'ThreadAttributes::SchedulingPolicy'.
        // Return 'ThreadAttributes::BSLMT_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 int getMinSchedulingPriority(
                                    ThreadAttributes::SchedulingPolicy policy);
        // Return the minimum available priority for the specified 'policy',
        // where 'policy' is of type 'ThreadAttributes::SchedulingPolicy'.
        // Return 'ThreadAttributes::BSLMT_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 void getThreadName(bsl::string *threadName);
        // Load the name of the current thread into the specified 'threadName'.
        // Note that this method clears '*threadName' on all platforms other
        // than Linux and Darwin.

    static int join(Handle& threadHandle, void **status = (void**)0);
        // Suspend execution of the current thread until the thread specified
        // by 'threadHandle' terminates, and reclaim any system resources
        // associated with the 'threadHandle'.  Return 0 on success, and a
        // non-zero value otherwise.  If the optionally specified 'status' is
        // not 0, load into the 'status' the value returned by the specified
        // 'thread'.

    static int microSleep(int                 microseconds,
                          int                 seconds = 0,
                          bsls::TimeInterval *unsleptTime = 0);
        // Suspend execution of the current thread for a period of at least the
        // optionally specified 'seconds' and 'microseconds' (relative time),
        // and optionally load into the optionally specified 'unsleptTime' the
        // amount of time that was not slept by this function if the operation
        // was interrupted by a signal.  Return 0 on success, and non-zero if
        // the operation was interrupted by a signal.  Note that the actual
        // time suspended depends on many factors including system scheduling,
        // and system timer resolution.  Note that the actual time suspended
        // depends on many factors including system scheduling, and system
        // timer resolution.

    static void setThreadName(const bslstl::StringRef& threadName);
        // Set the name of the current thread to the specified 'threadName'.
        // On all platforms other than Linux and Darwin 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 int sleep(const bsls::TimeInterval&  sleepTime,
                     bsls::TimeInterval        *unsleptTime = 0);
        // Suspend execution of the current thread for a period of at least the
        // specified 'sleepTime' (relative time), and optionally load into the
        // optionally specified 'unsleptTime' the amount of time that was not
        // slept by this function if the operation was interrupted by a signal.
        // Return 0 on success, and a non-zero value if the operation was
        // interrupted by a signal.  Note that the actual time suspended
        // depends on many factors including system scheduling, and system
        // timer resolution.

    static int sleepUntil(const bsls::TimeInterval&   absoluteTime,
                          bsls::SystemClockType::Enum clockType
                                          = bsls::SystemClockType::e_REALTIME);
        // Suspend execution of the current thread until the specified
        // 'absoluteTime'.  Optionally specify 'clockType' which determines the
        // epoch from which the interval 'absoluteTime' is measured (see
        // {Supported Clock-Types} in the component documentation).  Return 0
        // on success, and a non-zero value otherwise.  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.

    static int sleepUntil(const bsls::TimeInterval&   absoluteTime,
                          bool                        retryOnSignalInterrupt
                                                                       = false,
                          bsls::SystemClockType::Enum clockType
                                          = bsls::SystemClockType::e_REALTIME);
        // Suspend execution of the current thread until the specified
        // 'absoluteTime'.  Optionally specify 'retryOnSignalInterrupt'
        // indicating whether to put this thread to sleep again if the
        // operating system interrupts the sleep because of a signal.
        // Optionally specify 'clockType' which determines the epoch from which
        // the interval 'absoluteTime' is measured (see {Supported
        // Clock-Types} in the component documentation).  Return 0 on success,
        // and a non-zero value otherwise.  If 'retryOnSignalInterrupt' is
        // 'true', an interrupt from a signal will be ignored and the current
        // the thread will be put back to sleep until 'absoluteTime', otherwise
        // this call will return 0 to the calling thread immediately.  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.

    static void yield();
        // Put the current thread to the end of the scheduler's queue and
        // schedule another thread to run.  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, and 'false' otherwise.

    static bool areEqualId(const Id& a, const Id& b);
        // Return 'true' if the specified 'a' and 'b' thread id identify the
        // same thread, and 'false' otherwise.

    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 NativeHandle nativeHandle(const Handle& threadHandle);
        // Return the platform specific identifier associated with the thread
        // specified by 'threadHandle'.  Note that the returned native handle
        // may not be a globally unique identifier for the thread (see
        // 'selfIdAsUint').

    static Handle self();
        // Return an identifier that can be used to refer to the current thread
        // in future calls to this utility.

    static Id selfId();
        // Return an identifier that can be used to uniquely identify the
        // current thread within the current process.  Note that the id is only
        // valid until the thread terminates and may be reused thereafter.

    static bsls::Types::Uint64 selfIdAsInt();
        // Return an integer of the unique identifier of the current thread
        // within the current process.  This representation is particularly
        // useful for logging purposes.  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 integer of the unique identifier of the current thread
        // within the current process.  This representation is particularly
        // useful for logging purposes.  Note that this value is only valid
        // until the thread terminates and may be reused thereafter.

    static Id handleToId(const Handle& threadHandle);
        // Return the unique identifier of the thread having the specified
        // 'threadHandle' within the current process.  Note that this value is
        // only valid until the thread terminates and may be reused thereafter.

                // *** Thread-Specific (Local) Storage (TSS or TLS) ***

    static int createKey(Key *key, bslmt_KeyDestructorFunction destructor);
        // Load, into the specified 'key', an identifier that can be used to
        // store ('setSpecific') and retrieve ('getSpecific') a single
        // thread-specific pointer value.  Associate with the identifier, the
        // specified 'destructor' if a non-zero value is specified.  Return 0
        // on success, and a non-zero value otherwise.

    static int deleteKey(Key& key);
        // Delete the specified thread-specific 'key'.  Return 0 on success,
        // and a non-zero value otherwise.  Note that deleting a key does not
        // delete any data that is currently associated with the key in the
        // calling thread or any other thread.

    static void *getSpecific(const Key& key);
        // Return the value associated with the specified thread-specific
        // 'key'.  Note that if the key is not valid, a value of zero is
        // returned, which is indistinguishable from a valid key with a 0
        // value.

    static int setSpecific(const Key& key, const void *value);
        // Associate the specified 'value' with the specified thread-specific
        // 'key'.  Return 0 on success, and a non-zero value otherwise.  TBD
        // elaborate on what 'value' represents

    static unsigned int hardwareConcurrency();
        // Return the number of concurrent threads supported by the
        // implementation on success, and 0 otherwise.
};

}  // close package namespace

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

               // --------------------------------------------
               // class ThreadUtilImpl<Platform::PosixThreads>
               // --------------------------------------------

// CLASS METHODS
                          // *** Thread Management ***
inline
int bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::create(
                                            Handle               *threadHandle,
                                            bslmt_ThreadFunction  function,
                                            void                 *userData)
{
    ThreadAttributes attr;
    return create(threadHandle, attr, function, userData);
}

inline
int bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::detach(
                                                          Handle& threadHandle)
{
    return pthread_detach(threadHandle);
}

inline
void bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::exit(void *status)
{
    pthread_exit(status);
}

inline
int bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::join(
                                                        Handle&   threadHandle,
                                                        void    **status)
{
    return pthread_join(threadHandle, status);
}

inline
int bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::sleepUntil(
                                      const bsls::TimeInterval&   absoluteTime,
                                      bsls::SystemClockType::Enum clockType)
{
    return sleepUntil(absoluteTime, false, clockType);
}

inline
void bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::yield()
{
    sched_yield();
}

                       // *** Thread Identification ***

inline
bool bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::areEqual(
                                                               const Handle& a,
                                                               const Handle& b)
{
    return pthread_equal(a, b);
}

inline
bool bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::areEqualId(
                                                                   const Id& a,
                                                                   const Id& b)
{
    return pthread_equal(a, b);
}

inline
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::Id
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::handleToId(
                                                    const Handle& threadHandle)
{
    return threadHandle;
}

inline
bsls::Types::Uint64
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::idAsUint64(
                                                            const Id& threadId)
{
#ifdef BSLS_PLATFORM_OS_DARWIN
    return reinterpret_cast<bsls::Types::Uint64>(threadId);
#else
    return static_cast<bsls::Types::Uint64>(threadId);
#endif
}

inline
int
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::idAsInt(
                                                            const Id& threadId)
{
    // Our interface is not good if the id is a pointer.  The two casts will
    // avoid a compilation error though.  TBD

    return static_cast<int>(idAsUint64(threadId));
}

inline
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::NativeHandle
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::nativeHandle(
                                                    const Handle& threadHandle)
{
    return threadHandle;
}

inline
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::Handle
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::self()
{
    return pthread_self();
}

inline
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::Id
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::selfId()
{
    return pthread_self();
}

inline
bsls::Types::Uint64
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::selfIdAsInt()
{
    return idAsInt(selfId());
}

inline
bsls::Types::Uint64
bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::selfIdAsUint64()
{
    return idAsUint64(selfId());
}

                // *** Thread-Specific (Local) Storage (TSS or TLS) ***

inline
int bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::createKey(
                                       Key                         *key,
                                       bslmt_KeyDestructorFunction  destructor)
{
    return pthread_key_create(key,destructor);
}

inline
int bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::deleteKey(Key& key)
{
    return pthread_key_delete(key);
}

inline
void *bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::getSpecific(
                                                                const Key& key)
{
    return pthread_getspecific(key);
}

inline
int bslmt::ThreadUtilImpl<bslmt::Platform::PosixThreads>::setSpecific(
                                                             const Key&  key,
                                                             const void *value)
{
    return pthread_setspecific(key, value);
}

}  // close enterprise namespace

#endif  // BSLMT_PLATFORM_POSIX_THREADS

#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 ----------------------------------