Document number: N2178=07-0038
Programming Language C++, Evolution and Library Subgroups
 
Peter Dimov, <pdimov@pdimov.com>
 
2007-03-03

Proposed Text for Chapter 30, Thread Support Library [threads]

I. Overview

This document presents a complete proposal for Chapter 30, Thread Support Library, of the C++ standard. It is based on the threading portion of ISO/IEC 9945:2003, also known as "POSIX threads" or "pthreads". Part of it builds on a previous paper of mine, N2090=06-0160, whereas the rest of the C++ interface is loosely based on Boost.Threads, Howard Hinnant's suggestion to template std::condition on the mutex type, as well as on countless discussions on the Boost mailing lists and the committee reflectors, and contains contributions from too many people for me to be able to list.

II. Proposed Text

Chapter 30, Threading Support Library [threads]

This clause describes facilities that C++ programs may use to launch or control threads and to synchronize multiple threads.

Definitions

Throughout this chapter, the term ISO/IEC 9945 shall refer to ISO/IEC 9945:2003 with ISO/IEC 9945-2:2003-Cor 1:2004 applied.

Summary

The library provides a C interface and a C++ interface to the thread support facilities. Unless otherwise specified, the interface through which a facility is accessed does not affect its semantics.

The header <pthread.h> defines the C interface. It is compatible with the header of the same name defined by ISO/IEC 9945.

The header <thread> defines the C++ interface.

Header <pthread.h> synopsis

extern "C"
{

#define CLOCK_REALTIME unspecified
#define CLOCK_MONOTONIC unspecified

struct timespec;
typedef unspecified clockid_t;

int clock_gettime( clockid_t clock_id, struct timespec * tp );
int nanosleep( const struct timespec * rqtp, struct timespec * rmtp );

#define SCHED_FIFO unspecified
#define SCHED_RR unspecified
#define SCHED_OTHER unspecified

struct sched_param;

int sched_get_priority_max( int policy );
int sched_get_priority_min( int policy );
int sched_yield( void );

#define PTHREAD_CANCEL_ASYNCHRONOUS unspecified
#define PTHREAD_CANCEL_ENABLE unspecified
#define PTHREAD_CANCEL_DEFERRED unspecified
#define PTHREAD_CANCEL_DISABLE unspecified
#define PTHREAD_CANCELED unspecified
#define PTHREAD_COND_INITIALIZER unspecified
#define PTHREAD_CREATE_DETACHED unspecified
#define PTHREAD_CREATE_JOINABLE unspecified
#define PTHREAD_MUTEX_DEFAULT unspecified
#define PTHREAD_MUTEX_ERRORCHECK unspecified
#define PTHREAD_MUTEX_INITIALIZER unspecified
#define PTHREAD_MUTEX_NORMAL unspecified
#define PTHREAD_MUTEX_RECURSIVE unspecified
#define PTHREAD_ONCE_INIT unspecified
#define PTHREAD_PROCESS_SHARED unspecified
#define PTHREAD_PROCESS_PRIVATE unspecified

typedef unspecified pthread_attr_t;
typedef unspecified pthread_cond_t;
typedef unspecified pthread_condattr_t;
typedef unspecified pthread_key_t;
typedef unspecified pthread_mutex_t;
typedef unspecified pthread_mutexattr_t;
typedef unspecified pthread_once_t;
typedef unspecified pthread_rwlock_t;
typedef unspecified pthread_rwlockattr_t;
typedef unspecified pthread_t;

int pthread_attr_init( pthread_attr_t * attr );
int pthread_attr_destroy( pthread_attr_t * attr );
int pthread_attr_getdetachstate( const pthread_attr_t * attr, int * state );
int pthread_attr_setdetachstate( pthread_attr_t * attr, int state );
int pthread_attr_getschedparam( const pthread_attr_t * attr, struct sched_param * param );
int pthread_attr_setschedparam( pthread_attr_t * attr, const struct sched_param * param );
int pthread_attr_getstacksize( const pthread_attr_t * attr, size_t * size );
int pthread_attr_setstacksize( pthread_attr_t * attr, size_t size );
int pthread_attr_getguardsize( const pthread_attr_t * attr, size_t * guardsize );
int pthread_attr_setguardsize( pthread_attr_t * attr, size_t guardsize );

int pthread_create( pthread_t * thread, const pthread_attr_t * attr, void *(*start_routine)(void *), void * arg );
int pthread_detach( pthread_t thread );

int pthread_join( pthread_t thread, void ** value_ptr );
void pthread_exit( void * value_ptr );

int pthread_equal( pthread_t t1, pthread_t t2 );
pthread_t pthread_self( void );

int pthread_cancel( pthread_t thread );

int pthread_setcancelstate( int state, int * oldstate );
int pthread_setcanceltype( int type, int * oldtype );

void pthread_testcancel( void );

#define pthread_cleanup_push( routine, arg ) unspecified
#define pthread_cleanup_pop( execute ) unspecified

void * pthread_getspecific( pthread_key_t key );
int pthread_setspecific( pthread_key_t key, const void * value );

int pthread_key_create( pthread_key_t * key, void (*destructor)(void *) );
int pthread_key_delete( pthread_key_t key );

int pthread_mutexattr_init( pthread_mutexattr_t * attr );
int pthread_mutexattr_destroy( pthread_mutexattr_t * attr );
int pthread_mutexattr_gettype( const pthread_mutexattr_t * attr, int * type );
int pthread_mutexattr_settype( pthread_mutexattr_t * attr, int type );
int pthread_mutexattr_getpshared( const pthread_mutexattr_t * attr, int * pshared );
int pthread_mutexattr_setpshared( pthread_mutexattr_t * attr, int pshared );

int pthread_mutex_init( pthread_mutex_t * mutex, const pthread_mutexattr_t * attr );
int pthread_mutex_destroy( pthread_mutex_t * mutex );
int pthread_mutex_lock( pthread_mutex_t * mutex );
int pthread_mutex_timedlock( pthread_mutex_t * mutex, const struct timespec * abstime );
int pthread_mutex_trylock( pthread_mutex_t * mutex );
int pthread_mutex_unlock( pthread_mutex_t * mutex );

int pthread_condattr_init( pthread_condattr_t * attr );
int pthread_condattr_destroy( pthread_condattr_t * attr );
int pthread_condattr_getpshared( const pthread_condattr_t * attr, int * pshared );
int pthread_condattr_setpshared( pthread_condattr_t * attr, int pshared );
int pthread_condattr_getclock( const pthread_condattr_t * attr, clockid_t * clock_id );
int pthread_condattr_setclock( pthread_condattr_t * attr, clockid_t clock_id );

int pthread_cond_init( pthread_cond_t * cond, const pthread_condattr_t * attr );
int pthread_cond_destroy( pthread_cond_t * cond );
int pthread_cond_signal( pthread_cond_t * cond );
int pthread_cond_broadcast( pthread_cond_t * cond );
int pthread_cond_timedwait( pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec * abstime );
int pthread_cond_wait( pthread_cond_t * cond, pthread_mutex_t * mutex );

int pthread_rwlockattr_destroy( pthread_rwlockattr_t * attr );
int pthread_rwlockattr_init( pthread_rwlockattr_t * attr );
int pthread_rwlockattr_getpshared( const pthread_rwlockattr_t * attr, int * pshared );
int pthread_rwlockattr_setpshared( pthread_rwlockattr_t * attr, int pshared );

int pthread_rwlock_destroy( pthread_rwlock_t * rwlock );
int pthread_rwlock_init( pthread_rwlock_t * rwlock, const pthread_rwlockattr_t * attr );
int pthread_rwlock_rdlock( pthread_rwlock_t * rwlock );
int pthread_rwlock_timedrdlock( pthread_rwlock_t * rwlock, const struct timespec * abstime );
int pthread_rwlock_timedwrlock( pthread_rwlock_t * rwlock, const struct timespec * abstime );
int pthread_rwlock_tryrdlock( pthread_rwlock_t * rwlock );
int pthread_rwlock_trywrlock( pthread_rwlock_t * rwlock );
int pthread_rwlock_unlock( pthread_rwlock_t * rwlock );
int pthread_rwlock_wrlock( pthread_rwlock_t * rwlock );

int pthread_once( pthread_once_t * once_control, void (*init_routine)(void) );

// C++ support extensions (optional)

#define _POSIX_CXX09_EXTENSIONS

int pthread_less_np( pthread_t t1, pthread_t t2 );
size_t pthread_hash_np( pthread_t t1 );

int pthread_attach_np( pthread_t thread );

int pthread_once2_np( pthread_once_t * once_control, void (*init_routine)(void*), void * arg );

int pthread_join2_np( pthread_t thread );
int pthread_tryjoin2_np( pthread_t thread );
int pthread_timedjoin2_np( pthread_t thread, const struct timespec * abstime );

int pthread_attr_copy_np( pthread_attr_t * target, pthread_attr_t const * source );
int pthread_mutexattr_copy_np( pthread_mutexattr_t * target, pthread_mutexattr_t const * source );
int pthread_condattr_copy_np( pthread_condattr_t * target, pthread_condattr_t const * source );
int pthread_rwlockattr_copy_np( pthread_rwlockattr_t * target, pthread_rwlockattr_t const * source );

} // extern "C"

Unless otherwise specified, the symbols, types, functions and macros defined by <pthread.h> shall have semantics as described in ISO/IEC 9945.

The synopsis shows symbols as being defined as macros for presentation purposes. An implementation is allowed to define the symbols as integral constants or enumerated types.

The functions declared by <pthread.h> may also have masking macros with the same semantics. pthread_cleanup_push and pthread_cleanup_pop shall always be defined as macros and may have no function declarations.

The implementation shall ensure that the functions that accept a pointer to a function are callable with a pointer to a function having C++ linkage. The implementation is allowed to add function overloads with C++ linkage for this purpose.

The behavior of a C++ program that enables asynchronous cancelation for a thread that has a non-POF as its start routine ([support.runtime]/6) is undefined.

If the init_routine invoked by pthread_once throws an exception, the exception shall be propagated to the caller of pthread_once and the effect on once_control shall be as if pthread_once was never called.

The inclusion of <pthread.h> in a translation unit reserves certain identifiers to the implementation as specified by ISO/IEC 9945. In particular, all symbols beginning with PTHREAD_, pthread_, SCHED_ and sched_ are reserved to the implementation.

pthread_exit( value_ptr ) should behave as if it throws an exception std::thread_exit( value_ptr ).

Acting on a cancelation request should behave as if it unwinds the stack in response to an std::thread_cancel being thrown.

If an exception of type std::thread_exit or std::thread_cancel is caught and not rethrown, the program should resume its normal operation. In particular, if the stack unwinding was initiated by acting on a cancelation request, the thread should revert to its normal state and cancelation should be reenabled as if by a call to pthread_setcancelstate( PTHREAD_CANCEL_ENABLE ).

[Note: ISO/IEC 9945 requires that cancelation is disabled as if by a call to pthread_setcancelstate( PTHREAD_CANCEL_DISABLE ) before a cancelation request is acted upon. The preceding requirement undoes this operation. --end note]

The handlers installed by pthread_cleanup_push should be invoked in response to any C++ exception as if it were a cancelation exception.

C++ support extensions (optional)

The implementation is encouraged but not required to meet some additional requirements and provide additional functions in order to enable more efficient integration with the C++ interface.

The requirements in this section are optional, but if provided, they shall be implemented as described.

The implementation shall define the macro _POSIX_CXX09_EXTENSIONS if it meets the requirements of this section.

The pthread_t type shall have a null value, obtained via value initialization. A null pthread_t shall refer to the null thread and shall not compare equal to any other valid pthread_t value.

A thread shall have an associated reference count. The initial value of the reference count shall be 2 for joinable threads and 1 for detached threads. The reference count shall be decreased by one by pthread_join, pthread_detach, and the thread ending. When the reference count reaches zero, the pthread_t for the thread is invalidated and the storage for the thread should be reclaimed.

[Note: In the absence of pthread_attach_np calls, this behavior is compatible with ISO/IEC 9945. --end note]

pthread_cancel, pthread_detach, pthread_attach_np, pthread_join2_np, pthread_tryjoin2_np and pthread_timedjoin2_np shall accept the null thread as an argument and return 0 to indicate success; there shall be no other effects.

Unless otherwise specified, all functions require their pthread_t arguments to be valid.

The implementation shall support concurrent calls to functions operating on the same thread, unless the behavior of the sequential execution of the concurrent operations is undefined.

int pthread_less_np( pthread_t t1, pthread_t t2 );

Returns: an unspecified boolean value such that pthread_less_np is a total ordering on all valid pthread_t values, including null.

size_t pthread_hash_np( pthread_t t1 );

Returns: the hash value of its argument. Equal arguments shall yield the same result.

int pthread_attach_np( pthread_t thread );

Effects: Increases the reference count of thread.

Returns: 0.

int pthread_once2_np( pthread_once_t * once_control, void (*init_routine)(void*), void * arg );

Effects: Equivalent to pthread_once, except that arg is passed to init_routine.

int pthread_join2_np( pthread_t thread );

Effects: Suspends execution of the calling thread until thread terminates. This function shall be a cancelation point.

Returns: see pthread_join.

[Note: pthread_join2_np is similar to pthread_join, but it does not affect the reference count of thread and is therefore safe to call multiple times, potentially from different threads. --end note]

int pthread_tryjoin2_np( pthread_t thread );

Returns: 0 if thread has terminated, EBUSY if it has not. May also return EINVAL or ESRCH as per the specification of pthread_join.

int pthread_timedjoin2_np( pthread_t thread, const struct timespec * abstime );

Effects: Suspends execution of the calling thread until thread terminates or abstime is reached. This function shall be a cancelation point.

Returns: ETIMEDOUT if abstime is reached, one of the return values of pthread_join otherwise.

int pthread_attr_copy_np( pthread_attr_t * target, pthread_attr_t const * source );
int pthread_mutexattr_copy_np( pthread_mutexattr_t * target, pthread_mutexattr_t const * source );
int pthread_condattr_copy_np( pthread_condattr_t * target, pthread_condattr_t const * source );
int pthread_rwlockattr_copy_np( pthread_rwlockattr_t * target, pthread_rwlockattr_t const * source );

Requires: source and target refer to valid and initialized attribute objects.

Effects: Copies the attributes from *source to *target.

Returns: 0. May return EINVAL if the requires clause is violated for consistency with the pthread_*_get/set* family of functions.

Postconditions: *target is equivalent to *source.

Header <thread> synopsis

namespace std
{

// Exceptions

class thread_exit;
class thread_cancel: public thread_exit;
class thread_error: public exception;
class lock_error: public exception;

// Synchronization primitives

class mutex_attr;
class mutex;

template< class Mx > class basic_lock;
typedef basic_lock< mutex > scoped_lock;

class condition_attr;
template< class Mx > class basic_condition;
template<> class basic_condition< mutex >;
typedef basic_condition< mutex > condition;

class rwlock_attr;
class rwlock;

typedef basic_lock< rwlock > write_lock;

template< class Mx > class basic_read_lock;
typedef basic_read_lock< rwlock > read_lock;

// Thread-safe initialization

template< class F > void call_once( pthread_once_t & once_control, F f );
template< class F, class A1, ..., class An > void call_once( pthread_once_t & once_control, F f, A1 a1, ..., An an );

// Thread control

class thread_attr;

namespace thread
{

class handle;

bool operator==( handle const & h1, handle const & h2 );
bool operator<( handle const & h1, handle const & h2 );
size_t hash_value( handle const & th );

template< class F > handle create( pthread_attr_t const * attr, F f );
template< class F > handle create( thread_attr const & attr, F f );
template< class F, class A1, ..., class An > handle create( pthread_attr_t const * attr, F f, A1 a1, ..., An an );
template< class F, class A1, ..., class An > handle create( thread_attr const & attr, F f, A1 a1, ..., An an );

template< class F > handle create( F f );
template< class F, class A1, ..., class An > handle create( F f, A1 a1, ..., An an );

handle self();

int join( handle const & th );
int try_join( handle const & th );
int timed_join( handle const & th, timespec const & abstime );

void exit();

void cancel( handle const & th );
int set_cancel_state( int cs );
void test_cancel();

class disable_cancelation;

void sleep( timespec const & reltime );

void yield();
void yield( unsigned k );

unsigned concurrency();

} // namespace thread

} // namespace std
Exceptions
class thread_exit
{
private: // exposition only

    explicit thread_exit( void * pv );

public:

    void * value_ptr() const;
};

The class thread_exit, constructed with an argument pv, represents stack unwinding initiated by a call to pthread_exit( pv ) or std::thread::exit( pv ). It can only be constructed by the implementation.

[Note: an implementation is allowed to defer constructing an object of type thread_exit until such an object is caught by a catch clause. --end note]

explicit thread_exit( void * pv );

Postconditions: value_ptr() == pv.

void * value_ptr() const;

Returns: The value pv with which this object has been constructed.

class thread_cancel: public thread_exit
{
private: // exposition only

    thread_cancel();
};

The class thread_cancel represents stack unwinding initiated by a call to pthread_cancel or std::thread::cancel. It can only be constructed by the implementation.

[Note: an implementation is allowed to defer constructing an object of type thread_cancel until such an object is caught by a catch clause. --end note]

thread_cancel();

Postconditions: value_ptr() == PTHREAD_CANCELED.

class thread_error: public exception
{
private: // exposition only

    int r_;

public:

    explicit thread_error( int r );
    virtual char const * what() throw();
    int error() const;
};

The class thread_error is thrown by the implementation with an argument r when an underlying pthread_ function returns an error code r.

explicit thread_error( int r );

Effects: intializes r_ to r.

virtual char const * what() throw();

Returns: "std::thread_error".

int error() const;

Returns: r_.

class lock_error: public exception
{
private: // exposition only

    int r_;

public:

    explicit lock_error( int r );
    virtual char const * what() throw();
    int error() const;
};

The class lock_error is thrown by the lock classes when an error occurs.

explicit lock_error( int r );

Effects: intializes r_ to r.

virtual char const * what() throw();

Returns: "std::lock_error".

int error() const;

Returns: r_.

Synchronization primitives
class mutex_attr
{
private: // exposition only

    pthread_mutexattr_t attr_;

public:

    explicit mutex_attr( pthread_mutexattr_t const * attr = 0 );
    ~mutex_attr();

    mutex_attr( mutex_attr const & attr );
    mutex_attr & operator=( mutex_attr const & attr );

    operator pthread_mutexattr_t * ();
    operator pthread_mutexattr_t const * () const;

    int get_type() const;
    int set_type( int type );

    int get_pshared() const;
    int set_pshared( int pshared );
};

The class mutex_attr provides a C++ equivalent of pthread_mutexattr_t.

explicit mutex_attr( pthread_mutexattr_t const * attr = 0 );

Effects: Initializes attr_ as if by pthread_mutexattr_init( &attr_ ). If attr is not 0, calls pthread_mutexattr_copy_np( &attr_, attr ).

Throws: thread_error.

~mutex_attr();

Effects: pthread_mutexattr_destroy( &attr_ ).

Throws: nothing.

mutex_attr( mutex_attr const & attr );

Effects: Initializes attr_ as if by pthread_mutexattr_init( &attr_ ), then calls pthread_mutexattr_copy_np( &attr_, attr ).

Throws: thread_error.

mutex_attr & operator=( mutex_attr const & attr );

Effects: pthread_mutexattr_copy_np( &attr_, attr ).

Throws: thread_error.

operator pthread_mutexattr_t * ();
operator pthread_mutexattr_t const * () const;

Returns: &attr_.

Throws: nothing.

int get_type() const;

Returns: type as obtained by a call to pthread_mutexattr_gettype( &attr_, &type ).

Throws: nothing.

int set_type( int type );

Returns: pthread_mutexattr_settype( &attr_, type ).

Throws: nothing.

int get_pshared() const;

Returns: pshared as obtained by a call to pthread_mutexattr_getpshared( &attr_, &pshared ).

Throws: nothing.

int set_pshared( int pshared );

Returns: pthread_mutexattr_setpshared( &attr_, pshared ).

Throws: nothing.

class mutex
{
private: // exposition only

   pthread_mutex_t mx_;
   
   mutex( mutex const & );
   mutex & operator=( mutex const & );

public:

   explicit mutex( pthread_mutexattr_t const * attr = 0 );
   explicit mutex( int type );
   ~mutex();

   int lock();
   int try_lock();
   int timed_lock( timespec const & abstime );
   int unlock();
};

The class mutex provides a C++ equivalent of pthread_mutex_t.

explicit mutex( pthread_mutexattr_t const * attr = 0 );

Effects: pthread_mutex_init( &mx_, attr ).

Throws: thread_error( r ), where r is the error returned by pthread_mutex_init.

explicit mutex( int type );

Requires: type is one of PTHREAD_MUTEX_DEFAULT, PTHREAD_MUTEX_NORMAL, PTHREAD_MUTEX_ERRORCHECK, or PTHREAD_MUTEX_RECURSIVE.

Effects: pthread_mutex_init( &mx_, &attr ), where attr is an initialized object of type pthread_mutexattr_t, whose type attribute has been set to type as if by pthread_mutexattr_settype( &attr, type ).

Throws: thread_error( r ), where r is the error returned by pthread_mutex_init.

~mutex();

Effects: pthread_mutex_destroy( &mx_ ).

int lock();

Returns: pthread_mutex_lock( &mx_ ).

int try_lock();

Returns: pthread_mutex_trylock( &mx_ ).

int timed_lock( timespec const & abstime );

Returns: pthread_mutex_timedlock( &mx_, &abstime ).

int unlock();

Returns: pthread_mutex_unlock( &mx_ ).

template< class Mx > class basic_lock
{
private: // exposition only

    Mx * pmx_;
    bool locked_;
    
    basic_lock( basic_lock const & );
    basic_lock & operator=( basic_lock const & );

public:

    typedef Mx mutex_type;

    explicit basic_lock( Mx & mx, bool locked = true );
    ~basic_lock();

    void lock();
    bool try_lock();
    bool timed_lock( timespec const & abstime );
    void unlock();

    bool locked() const;
    Mx * mutex();
};

The class template basic_lock provides an exception-safe interface to mutex operations.

explicit basic_lock( Mx & mx, bool locked = true );

Effects: initializes pmx_ to &mx. If locked is true, calls lock().

~basic_lock();

Effects: if locked_ is true, calls unlock().

void lock();

Requires: !locked().

Effects: calls pmx_->lock(). If its return value r is 0, sets locked_ to true, otherwise throws lock_error( r ).

bool try_lock();

Requires: !locked().

Effects: calls pmx_->try_lock(). If its return value r is 0, sets locked_ to true, otherwise if r is not EBUSY, throws lock_error( r ).

Returns: locked_.

bool timed_lock( timespec & abstime );

Requires: !locked().

Effects: calls pmx_->timed_lock( abstime ). If its return value r is 0, sets locked_ to true, otherwise if r is not ETIMEDOUT, throws lock_error( r ).

Returns: locked_.

void unlock();

Requires: locked().

Effects: calls pmx_->unlock() and sets locked_ to false. If the call to pmx_->unlock() returns nonzero, the behavior is implementation-defined, except that no exception shall be thrown.

Throws: nothing.

bool locked() const;

Returns: locked_.

Mx * mutex();

Returns: pmx_.

class condition_attr
{
private: // exposition only

    pthread_condattr_t attr_;

public:

    explicit condition_attr( pthread_condattr_t const * attr = 0 );
    ~condition_attr();

    condition_attr( condition_attr const & attr );
    condition_attr & operator=( condition_attr const & attr );

    operator pthread_condattr_t * ();
    operator pthread_condattr_t const * () const;

    int get_pshared() const;
    int set_pshared( int pshared );

    clockid_t get_clock() const;
    int set_clock( clockid_t clock_id );
};

The class condition_attr provides a C++ equivalent of pthread_condattr_t.

explicit condition_attr( pthread_condattr_t const * attr = 0 );

Effects: Initializes attr_ as if by pthread_condattr_init( &attr_ ). If attr is not 0, calls pthread_condattr_copy_np( &attr_, attr ).

Throws: thread_error.

~condition_attr();

Effects: pthread_condattr_destroy( &attr_ ).

Throws: nothing.

condition_attr( condition_attr const & attr );

Effects: Initializes attr_ as if by pthread_condattr_init( &attr_ ), then calls pthread_condattr_copy_np( &attr_, attr ).

Throws: thread_error.

condition_attr & operator=( condition_attr const & attr );

Effects: pthread_condattr_copy_np( &attr_, attr ).

Throws: thread_error.

operator pthread_condattr_t * ();
operator pthread_condattr_t const * () const;

Returns: &attr_.

Throws: nothing.

int get_pshared() const;

Returns: pshared as obtained by a call to pthread_condattr_getpshared( &attr_, &pshared ).

Throws: nothing.

int set_pshared( int pshared );

Returns: pthread_condattr_setpshared( &attr_, pshared ).

Throws: nothing.

clockid_t get_clock() const;

Returns: clock_id as obtained by a call to pthread_condattr_getclock( &attr_, &clock_id ).

Throws: nothing.

int set_clock( clockid_t clock_id );

Returns: pthread_condattr_setclock( &attr_, clock_id ).

Throws: nothing.

template<> class basic_condition< mutex >
{
private: // exposition only

    pthread_cond_t cond_;
    
    basic_condition( basic_condition const & );
    basic_condition & operator=( basic_condition const & );

public:

    explicit basic_condition( pthread_condattr_t const * attr = 0 );
    ~basic_condition();

    template< class Lock > int wait( Lock & lock );
    template< class Lock > int timed_wait( Lock & lock, timespec const & abstime );

    int signal();
    int broadcast();
};

The specialization basic_condition<mutex>, also available under the name condition, provides a C++ interface to a pthread_cond_t object.

explicit basic_condition( pthread_condattr_t const * attr = 0 );

Effects: pthread_cond_init( &cond_, attr ).

Throws: thread_error( r ), where r is the error returned by pthread_cond_init.

~basic_condition();

Effects: pthread_cond_destroy( &cond_ ).

template< class Lock > int wait( Lock & lock );

Requires: lock.locked(); Lock::mutex_type shall be std::mutex.

Returns: pthread_cond_wait( &cond_, &lock.mutex()->mx_ ).

Throws: thread_cancel.

[Note: in some cases, wait can return due to a spurious wakeup, without signal or broadcast being called on the condition variable. The caller is expected to recheck the predicate upon return. --end note]

[Example:

template< class T > class queue
{
private:

    std::condition cond_;
    std::mutex mx_;

    std::deque< T > data_;

public:

    void pop( T & item )
    {
        std::scoped_lock lock( mx_ );

        while( data_.empty() ) cond_.wait( lock );

        item = data_.pop_front(); 
    }

    bool timed_pop( T & item, timespec const & abstime ); // see below

    void push( T const & item ); // see below
};

--end example.]

template< class Lock > int timed_wait( Lock & lock, timespec & abstime );

Requires: lock.locked(); Lock::mutex_type shall be std::mutex.

Returns: pthread_cond_timedwait( &cond_, &lock.mutex()->mx_, &abstime ).

Throws: thread_cancel.

[Note: in addition to spurious wakeups, the timed_wait function is susceptible to timing out even though the associated predicate may have become true. Re-checking the predicate on return is highly recommended. --end note]

[Example:

    bool timed_pop( T & item, timespec const & abstime )
    {
        std::scoped_lock lock( mx_ );

        while( data_.empty() )
        {
            int r = cond_.timed_wait( lock, abstime );

            assert( r == 0 || r == ETIMEDOUT );

            if( r == ETIMEDOUT )
            {
                if( data_.empty() )
                {
                    return false;
                }
                else
                {
                    break;
                }
            }
        }

        item = data_.pop_front();
        return true;
    }

--end example.]

int signal();

Returns: pthread_cond_signal( &cond_ ).

[Example:

    void push( T const & item )
    {
        {
            std::scoped_lock lock( mx_ );
            data_.push_back( item );
        }

        cond_.signal();
    }

--end example.]

int broadcast();

Returns: pthread_cond_broadcast( &cond_ ).

template< class Mx > class basic_condition
{
public:

    explicit basic_condition( pthread_condattr_t const * attr = 0 );
    ~basic_condition();

    template< class Lock > int wait( Lock & lock );
    template< class Lock > int timed_wait( Lock & lock, timespec const & abstime );

    int signal();
    int broadcast();
};

The class template basic_condition represents a generalized condition variable that is not tied to a specific mutex type.

explicit basic_condition( pthread_condattr_t const * attr = 0 );

Effects: initializes *this using the attributes provided in attr.

~basic_condition();

Effects: destroys *this.

Throws: nothing.

template< class Lock > int wait( Lock & lock );

Requires: lock.locked(); Lock::mutex_type shall be Mx.

Effects: Atomically unlocks lock and waits on this condition variable. Cancelation point.

Returns: see the specification of pthread_cond_wait.

Throws: thread_cancel.

template< class Lock > int timed_wait( Lock & lock, timespec const & abstime );

Requires: lock.locked(); Lock::mutex_type shall be Mx.

Effects: Atomically unlocks lock and waits on this condition variable until abstime is reached. Cancelation point.

Returns: see the specification of pthread_cond_timedwait.

Throws: thread_cancel.

int signal();

Effects: Unblocks one of the threads waiting on this condition variable.

Returns: see the specification of pthread_cond_signal.

int broadcast();

Effects: Unblocks all threads waiting on this condition variable.

Returns: see the specification of pthread_cond_broadcast.

[Note: a typical implementation of basic_condition is shown below:

template< class Lock > class __scoped_unlock
{
private:

    Lock & lock_;

public:

    explicit __scoped_unlock( Lock & lock ): lock_( lock )
    {
        assert( lock_.locked() );
        lock_.unlock();
    }

    ~__scoped_unlock()
    {
        lock_.lock();
    }
};

inline mutex_attr __mutexattr_from_condattr( pthread_condattr_t const * ca )
{
    mutex_attr ma;

    if( ca != 0 )
    {
        int pshared;

        if( pthread_condattr_getpshared( ca, &pshared ) == 0 )
        {
            ma.set_pshared( pshared );
        }
    }

    return ma;
}

template< class Mx > class basic_condition
{
private:

    condition cond_;
    mutex mx_;

    basic_condition( basic_condition const & );
    basic_condition & operator=( basic_condition const & );

public:

    basic_condition()
    {
    }

    explicit basic_condition( pthread_condattr_t const * attr ): cond_( attr ), mx_( __mutexattr_from_condattr( attr ) )
    {
    }

    template< class Lock > int wait( Lock & lock )
    {
        assert( lock.locked() );

        scoped_lock lk( mx_ );
        __scoped_unlock< Lock > unlock( lock );

        return cond_.wait( lk );
    }

    template< class Lock > int timed_wait( Lock & lock, timespec const & abstime )
    {
        assert( lock.locked() );

        scoped_lock lk( mx_ );
        __scoped_unlock< Lock > unlock( lock );

        return cond_.timed_wait( lk, abstime );
    }

    int signal()
    {
        scoped_lock lock( mx_ );
        return cond_.signal();
    }

    int broadcast()
    {
        scoped_lock lock( mx_ );
        return cond_.broadcast();
    }
};

--end note.]

class rwlock_attr
{
private: // exposition only

    pthread_rwlockattr_t attr_;

public:

    explicit rwlock_attr( pthread_rwlockattr_t const * attr = 0 );
    ~rwlock_attr();

    rwlock_attr( rwlock_attr const & attr );
    rwlock_attr & operator=( rwlock_attr const & attr );

    operator pthread_rwlockattr_t * ();
    operator pthread_rwlockattr_t const * () const;

    int get_pshared() const;
    int set_pshared( int pshared );
};

The class rwlock_attr provides a C++ equivalent of pthread_rwlockattr_t.

explicit rwlock_attr( pthread_rwlockattr_t const * attr = 0 );

Effects: Initializes attr_ as if by pthread_rwlockattr_init( &attr_ ). If attr is not 0, calls pthread_rwlockattr_copy_np( &attr_, attr ).

Throws: thread_error.

~rwlock_attr();

Effects: pthread_rwlockattr_destroy( &attr_ ).

Throws: nothing.

rwlock_attr( rwlock_attr const & attr );

Effects: Initializes attr_ as if by pthread_rwlockattr_init( &attr_ ), then calls pthread_rwlockattr_copy_np( &attr_, attr ).

Throws: thread_error.

rwlock_attr & operator=( rwlock_attr const & attr );

Effects: pthread_rwlockattr_copy_np( &attr_, attr ).

Throws: thread_error.

operator pthread_rwlockattr_t * ();
operator pthread_rwlockattr_t const * () const;

Returns: &attr_.

Throws: nothing.

int get_pshared() const;

Returns: pshared as obtained by a call to pthread_rwlockattr_getpshared( &attr_, &pshared ).

Throws: nothing.

int set_pshared( int pshared );

Returns: pthread_rwlockattr_setpshared( &attr_, pshared ).

Throws: nothing.

class rwlock
{
private: // exposition only

    pthread_rwlock_t rwl_;
    rwlock( rwlock const & );
    rwlock & operator=( rwlock const & );

public:

    explicit rwlock( pthread_rwlockattr_t const * attr = 0 );
    ~rwlock();

    int lock();
    int try_lock();
    int timed_lock( timespec const & abstime );
    int unlock();

    int rdlock();
    int try_rdlock();
    int timed_rdlock( timespec const & abstime );
    int rdunlock();
};

The class rwlock represents the C++ interface of pthread_rwlock_t.

explicit rwlock( pthread_rwlockattr_t const * attr = 0 );

Effects: pthread_rwlock_init( &rwl_, attr ).

Throws: thread_error( r ), where r is the error returned by pthread_rwlock_init.

~rwlock();

Effects: pthread_rwlock_destroy( &rwl_ ).

int lock();

Returns: pthread_rwlock_wrlock( &rwl_ ).

int try_lock();

Returns: pthread_rwlock_trywrlock( &rwl_ ).

int timed_lock( timespec const & abstime );

Returns: pthread_rwlock_timedwrlock( &rwl_, &abstime ).

int unlock();

Returns: pthread_rwlock_unlock( &rwl_ ).

int rdlock();

Returns: pthread_rwlock_rdlock( &rwl_ ).

int try_rdlock();

Returns: pthread_rwlock_tryrdlock( &rwl_ ).

int timed_rdlock( timespec const & abstime );

Returns: pthread_rwlock_timedrdlock( &rwl_, &abstime ).

int rdunlock();

Returns: pthread_rwlock_unlock( &rwl_ ).

template< class Mx > class basic_read_lock
{
private: // exposition only

    Mx * pmx_;
    bool locked_;

    basic_read_lock( basic_read_lock const & );
    basic_read_lock & operator=( basic_read_lock const & );

public:

    typedef Mx mutex_type;

    explicit basic_read_lock( Mx & mx, bool locked = true );
    ~basic_read_lock();

    void lock();
    bool try_lock();
    bool timed_lock( timespec const & abstime );
    void unlock();

    bool locked() const;
    Mx * mutex();
};

The class template basic_read_lock provides an exception-safe interface for locking a mutex for reading.

explicit basic_read_lock( Mx & mx, bool locked = true );

Effects: initializes pmx_ to &mx. If locked is true, calls lock().

~basic_read_lock();

Effects: if locked_ is true, calls unlock().

void lock();

Requires: !locked().

Effects: calls pmx_->rdlock(). If its return value r is 0, sets locked_ to true, otherwise throws lock_error( r ).

bool try_lock();

Requires: !locked().

Effects: calls pmx_->try_rdlock(). If its return value r is 0, sets locked_ to true, otherwise if r is not EBUSY, throws lock_error( r ).

Returns: locked_.

bool timed_lock( timespec const & abstime );

Requires: !locked().

Effects: calls pmx_->timed_rdlock( abstime ). If its return value r is 0, sets locked_ to true, otherwise if r is not ETIMEDOUT, throws lock_error( r ).

Returns: locked_.

void unlock();

Requires: locked().

Effects: calls pmx_->rdunlock() and sets locked_ to false. If the call to pmx_->rdunlock() returns nonzero, the behavior is implementation-defined, except that no exception shall be thrown.

Throws: nothing.

bool locked() const;

Returns: locked_.

Throws: nothing.

Mx * mutex();

Returns: pmx_.

Throws: nothing.

Thread-safe initialization
template< class F > void call_once( pthread_once_t & once_control, F f );

Requires: once_control shall have static storage duration and be statically initialized to PTHREAD_ONCE_INIT. The expression f() shall be well-defined.

Effects: call_once is equivalent to pthread_once with the call to init_routine() replaced by a call to f().

[Note: a typical implementation of call_once is shown below:

template< class F > void __call_once_helper( void * pv )
{
    F * pf = static_cast< F * >( pv );
    (*pf)();
}

template< class F > void call_once( pthread_once_t & once_control, F f )
{
    pthread_once2_np( &once_control, &__call_once_helper<F>, &f );
}

--end note.]

template< class F, class A1, ..., class An > void call_once( pthread_once_t & once_control, F f, A1 a1, ..., An an );

Effects: equivalent to call_once( once_control, bind( f, a1, ..., an ) ).

Thread control
class thread_attr
{
private: // exposition only

    pthread_attr_t attr_;

public:

    explicit thread_attr( pthread_attr_t const * attr = 0 );
    ~thread_attr();

    thread_attr( thread_attr const & attr );
    thread_attr & operator=( thread_attr const & attr );

    operator pthread_attr_t * ();
    operator pthread_attr_t const * () const;

    int get_detach_state() const;
    int set_detach_state( int detachstate );

    sched_param get_sched_param() const;
    int set_sched_param( sched_param const & schedparam );

    size_t get_stack_size() const;
    int set_stack_size( size_t stacksize );

    size_t get_guard_size() const;
    int set_guard_size( size_t guardsize );
};

The class thread_attr provides a C++ equivalent of pthread_attr_t.

explicit thread_attr( pthread_attr_t const * attr = 0 );

Effects: Initializes attr_ as if by pthread_attr_init( &attr_ ). If attr is not 0, calls pthread_attr_copy_np( &attr_, attr ).

Throws: thread_error.

~thread_attr();

Effects: pthread_attr_destroy( &attr_ ).

Throws: nothing.

thread_attr( thread_attr const & attr );

Effects: Initializes attr_ as if by pthread_attr_init( &attr_ ), then calls pthread_attr_copy_np( &attr_, attr ).

Throws: thread_error.

thread_attr & operator=( thread_attr const & attr );

Effects: pthread_attr_copy_np( &attr_, attr ).

Throws: thread_error.

operator pthread_attr_t * ();
operator pthread_attr_t const * () const;

Returns: &attr_.

Throws: nothing.

int get_detach_state() const;

Returns: detachstate as obtained by a call to pthread_attr_getdetachstate( &attr_, &detachstate ).

Throws: nothing.

int set_detach_state( int detachstate );

Returns: pthread_attr_setdetachstate( &attr_, detachstate ).

Throws: nothing.

sched_param get_sched_param() const;

Returns: schedparam as obtained by a call to pthread_attr_getschedparam( &attr_, &schedparam ).

Throws: nothing.

int set_sched_param( sched_param const & schedparam );

Returns: pthread_attr_setschedparam( &attr_, &schedparam ).

Throws: nothing.

size_t get_stack_size() const;

Returns: stacksize as obtained by a call to pthread_attr_getstacksize( &attr_, &stacksize ).

Throws: nothing.

int set_stack_size( size_t stacksize );

Returns: pthread_attr_setstacksize( &attr_, stacksize ).

Throws: nothing.

size_t get_guard_size() const;

Returns: guardsize as obtained by a call to pthread_attr_getguardsize( &attr_, &guardsize ).

Throws: nothing.

int set_guard_size( size_t guardsize );

Returns: pthread_attr_setguardsize( &attr_, guardsize ).

Throws: nothing.

class handle
{
private: // exposition only

    pthread_t pt_;

    void swap( handle const & rhs );

public:

    handle();
    handle( handle const & rhs );
    handle & operator=( handle const & rhs );
    ~handle();
};

The class handle represents a handle to a thread.

The operations on handle are described under the assumption that the C++ support extensions are available. This is done to simplify the specification and does not imply that the extensions are required.

void swap( handle const & rhs );

Effects: exchanges the contents of *this and rhs.

Throws: nothing.

handle();

Effects: Initializes pt_ to the null pthread_t value.

Throws: nothing.

handle( handle const & rhs );

Effects: Initializes pt_ to rhs.pt_ and calls pthread_attach_np( pt_ ).

Throws: nothing.

handle & operator=( handle const & rhs );

Effects: handle( rhs ).swap( *this ).

Returns: *this.

Throws: nothing.

~handle();

Effects: pthread_detach( pt_ ).

Throws: nothing.

bool operator==( handle const & h1, handle const & h2 );

Returns: pthread_equal( h1.pt_, h2.pt_ ).

Throws: nothing.

bool operator<( handle const & h1, handle const & h2 );

Returns: pthread_less_np( h1.pt_, h2.pt_ ).

Throws: nothing.

size_t hash_value( handle const & th );

Returns: pthread_hash_np( th.pt_ ).

Throws: nothing.

template< class F > handle create( pthread_attr_t const * attr, F f );
template< class F > handle create( thread_attr const & attr, F f );

Effects: Creates a new thread that executes f() and uses the attributes specified by attr. The attributes are interpreted as described in the specification of pthread_create, except that the new thread is always created as joinable.

[Note: since the function returns a handle to the caller, the initial reference count of the new thread needs to take it into account. This is accomplished by creating a joinable thread. If the caller does not store the returned handle, the thread will be detached by ~handle. --end note]

Returns: A handle to the new thread.

Throws: thread_error on failure.

[Note: one possible implementation of create is shown below:

template< class F > void * __threadproc( void * pv )
{
    std::auto_ptr< F > pf( static_cast< F * >( pv ) );

    (*pf)();

    return 0;
}

template< class F > handle create( pthread_attr_t const * attr, F f )
{
    std::auto_ptr<F> pf( new F( f ) );

    pthread_t pt;
    int r;

    if( attr != 0 )
    {
        thread_attr attr2( attr );
        attr2.set_detach_state( PTHREAD_CREATE_JOINABLE );

        r = pthread_create( &pt, attr2, &__threadproc<F>, pf.get() );
    }
    else
    {
        r = pthread_create( &pt, 0, &__threadproc<F>, pf.get() );
    }

    if( r != 0 )
    {
        throw thread_error( r );
    }

    pf.release();

    return handle( pt, false ); // private constructor, does not call pthread_attach_np
}

--end note.]

template< class F, class A1, ..., class An > handle create( pthread_attr_t const * attr, F f, A1 a1, ..., An an );
template< class F, class A1, ..., class An > handle create( thread_attr const & attr, F f, A1 a1, ..., An an );

Returns: create( attr, bind( f, a1, ..., an ) ).

template< class F > handle create( F f );

Returns: create( static_cast< pthread_attr_t const * >( 0 ), f ).

template< class F, class A1, ..., class An > handle create( F f, A1 a1, ..., An an );

Returns: create( static_cast< pthread_attr_t const * >( 0 ), bind( f, a1, ..., an ) ).

handle self();

Effects: pthread_attach_np( pthread_self() ).

Returns: a handle that stores pthread_self() as pt_.

int join( handle const & th );

Returns: pthread_join2_np( th.pt_ ).

Throws: thread_cancel.

int try_join( handle const & th );

Returns: pthread_tryjoin2_np( th.pt_ ).

Throws: nothing.

int timed_join( handle const & th, timespec const & abstime );

Returns: pthread_timedjoin2_np( th.pt_, &abstime ).

Throws: thread_cancel.

void exit();

Effects: Exits the current thread as if by calling pthread_exit( 0 ).

Throws: thread_exit( 0 ).

void cancel( handle const & th );

Effects: Delivers a cancel request to the thread identified by th as if by calling pthread_cancel( th.pt_ ).

Throws: nothing.

int set_cancel_state( int cs );

Requires: cs is PTHREAD_CANCEL_ENABLE or PTHREAD_CANCEL_DISABLE.

Effects: Sets the cancel state of the current thread to cs as if by calling pthread_setcancelstate.

Returns: The previous cancel state for the current thread.

Throws: nothing.

void test_cancel();

Effects: This function is a cancelation point. It has no other effects.

Throws: thread_cancel.

class disable_cancelation
{
private: // exposition only

    int cs_;

public:

    disable_cancelation();
    ~disable_cancelation();
};

The class disable_cancelation disables cancelations for the current scope.

disable_cancelation();

Effects: Initializes cs_ to set_cancel_state( PTHREAD_CANCEL_DISABLE ).

Throws: nothing.

~disable_cancelation();

Effects: set_cancel_state( cs_ );.

Throws: nothing.

void sleep( timespec const & reltime );

Effects: Suspends the execution of the current thread for the amount of time represented by reltime. This function is a cancelation point.

Throws: thread_cancel.

void yield();

Effects: Equivalent to sched_yield().

Throws: nothing.

void yield( unsigned k );

Effects: Provides a hint to the implementation that the current thread has been unable to make progress for k iterations. For the purposes of this definition, an iteration typically consists of a few machine instructions, some of them atomic.

Throws: nothing.

[Note: An implementation of yield might do the one of the following based on the value of k, listed from small to large values:

--end note.]

[Example:

void acquire_spinlock( int * spinlock )
{
    for( unsigned k = 0; atomic_swap_acquire( &spinlock, 1 ); ++k )
    {
        std::thread::yield( k );
    }
}

--end example.]

unsigned concurrency();

Returns: The approximate number of non-competing threads in the current process that will ensure optimal load for the underlying platform.

[Note: This is usually equal to the number of processing cores, but hardware support for symmetric multithreading or process affinity masks can affect the value. Successive calls may return different values. --end note.]

[Note: This function returns a value that is not compatible with that returned by pthread_getconcurrency. --end note]

III. Design Principles and Assumptions

A. Pthreads as a C interface

The central assumption of this proposal is that we want a common threading infrastructure for C and C++, with the two interfaces just being alternative ways to get at the common functionality. Among other things, this would allow mixed C/C++ code to work reliably in the presence of multiple threads.

It would be unrealistic for the C++ committee to innovate in the area of C threading interfaces, which is why the document makes the obvious decision to build on the existing ISO/IEC POSIX standard, which also happens to be the de-facto threading standard, available essentially everywhere.

Adopting a subset of the Pthreads C binding as part of chapter 30 also allows us more latitude in defining the C++ API; we can leave some facilities we deem too advanced or less critical as only accessible via <pthread.h>. At the same time, the design aims to make <thread> without <pthread.h> sufficient for most common tasks.

B. Pthreads as a C++ interface

Given the decision to adopt Pthreads as the C interface to a common threading infrastructure, it makes sense for the C++ portion to represent a C++ binding to the POSIX threading model and not to define an incompatible model of its own.

C. Cancelation

The proposed text assumes that we want, in a few years time, to be able to freely use Pthreads cancelation in C++ code and C++ cancelation in C code. It also assumes that we want to be able to throw C++ exceptions through C code and have a mechanism via which the C code can release its resources as its scopes are left during the stack unwinding.

D. Expressive Power of the C++ API

With one exception (not providing support for a void* thread return value), the proposal conservatively places no additional constraints on the C++ side that aren't already present in Pthreads. In particular, the C++ API does not prohibit:

IV. Design Decisions

A. Naming

The proposed text uses the Pthreads names with the words separated with underscores for its C++ portion, consistent with its C++ binding nature.

B. POSIX Function Subset

The proposal generally aimed to include a portable subset of the POSIX functions that is required to implement the C++ API with most of its functions being one-liners. The starting point was the list of pthread_* functions that are described as always available on XSI-conformant systems, with the following subtractions:

pthread_atfork, pthread_attr_get/setstack, pthread_attr_get/setstackaddr, pthread_kill, pthread_sigmask, pthread_get/setconcurrency

and the following additions:

pthread_*_timedlock, CLOCK_MONOTONIC, pthread_condattr_get/setclock

The subtractions are either not useful in practice or were motivated by the current limitations of the Windows platform.

Some of the additions (or the absence of some subtractions) may prove controversial, even though they are backed by extensive POSIX motivation and rationale. They are part of the proposed text since their absence would unfairly tip the scales in favor of not including them in the standard based on non-technical grounds.

The document uses struct timespec for timeouts even in its C++ portion due to current lack of a suitable C++ type. This has necessitated the inclusion of clock_gettime, because otherwise the user would have no direct way of coming up with proper values for a timespec.

C. Copyable Thread Handles

The proposal uses the copyable handle model presented in N2090, motivated by the following:

On the efficiency side, the proposed text contains suggested minimal extensions to the Pthreads infrastructure that enables the copyable handle to be implemented essentially with one-liners and little to no overhead over a pthread_t. When the suggested extensions are not available, the current prototype implementation emulates them, requiring less memory per thread than the N2090 prototype.

D. Thread Safety

The proposal allows concurrent operations on the same thread. This allows a portion of the code to observe or manipulate a thread via a handle without fear of introducing undefined behavior due to a data race.

E. Implementation Hiding

The proposal aims to maximize the flexibility of the implementation by not exposing the internal layout of the underlying data structures. This could allow a vendor to ship an updated implementation while still maintaining binary compatibility.

F. <pthread.h> vs <cpthread>

The proposal uses the POSIX name of the header, <pthread.h>, consistent with the new approach to .h headers that the committee seems to be adopting: leave the C header.h unmodified and provide <cheader> as a C++ alias.

A <cpthread> is conservatively not proposed at the moment, but it can be added if deemed necessary. The current absence of <cpthread> reflects the intent that a C++ program should generally be able to only use <thread> and std:: facilities (except in rare circumstances such as using statically initialized objects in an implementation file); that is, <thread> is intended to serve as a C++ binding to the same underlying POSIX thread support infrastructure.

The proposal does not move the <pthread.h> names into namespace std because the names are allowed to be macros.

The document specifies the full synopsis of the subset of <pthread.h> that is guaranteed to be provided by conforming C++ implementations, rather than list only the differences between it and POSIX's <pthread.h>. It's much easier for a programmer to just look at the synopsis to see what is available rather than to have to apply a mental "patch" to the correct revision of POSIX <pthread.h> using a "diff" in the C++ standard.

G. Cancelation

I won't rehash the endless cancelation debates here, as most of us have grown tired of them. I'll just say the following:

We have an unique opportunity before us. The chance to make mixed C/C++ code possible to write correctly and reliably, even in the presence of multiple threads. The POSIX group has already done the heavy lifting for us, effectively introducing exceptions and destructors into C via pthread_exit and pthread_cleanup_push. Now we just need to acknowledge their efforts in the C++ standard.

Despite the fact that proper exception-based cancelation is a fearsome and controversial topic, it has been shown that there are no insurmountable technical problems that stand in the way. The primary problem that precludes its adoption is lack of will.

V. Implementability

The three main platforms an implementation would need to support are POSIX with the proposed extensions, POSIX without the proposed extensions, and Windows.

One implementation approach is to reduce each platform to the previous one in the list by providing an appropriate emulation layer.

Windows can be converted into a POSIX platform by using pthreads-win32, available at http://sourceware.org/pthreads-win32/.

A POSIX platform not supporting the proposed extensions can be converted to one that does by using an emulation layer that defines a separate thread handle (called in the prototype implementaion pthread2_t) on top of the native pthread_t. A prototype implementation of such an emulation layer is available at:

Finally, a POSIX platform providing the proposed extensions is sufficiently powerful to allow a nearly-trivial implementation of <thread>. A prototype is available at:

A prototype implementation of the proposed <pthread.h> on the Windows platform, including a native implementation of the proposed extensions, will be available for the meeting.


Thanks to Beman Dawes, Alexander Terekhov, Ion Gaztañaga and Emil Dotchevski for reviewing this document.

--end