// 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" #if !defined( _POSIX_CXX09_EXTENSIONS ) #include "thread.hpp" #include "event.hpp" // atomics extern "C" long __cdecl _InterlockedIncrement( long volatile * ); extern "C" long __cdecl _InterlockedDecrement( long volatile * ); # pragma intrinsic( _InterlockedIncrement ) # pragma intrinsic( _InterlockedDecrement ) void atomic_increment( long * p ) { _InterlockedIncrement( p ); } long atomic_decrement( long * p ) { return _InterlockedDecrement( p ); } // pthread2_state struct pthread2_state { long refs_; //std::mutex mx_; //std::condition cn_; //bool ended_; event ended_; pthread_t handle_; void attach() { atomic_increment( &refs_ ); } void detach() { if( atomic_decrement( &refs_ ) == 0 ) { delete this; } } void on_thread_end() { //{ // std::scoped_lock lock( mx_ ); // ended_ = true; //} //cn_.broadcast(); ended_.set(); } pthread2_state(): refs_( 0 ), handle_() // , ended_( false ) { } virtual ~pthread2_state() { } int join2() { //std::scoped_lock lock( mx_ ); //while( !ended_ ) //{ // int r = cn_.wait( lock ); // if( r != 0 ) return r; //} //return 0; return ended_.wait(); } int tryjoin2() { //std::scoped_lock lock( mx_ ); //return ended_? 0: EBUSY; return ended_.try_wait(); } int timedjoin2( timespec const * abstime ) { //std::scoped_lock lock( mx_ ); //while( !ended_ ) //{ // int r = cn_.timed_wait( lock, abstime ); // if( r != 0 ) return r; //} //return 0; if( abstime == 0 ) return EINVAL; return ended_.timed_wait( *abstime ); } int cancel() { std::basic_lock< event > lock( ended_ ); if( !ended_.unlocked_is_set() ) { return pthread_cancel( handle_ ); } else { return 0; } //std::scoped_lock lock( mx_ ); //if( !ended_ ) //{ // return pthread_cancel( handle_ ); //} //else //{ // return 0; //} } }; int pthread_attach_np( pthread2_t thread ) { if( thread ) { thread->attach(); } return 0; } int pthread_detach( pthread2_t thread ) { if( thread ) { thread->detach(); } return 0; } static pthread_key_t s_tsp; static pthread_once_t s_tsp_once = PTHREAD_ONCE_INIT; extern "C" static void tsp_destroy( void * pv ) { pthread2_state * ps = static_cast< pthread2_state * >( pv ); if( ps != 0 ) { ps->on_thread_end(); ps->detach(); } } extern "C" static void tsp_init() { pthread_key_create( &s_tsp, tsp_destroy ); } static void set_tsp( pthread2_state * ps ) { pthread_once( &s_tsp_once, tsp_init ); pthread_setspecific( s_tsp, ps ); } static pthread2_state * get_tsp() { pthread_once( &s_tsp_once, tsp_init ); return static_cast< pthread2_state * >( pthread_getspecific( s_tsp ) ); } struct thp_param { pthread2_state * ps_; void * (*pf_) ( void * ); void * arg_; }; extern "C" void * threadproc( void * pv ) { std::auto_ptr ptp( static_cast< thp_param * >( pv ) ); set_tsp( ptp->ps_ ); ptp->ps_->handle_ = pthread_self(); // race here, hopefully harmless return ptp->pf_( ptp->arg_ ); } int pthread_create( pthread2_t * thread, const pthread_attr_t * attr, void *(*start_routine)(void *), void * arg ) { int ds = PTHREAD_CREATE_JOINABLE; int r; if( attr != 0 ) { r = pthread_attr_getdetachstate( attr, &ds ); if( r != 0 ) return r; } std::auto_ptr ps( new pthread2_state ); if( ds == PTHREAD_CREATE_JOINABLE ) { ++ps->refs_; } std::auto_ptr ptp( new thp_param ); ptp->ps_ = ps.get(); ptp->pf_ = start_routine; ptp->arg_ = arg; ++ps->refs_; // for s_tsp, to avoid the race condition where the caller manages to detach r = pthread_create( &ps->handle_, 0, threadproc, ptp.get() ); if( r == 0 ) { if( ds == PTHREAD_CREATE_JOINABLE ) { pthread_detach( ps->handle_ ); } *thread = ps.release(); ptp.release(); } return r; } pthread2_t __pthread_self() { pthread2_state * ps = get_tsp(); if( ps == 0 ) { // we're being called from a foreign thread, adopt it ps = new pthread2_state; ps->handle_ = pthread_self(); set_tsp( ps ); ps->attach(); } return ps; } int pthread_cancel( pthread2_t thread ) { return thread? thread->cancel(): 0; } int pthread_join2_np( pthread2_t thread ) { return thread? thread->join2(): 0; } int pthread_tryjoin2_np( pthread2_t thread ) { return thread? thread->tryjoin2(): 0; } int pthread_timedjoin2_np( pthread2_t thread, const struct timespec * abstime ) { return thread? thread->timedjoin2( abstime ): 0; } static pthread_key_t s_once2_key; static pthread_once_t s_once2_ctrl = PTHREAD_ONCE_INIT; extern "C" static void once2_init() { pthread_key_create( &s_once2_key, 0 ); } struct once2_params { void (*init_routine)( void * ); void * arg; }; extern "C" static void once2_execute() { void * pv = pthread_getspecific( s_once2_key ); once2_params * pp2 = static_cast< once2_params * >( pv ); pp2->init_routine( pp2->arg ); } int pthread_once2_np( pthread_once_t * once_control, void (*init_routine)(void*), void * arg ) { int r = pthread_once( &s_once2_ctrl, &once2_init ); if( r != 0 ) return r; once2_params p2 = { init_routine, arg }; r = pthread_setspecific( s_once2_key, &p2 ); if( r != 0 ) return r; return pthread_once( once_control, once2_execute ); } #define ATTR_COPY( prefix, Tp, name ) \ Tp v_##name; \ r = pthread_##prefix##attr_get##name( source, &v_##name ); \ if( r != 0 ) return r; \ r = pthread_##prefix##attr_set##name( target, v_##name ); \ if( r != 0 ) return r; int pthread_attr_copy_np( pthread_attr_t * target, pthread_attr_t const * source ) { int r; ATTR_COPY( , int, detachstate ) ATTR_COPY( , size_t, stacksize ) ATTR_COPY( , size_t, guardsize ) // ATTR_COPY( , struct sched_param, schedparam ) struct sched_param v_schedparam; r = pthread_attr_getschedparam( source, &v_schedparam ); if( r != 0 ) return r; r = pthread_attr_setschedparam( target, &v_schedparam ); if( r != 0 ) return r; //ATTR_COPY( , int, inheritsched ) //ATTR_COPY( , int, schedpolicy ) //ATTR_COPY( , int, scope ) return 0; } int pthread_mutexattr_copy_np( pthread_mutexattr_t * target, pthread_mutexattr_t const * source ) { int r; ATTR_COPY( mutex, int, type ) ATTR_COPY( mutex, int, pshared ) // ATTR_COPY( mutex, int, prioceiling ) // ATTR_COPY( mutex, int, protocol ) return 0; } int pthread_condattr_copy_np( pthread_condattr_t * target, pthread_condattr_t const * source ) { int r; ATTR_COPY( cond, int, pshared ) ATTR_COPY( cond, int, clock ) return 0; } int pthread_rwlockattr_copy_np( pthread_rwlockattr_t * target, pthread_rwlockattr_t const * source ) { int r; ATTR_COPY( rwlock, int, pshared ) return 0; } #endif // !defined( _POSIX_CXX09_EXTENSIONS ) #ifdef PTW32_VERSION int clock_gettime( clockid_t clock_id, struct timespec * tp ); int nanosleep( const struct timespec * rqtp, struct timespec * rmtp ); int pthread_attr_getguardsize( const pthread_attr_t * attr, size_t * guardsize ) { *guardsize = 4096; return 0; } int pthread_attr_setguardsize( pthread_attr_t * attr, size_t guardsize ) { return guardsize <= 4096? 0: ENOTSUP; } int pthread_condattr_getclock( const pthread_condattr_t * attr, clockid_t * clock_id ) { *clock_id = CLOCK_REALTIME; return 0; } int pthread_condattr_setclock( pthread_condattr_t * attr, clockid_t clock_id ) { return clock_id == CLOCK_REALTIME? 0: ENOTSUP; } #endif // PTW32_VERSION