#ifndef THREAD_HPP_INCLUDED #define THREAD_HPP_INCLUDED // Copyright (c) 2007 Peter Dimov // // Distributed under the Boost Software License, Version 1.0. // http://www.boost.org/LICENSE_1_0.txt #include "pthread.hpp" #include #include #include #include #include #include #ifndef ETIMEDOUT # define ETIMEDOUT 10060 #endif namespace std { // Exceptions class thread_exit { private: explicit thread_exit( void * value_ptr ); public: void * value_ptr() const; }; class thread_cancel: public thread_exit { private: thread_cancel(); }; class thread_error: public std::exception { private: int r_; public: explicit thread_error( int r ): r_( r ) { } virtual char const * what() throw() { return "std::thread_error"; } int error() const { return r_; } }; // Mutex template< class Mx > class basic_condition; class mutex_attr { private: pthread_mutexattr_t attr_; public: mutex_attr() { int r = pthread_mutexattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } } ~mutex_attr() { pthread_mutexattr_destroy( &attr_ ); } explicit mutex_attr( pthread_mutexattr_t const * attr ) { int r = pthread_mutexattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } if( attr ) { r = pthread_mutexattr_copy_np( &attr_, attr ); if( r != 0 ) { pthread_mutexattr_destroy( &attr_ ); throw thread_error( r ); } } } mutex_attr( mutex_attr const & attr ) { int r = pthread_mutexattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } r = pthread_mutexattr_copy_np( &attr_, attr ); if( r != 0 ) { pthread_mutexattr_destroy( &attr_ ); throw thread_error( r ); } } mutex_attr & operator=( mutex_attr const & attr ) { int r = pthread_mutexattr_copy_np( &attr_, attr ); if( r != 0 ) { throw thread_error( r ); } } operator pthread_mutexattr_t * () { return &attr_; } operator pthread_mutexattr_t const * () const { return &attr_; } int get_type() const { int type = PTHREAD_MUTEX_DEFAULT; int r = pthread_mutexattr_gettype( &attr_, &type ); assert( r == 0 ); return type; } int set_type( int type ) { return pthread_mutexattr_settype( &attr_, type ); } int get_pshared() const { int pshared = PTHREAD_PROCESS_PRIVATE; int r = pthread_mutexattr_getpshared( &attr_, &pshared ); assert( r == 0 ); return pshared; } int set_pshared( int pshared ) { return pthread_mutexattr_setpshared( &attr_, pshared ); } }; class mutex { private: pthread_mutex_t mx_; mutex( mutex const & ); mutex & operator=( mutex const & ); template< class Mx > friend class basic_condition; public: explicit mutex( pthread_mutexattr_t const * attr = 0 ) { int r = pthread_mutex_init( &mx_, attr ); if( r != 0 ) { throw thread_error( r ); } } explicit mutex( int type ) { assert( type == PTHREAD_MUTEX_DEFAULT || type == PTHREAD_MUTEX_NORMAL || type == PTHREAD_MUTEX_ERRORCHECK || type == PTHREAD_MUTEX_RECURSIVE ); mutex_attr attr; int r = attr.set_type( type ); if( r != 0 ) { throw thread_error( r ); } r = pthread_mutex_init( &mx_, attr ); if( r != 0 ) { throw thread_error( r ); } } ~mutex() { pthread_mutex_destroy( &mx_ ); } int lock() { return pthread_mutex_lock( &mx_ ); } int try_lock() { return pthread_mutex_trylock( &mx_ ); } int timed_lock( timespec const & abstime ) { return pthread_mutex_timedlock( &mx_, &abstime ); } int unlock() { return pthread_mutex_unlock( &mx_ ); } }; class lock_error: public exception { private: int r_; public: explicit lock_error( int r ): r_( r ) { } virtual char const * what() throw() { return "std::lock_error"; } int error() const { return r_; } }; template< class Mx > class basic_lock { private: 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 ): pmx_( &mx_ ), locked_( false ) { if( locked ) { lock(); } } ~basic_lock() { if( locked_ ) { unlock(); } } void lock() { assert( !locked_ ); int r = pmx_->lock(); if( r == 0 ) { locked_ = true; } else { throw lock_error( r ); } } bool try_lock() { assert( !locked_ ); int r = pmx_->try_lock(); if( r == 0 ) { locked_ = true; } else if( r == EBUSY ) { } else { throw lock_error( r ); } return locked_; } bool timed_lock( timespec const & abstime ) { assert( !locked_ ); int r = pmx_->timed_lock( abstime ); if( r == 0 ) { locked_ = true; } else if( r == ETIMEDOUT ) { } else { throw lock_error( r ); } return locked_; } void unlock() { assert( locked_ ); int r = pmx_->unlock(); assert( r == 0 ); locked_ = false; } bool locked() const { return locked_; } Mx * mutex() { return pmx_; } }; typedef basic_lock< mutex > scoped_lock; // Once 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 ); } // Condition class condition_attr { private: pthread_condattr_t attr_; public: condition_attr() { int r = pthread_condattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } } ~condition_attr() { pthread_condattr_destroy( &attr_ ); } explicit condition_attr( pthread_condattr_t const * attr ) { int r = pthread_condattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } if( attr ) { r = pthread_condattr_copy_np( &attr_, attr ); if( r != 0 ) { pthread_condattr_destroy( &attr_ ); throw thread_error( r ); } } } condition_attr( condition_attr const & attr ) { int r = pthread_condattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } r = pthread_condattr_copy_np( &attr_, attr ); if( r != 0 ) { pthread_condattr_destroy( &attr_ ); throw thread_error( r ); } } condition_attr & operator=( condition_attr const & attr ) { int r = pthread_condattr_copy_np( &attr_, attr ); if( r != 0 ) { throw thread_error( r ); } } operator pthread_condattr_t * () { return &attr_; } operator pthread_condattr_t const * () const { return &attr_; } int get_pshared() const { int pshared = PTHREAD_PROCESS_PRIVATE; int r = pthread_condattr_getpshared( &attr_, &pshared ); assert( r == 0 ); return pshared; } int set_pshared( int pshared ) { return pthread_condattr_setpshared( &attr_, pshared ); } clockid_t get_clock() const { clockid_t clock_id = CLOCK_REALTIME; int r = pthread_condattr_getclock( &attr_, &clock_id ); assert( r == 0 ); return clock_id; } int set_clock( clockid_t clock_id ) { return pthread_condattr_setclock( &attr_, clock_id ); } }; template<> class basic_condition< mutex >; typedef basic_condition< mutex > condition; 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(); } }; template<> class basic_condition< mutex > { private: 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 ) { int r = pthread_cond_init( &cond_, attr ); if( r != 0 ) { throw thread_error( r ); } } ~basic_condition() { pthread_cond_destroy( &cond_ ); } template< class Lock > int wait( Lock & lock ) { assert( lock.locked() ); return pthread_cond_wait( &cond_, &lock.mutex()->mx_ ); } template< class Lock > int timed_wait( Lock & lock, timespec const & abstime ) { assert( lock.locked() ); return pthread_cond_timedwait( &cond_, &lock.mutex()->mx_, &abstime ); } int signal() { return pthread_cond_signal( &cond_ ); } int broadcast() { return pthread_cond_broadcast( &cond_ ); } }; // Read/write lock class rwlock_attr { private: pthread_rwlockattr_t attr_; public: rwlock_attr() { int r = pthread_rwlockattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } } ~rwlock_attr() { pthread_rwlockattr_destroy( &attr_ ); } explicit rwlock_attr( pthread_rwlockattr_t const * attr ) { int r = pthread_rwlockattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } if( attr ) { r = pthread_rwlockattr_copy_np( &attr_, attr ); if( r != 0 ) { pthread_rwlockattr_destroy( &attr_ ); throw thread_error( r ); } } } rwlock_attr( rwlock_attr const & attr ) { int r = pthread_rwlockattr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } r = pthread_rwlockattr_copy_np( &attr_, attr ); if( r != 0 ) { pthread_rwlockattr_destroy( &attr_ ); throw thread_error( r ); } } rwlock_attr & operator=( rwlock_attr const & attr ) { int r = pthread_rwlockattr_copy_np( &attr_, attr ); if( r != 0 ) { throw thread_error( r ); } } operator pthread_rwlockattr_t * () { return &attr_; } operator pthread_rwlockattr_t const * () const { return &attr_; } int get_pshared() const { int pshared = PTHREAD_PROCESS_PRIVATE; int r = pthread_rwlockattr_getpshared( &attr_, &pshared ); assert( r == 0 ); return pshared; } int set_pshared( int pshared ) { return pthread_rwlockattr_setpshared( &attr_, pshared ); } }; class rwlock { private: pthread_rwlock_t rwl_; rwlock( rwlock const & ); rwlock & operator=( rwlock const & ); public: explicit rwlock( pthread_rwlockattr_t const * attr = 0 ) { int r = pthread_rwlock_init( &rwl_, 0 /*attr*/ ); // pthreads-win32 2.8 is broken if( r != 0 ) { throw thread_error( r ); } } ~rwlock() { pthread_rwlock_destroy( &rwl_ ); } int lock() { return pthread_rwlock_wrlock( &rwl_ ); } int try_lock() { return pthread_rwlock_trywrlock( &rwl_ ); } int timed_lock( timespec const & abstime ) { return pthread_rwlock_timedwrlock( &rwl_, &abstime ); } int unlock() { return pthread_rwlock_unlock( &rwl_ ); } int rdlock() { return pthread_rwlock_rdlock( &rwl_ ); } int try_rdlock() { return pthread_rwlock_tryrdlock( &rwl_ ); } int timed_rdlock( timespec const & abstime ) { return pthread_rwlock_timedrdlock( &rwl_, &abstime ); } int rdunlock() { return pthread_rwlock_unlock( &rwl_ ); } }; typedef basic_lock< rwlock > write_lock; template< class Mx > class basic_read_lock { private: 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 ): pmx_( &mx_ ), locked_( false ) { if( locked ) { lock(); } } ~basic_read_lock() { if( locked_ ) { unlock(); } } void lock() { assert( !locked_ ); int r = pmx_->rdlock(); if( r == 0 ) { locked_ = true; } else { throw lock_error( r ); } } bool try_lock() { assert( !locked_ ); int r = pmx_->try_rdlock(); if( r == 0 ) { locked_ = true; } else if( r == EBUSY ) { } else { throw lock_error( r ); } return locked_; } bool timed_lock( timespec const & abstime ) { assert( !locked_ ); int r = pmx_->timed_rdlock( abstime ); if( r == 0 ) { locked_ = true; } else if( r == ETIMEDOUT ) { } else { throw lock_error( r ); } return locked_; } void unlock() { assert( locked_ ); int r = pmx_->rdunlock(); assert( r == 0 ); locked_ = false; } bool locked() const { return locked_; } Mx * mutex() { return pmx_; } }; typedef basic_read_lock< rwlock > read_lock; // Thread class thread_attr { private: pthread_attr_t attr_; public: thread_attr() { int r = pthread_attr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } } ~thread_attr() { pthread_attr_destroy( &attr_ ); } thread_attr( thread_attr const & attr ) { int r = pthread_attr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } r = pthread_attr_copy_np( &attr_, attr ); if( r != 0 ) { pthread_attr_destroy( &attr_ ); throw thread_error( r ); } } explicit thread_attr( pthread_attr_t const * attr ) { int r = pthread_attr_init( &attr_ ); if( r != 0 ) { throw thread_error( r ); } if( attr ) { r = pthread_attr_copy_np( &attr_, attr ); if( r != 0 ) { pthread_attr_destroy( &attr_ ); throw thread_error( r ); } } } thread_attr & operator=( thread_attr const & attr ) { int r = pthread_attr_copy_np( &attr_, attr ); if( r != 0 ) { throw thread_error( r ); } } operator pthread_attr_t * () { return &attr_; } operator pthread_attr_t const * () const { return &attr_; } int get_detach_state() const { int detachstate = PTHREAD_CREATE_JOINABLE; int r = pthread_attr_getdetachstate( &attr_, &detachstate ); // pthreads-win32 2.8 doesn't conform assert( r == 0 ); return detachstate; } int set_detach_state( int detachstate ) { return pthread_attr_setdetachstate( &attr_, detachstate ); } sched_param get_sched_param() const { sched_param schedparam = {}; int r = pthread_attr_getschedparam( &attr_, &schedparam ); assert( r == 0 ); return schedparam; } int set_sched_param( sched_param const & schedparam ) { return pthread_attr_setschedparam( &attr_, &schedparam ); } size_t get_stack_size() const { size_t stacksize = 0; int r = pthread_attr_getstacksize( &attr_, &stacksize ); assert( r == 0 ); return stacksize; } int set_stack_size( size_t stacksize ) { return pthread_attr_setstacksize( &attr_, stacksize ); } }; namespace thread { class handle { public: // private pthread2_t __pt; handle( pthread2_t pt, bool attach ): __pt( pt ) { if( attach ) { pthread_attach_np( __pt ); } } void swap( handle & rhs ) { std::swap( __pt, rhs.__pt ); } public: handle(): __pt() { } handle( handle const & rhs ): __pt( rhs.__pt ) { pthread_attach_np( __pt ); } handle & operator=( handle const & rhs ) { handle( rhs ).swap( *this ); return *this; } ~handle() { pthread_detach( __pt ); } }; inline bool operator==( handle h1, handle h2 ) { return pthread_equal( h1.__pt, h2.__pt ); } inline bool operator<( handle h1, handle h2 ) { return pthread_less_np( h1.__pt, h2.__pt ); } inline size_t hash_value( handle th ) { return pthread_hash_np( th.__pt ); } 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 pf( new F( f ) ); pthread2_t pt; int r; if( attr != 0 ) { thread_attr attr2( attr ); attr2.set_detach_state( PTHREAD_CREATE_JOINABLE ); r = pthread_create( &pt, attr2, &__threadproc, pf.get() ); } else { r = pthread_create( &pt, 0, &__threadproc, pf.get() ); } if( r != 0 ) { throw thread_error( r ); } pf.release(); return handle( pt, false ); } // template< class F, class A1 > handle create( pthread_attr_t const * attr, F f, A1 a1 ) { return create( attr, boost::bind( f, a1 ) ); } template< class F, class A1, class A2 > handle create( pthread_attr_t const * attr, F f, A1 a1, A2 a2 ) { return create( attr, boost::bind( f, a1, a2 ) ); } template< class F, class A1, class A2, class A3 > handle create( pthread_attr_t const * attr, F f, A1 a1, A2 a2, A3 a3 ) { return create( attr, boost::bind( f, a1, a2, a3 ) ); } // template< class F > handle create( thread_attr const & attr, F f ) { return create( static_cast< pthread_attr_t const * >( attr ), f ); } template< class F, class A1 > handle create( thread_attr const & attr, F f, A1 a1 ) { return create( static_cast< pthread_attr_t const * >( attr ), boost::bind( f, a1 ) ); } template< class F, class A1, class A2 > handle create( thread_attr const & attr, F f, A1 a1, A2 a2 ) { return create( static_cast< pthread_attr_t const * >( attr ), boost::bind( f, a1, a2 ) ); } // template< class F > handle create( F f ) { return create( static_cast< pthread_attr_t const * >( 0 ), f ); } template< class F, class A1 > handle create( F f, A1 a1 ) { return create( static_cast< pthread_attr_t const * >( 0 ), boost::bind( f, a1 ) ); } template< class F, class A1, class A2 > handle create( F f, A1 a1, A2 a2 ) { return create( static_cast< pthread_attr_t const * >( 0 ), boost::bind( f, a1, a2 ) ); } inline handle self() { return handle( __pthread_self(), true ); } inline int join( handle const & th ) { return pthread_join2_np( th.__pt ); } inline int try_join( handle const & th ) { return pthread_tryjoin2_np( th.__pt ); } inline int timed_join( handle const & th, timespec const & abstime ) { return pthread_timedjoin2_np( th.__pt, &abstime ); } inline int cancel( handle const & th ) { return pthread_cancel( th.__pt ); } inline int set_cancel_state( int cs ) { assert( cs == PTHREAD_CANCEL_ENABLE || cs == PTHREAD_CANCEL_DISABLE ); int r = PTHREAD_CANCEL_ENABLE; pthread_setcancelstate( cs, &r ); return r; } inline void test_cancel() { pthread_testcancel(); } class disable_cancelation { private: int cs_; public: disable_cancelation(): cs_( set_cancel_state( PTHREAD_CANCEL_DISABLE ) ) { } ~disable_cancelation() { set_cancel_state( cs_ ); } }; void sleep( timespec const & reltime ); inline void yield() { sched_yield(); } void yield( unsigned k ); } // namespace thread } // namespace std #endif // #ifndef THREAD_HPP_INCLUDED