2019-12-19 03:24:12 +00:00
# include "cwCommon.h"
# include "cwLog.h"
# include "cwCommonImpl.h"
# include "cwMem.h"
# include "cwThread.h"
# include <pthread.h>
namespace cw
{
2019-12-24 15:05:24 +00:00
namespace thread
2019-12-19 03:24:12 +00:00
{
2019-12-24 15:05:24 +00:00
enum
2019-12-19 03:24:12 +00:00
{
2019-12-24 15:05:24 +00:00
kDoExitThFl = 0x01 ,
kDoPauseThFl = 0x02 ,
kDoRunThFl = 0x04
} ;
2019-12-19 03:24:12 +00:00
2019-12-24 15:05:24 +00:00
typedef struct thread_str
{
pthread_t pThreadH ;
2020-04-19 17:01:47 +00:00
std : : atomic < stateId_t > stateId ;
std : : atomic < unsigned > doFlags ;
2019-12-24 15:05:24 +00:00
cbFunc_t func ;
void * funcArg ;
unsigned stateMicros ;
unsigned pauseMicros ;
2020-12-11 20:57:35 +00:00
unsigned sleepMicros ;
2020-02-12 18:26:18 +00:00
pthread_attr_t attr ;
2024-02-18 13:37:33 +00:00
char * label ;
2020-02-12 18:26:18 +00:00
2019-12-24 15:05:24 +00:00
} thread_t ;
inline thread_t * _handleToPtr ( handle_t h ) { return handleToPtr < handle_t , thread_t > ( h ) ; }
2020-04-19 17:01:47 +00:00
// Called from client thread to wait for the internal thread to transition to a specified state.
2024-02-14 16:30:02 +00:00
rc_t _waitForState ( thread_t * p , stateId_t stateId )
2019-12-24 15:05:24 +00:00
{
unsigned waitTimeMicroSecs = 0 ;
2020-04-19 17:01:47 +00:00
stateId_t curStateId ;
do
2019-12-24 15:05:24 +00:00
{
2020-04-19 17:01:47 +00:00
curStateId = p - > stateId . load ( std : : memory_order_acquire ) ;
if ( curStateId = = stateId )
break ;
2019-12-24 15:05:24 +00:00
sleepUs ( p - > sleepMicros ) ;
2020-12-11 20:57:35 +00:00
waitTimeMicroSecs + = p - > sleepMicros ;
2019-12-19 03:24:12 +00:00
2020-04-19 17:01:47 +00:00
} while ( waitTimeMicroSecs < p - > stateMicros ) ;
2024-02-14 16:30:02 +00:00
2020-04-19 17:01:47 +00:00
return curStateId = = stateId ? kOkRC : kTimeOutRC ;
2019-12-24 15:05:24 +00:00
}
2019-12-19 03:24:12 +00:00
2019-12-24 15:05:24 +00:00
void _threadCleanUpCallback ( void * p )
2019-12-19 03:24:12 +00:00
{
2020-04-19 17:01:47 +00:00
( ( thread_t * ) p ) - > stateId . store ( kExitedThId , std : : memory_order_release ) ;
2019-12-24 15:05:24 +00:00
}
2019-12-19 03:24:12 +00:00
2019-12-24 15:05:24 +00:00
void * _threadCallback ( void * param )
{
thread_t * p = ( thread_t * ) param ;
// set a clean up handler - this will be called when the
// thread terminates unexpectedly or pthread_cleanup_pop() is called.
pthread_cleanup_push ( _threadCleanUpCallback , p ) ;
2020-04-19 17:01:47 +00:00
unsigned curDoFlags = 0 ;
do
2019-12-19 03:24:12 +00:00
{
2020-04-19 17:01:47 +00:00
// get the current thread state (running or paused)
stateId_t curStateId = p - > stateId . load ( std : : memory_order_relaxed ) ;
2019-12-19 03:24:12 +00:00
2019-12-24 15:05:24 +00:00
// if we are in the pause state
2020-04-19 17:01:47 +00:00
if ( curStateId = = kPausedThId )
2019-12-19 03:24:12 +00:00
{
2019-12-24 15:05:24 +00:00
sleepUs ( p - > pauseMicros ) ;
2019-12-19 03:24:12 +00:00
2020-04-19 17:01:47 +00:00
curDoFlags = p - > doFlags . load ( std : : memory_order_acquire ) ;
2019-12-24 15:05:24 +00:00
// check if we have been requested to leave the pause state
2020-04-19 17:01:47 +00:00
if ( cwIsFlag ( curDoFlags , kDoRunThFl ) )
2019-12-24 15:05:24 +00:00
{
2020-04-19 17:01:47 +00:00
p - > stateId . store ( kRunningThId , std : : memory_order_release ) ;
2019-12-24 15:05:24 +00:00
}
}
2020-04-19 17:01:47 +00:00
else // ... we are in running state
2019-12-19 03:24:12 +00:00
{
2019-12-24 15:05:24 +00:00
// call the user-defined function
if ( p - > func ( p - > funcArg ) = = false )
break ;
2020-04-19 17:01:47 +00:00
curDoFlags = p - > doFlags . load ( std : : memory_order_acquire ) ;
2019-12-24 15:05:24 +00:00
// check if we have been requested to enter the pause state
2020-04-19 17:01:47 +00:00
if ( cwIsFlag ( curDoFlags , kDoPauseThFl ) )
2019-12-24 15:05:24 +00:00
{
2020-04-19 17:01:47 +00:00
p - > stateId . store ( kPausedThId , std : : memory_order_release ) ;
2019-12-24 15:05:24 +00:00
}
2019-12-19 03:24:12 +00:00
}
2020-04-19 17:01:47 +00:00
} while ( cwIsFlag ( curDoFlags , kDoExitThFl ) = = false ) ;
2019-12-19 03:24:12 +00:00
2019-12-24 15:05:24 +00:00
pthread_cleanup_pop ( 1 ) ;
2019-12-19 03:24:12 +00:00
2019-12-24 15:05:24 +00:00
pthread_exit ( NULL ) ;
2019-12-19 03:24:12 +00:00
2019-12-24 15:05:24 +00:00
return p ;
}
}
2019-12-19 03:24:12 +00:00
}
2024-02-18 13:37:33 +00:00
cw : : rc_t cw : : thread : : create ( handle_t & hRef , cbFunc_t func , void * funcArg , const char * label , int stateMicros , int pauseMicros )
2019-12-19 03:24:12 +00:00
{
rc_t rc ;
int sysRC ;
2019-12-24 15:05:24 +00:00
if ( ( rc = destroy ( hRef ) ) ! = kOkRC )
2019-12-19 03:24:12 +00:00
return rc ;
2019-12-28 02:51:28 +00:00
thread_t * p = mem : : allocZ < thread_t > ( ) ;
2019-12-19 03:24:12 +00:00
p - > func = func ;
p - > funcArg = funcArg ;
p - > stateMicros = stateMicros ;
p - > pauseMicros = pauseMicros ;
p - > stateId = kPausedThId ;
2020-12-11 20:57:35 +00:00
p - > sleepMicros = 15000 ;
2024-02-18 13:37:33 +00:00
p - > label = mem : : duplStr ( label ) ;
2019-12-24 15:05:24 +00:00
2020-02-12 18:26:18 +00:00
if ( ( sysRC = pthread_attr_init ( & p - > attr ) ) ! = 0 )
2019-12-19 03:24:12 +00:00
{
p - > stateId = kNotInitThId ;
2019-12-24 15:05:24 +00:00
rc = cwLogSysError ( kOpFailRC , sysRC , " Thread attribute init failed. " ) ;
2019-12-19 03:24:12 +00:00
}
2019-12-24 15:05:24 +00:00
else
2024-02-14 16:30:02 +00:00
{
2020-03-18 23:05:48 +00:00
/*
// Creating the thread in a detached state should prevent it from leaking memory when
// the thread is closed and pthread_join() is not called but it doesn't seem to work anymore ????
2020-02-12 18:26:18 +00:00
if ( ( sysRC = pthread_attr_setdetachstate ( & p - > attr , PTHREAD_CREATE_DETACHED ) ) ! = 0 )
2019-12-24 15:05:24 +00:00
{
p - > stateId = kNotInitThId ;
rc = cwLogSysError ( kOpFailRC , sysRC , " Thread set detach attribute failed. " ) ;
}
else
2020-03-18 23:05:48 +00:00
*/
2020-02-12 18:26:18 +00:00
if ( ( sysRC = pthread_create ( & p - > pThreadH , & p - > attr , _threadCallback , ( void * ) p ) ) ! = 0 )
2019-12-24 15:05:24 +00:00
{
p - > stateId = kNotInitThId ;
rc = cwLogSysError ( kOpFailRC , sysRC , " Thread create failed. " ) ;
}
2024-02-14 16:30:02 +00:00
}
2024-02-18 13:37:33 +00:00
if ( label ! = nullptr )
pthread_setname_np ( p - > pThreadH , label ) ;
2019-12-19 03:24:12 +00:00
hRef . set ( p ) ;
2024-02-18 13:37:33 +00:00
cwLogInfo ( " Thread %s id:%p created. " , cwStringNullGuard ( label ) , p - > pThreadH ) ;
2019-12-19 03:24:12 +00:00
return rc ;
}
2019-12-24 15:05:24 +00:00
cw : : rc_t cw : : thread : : destroy ( handle_t & hRef )
2019-12-19 03:24:12 +00:00
{
rc_t rc = kOkRC ;
2020-03-18 23:05:48 +00:00
int sysRC ;
2019-12-19 03:24:12 +00:00
if ( ! hRef . isValid ( ) )
return rc ;
2019-12-24 15:05:24 +00:00
thread_t * p = _handleToPtr ( hRef ) ;
2019-12-19 03:24:12 +00:00
// tell the thread to exit
2020-04-19 17:01:47 +00:00
p - > doFlags . store ( kDoExitThFl , std : : memory_order_release ) ;
2019-12-19 03:24:12 +00:00
// wait for the thread to exit and then deallocate the thread object
if ( ( rc = _waitForState ( p , kExitedThId ) ) ! = kOkRC )
2024-02-18 13:37:33 +00:00
return cwLogError ( rc , " Thread '%s' timed out waiting for destroy. " , p - > label ) ;
2019-12-19 03:24:12 +00:00
2020-03-18 23:05:48 +00:00
// Block until the thread is actually fully cleaned up
if ( ( sysRC = pthread_join ( p - > pThreadH , NULL ) ) ! = 0 )
2024-02-18 13:37:33 +00:00
rc = cwLogSysError ( kOpFailRC , sysRC , " Thread '%s' join failed. " , p - > label ) ;
2020-03-18 23:05:48 +00:00
//if( pthread_attr_destroy(&p->attr) != 0 )
// rc = cwLogError(kOpFailRC,"Thread attribute destroy failed.");
2020-02-12 18:26:18 +00:00
2024-02-18 13:37:33 +00:00
mem : : release ( p - > label ) ;
2019-12-28 02:51:28 +00:00
mem : : release ( p ) ;
2019-12-24 15:05:24 +00:00
hRef . clear ( ) ;
2019-12-19 03:24:12 +00:00
return rc ;
}
2019-12-24 15:05:24 +00:00
cw : : rc_t cw : : thread : : pause ( handle_t h , unsigned cmdFlags )
2019-12-19 03:24:12 +00:00
{
rc_t rc = kOkRC ;
2019-12-24 15:05:24 +00:00
bool pauseFl = cwIsFlag ( cmdFlags , kPauseFl ) ;
bool waitFl = cwIsFlag ( cmdFlags , kWaitFl ) ;
thread_t * p = _handleToPtr ( h ) ;
2020-04-19 17:01:47 +00:00
stateId_t curStateId = p - > stateId . load ( std : : memory_order_acquire ) ;
bool isPausedFl = curStateId = = kPausedThId ;
2024-02-14 16:30:02 +00:00
stateId_t waitId ;
2019-12-19 03:24:12 +00:00
if ( isPausedFl = = pauseFl )
return kOkRC ;
if ( pauseFl )
{
2020-04-19 17:01:47 +00:00
p - > doFlags . store ( kDoPauseThFl , std : : memory_order_release ) ;
2019-12-19 03:24:12 +00:00
waitId = kPausedThId ;
}
else
{
2020-04-19 17:01:47 +00:00
p - > doFlags . store ( kDoRunThFl , std : : memory_order_release ) ;
2019-12-19 03:24:12 +00:00
waitId = kRunningThId ;
}
if ( waitFl )
rc = _waitForState ( p , waitId ) ;
if ( rc ! = kOkRC )
2024-02-18 13:37:33 +00:00
cwLogError ( rc , " Thread '%s' timed out waiting for '%s'. pauseMicros:%i stateMicros:%i sleepMicros:%i " , p - > label , pauseFl ? " pause " : " un-pause " , p - > pauseMicros , p - > stateMicros , p - > sleepMicros ) ;
2019-12-19 03:24:12 +00:00
return rc ;
}
2019-12-24 15:05:24 +00:00
cw : : rc_t cw : : thread : : unpause ( handle_t h )
{ return pause ( h , kWaitFl ) ; }
cw : : thread : : stateId_t cw : : thread : : state ( handle_t h )
2019-12-19 03:24:12 +00:00
{
2019-12-24 15:05:24 +00:00
thread_t * p = _handleToPtr ( h ) ;
2020-04-19 17:01:47 +00:00
return p - > stateId . load ( std : : memory_order_acquire ) ;
2019-12-19 03:24:12 +00:00
}
2020-01-27 22:50:57 +00:00
cw : : thread : : thread_id_t cw : : thread : : id ( )
{
typedef struct
{
union
{
thread_id_t id ;
pthread_t pthread_id ;
} u ;
} id_t ;
id_t id ;
id . u . pthread_id = pthread_self ( ) ;
return id . u . id ;
}
2019-12-19 03:24:12 +00:00
2024-02-18 13:37:33 +00:00
const char * cw : : thread : : label ( handle_t h )
{
thread_t * p = _handleToPtr ( h ) ;
return p - > label = = nullptr ? " <no_thread_label> " : p - > label ;
}
2024-02-14 16:30:02 +00:00
unsigned cw : : thread : : stateTimeOutMicros ( handle_t h )
{
thread_t * p = _handleToPtr ( h ) ;
return p - > stateMicros ;
}
unsigned cw : : thread : : pauseMicros ( handle_t h )
{
thread_t * p = _handleToPtr ( h ) ;
return p - > pauseMicros ;
}
2019-12-19 03:24:12 +00:00
namespace cw
{
bool _threadTestCb ( void * p )
{
unsigned * ip = ( unsigned * ) p ;
ip [ 0 ] + + ;
return true ;
}
}
cw : : rc_t cw : : threadTest ( )
{
2019-12-24 15:05:24 +00:00
thread : : handle_t h ;
unsigned val = 0 ;
rc_t rc ;
char c = 0 ;
2019-12-19 03:24:12 +00:00
2024-02-18 13:37:33 +00:00
if ( ( rc = thread : : create ( h , _threadTestCb , & val , " thread_test " ) ) ! = kOkRC )
2019-12-19 03:24:12 +00:00
return rc ;
2019-12-24 15:05:24 +00:00
if ( ( rc = thread : : pause ( h , 0 ) ) ! = kOkRC )
2019-12-19 03:24:12 +00:00
goto errLabel ;
cwLogInfo ( " o=print p=pause s=state q=quit \n " ) ;
while ( c ! = ' q ' )
{
c = ( char ) fgetc ( stdin ) ;
fflush ( stdin ) ;
switch ( c )
{
case ' o ' :
cwLogInfo ( " val: 0x%x \n " , val ) ;
break ;
case ' s ' :
2019-12-24 15:05:24 +00:00
cwLogInfo ( " state=%i \n " , thread : : state ( h ) ) ;
2019-12-19 03:24:12 +00:00
break ;
case ' p ' :
{
2019-12-24 15:05:24 +00:00
if ( thread : : state ( h ) = = thread : : kPausedThId )
rc = thread : : pause ( h , thread : : kWaitFl ) ;
2019-12-19 03:24:12 +00:00
else
2019-12-24 15:05:24 +00:00
rc = thread : : pause ( h , thread : : kPauseFl | thread : : kWaitFl ) ;
2019-12-19 03:24:12 +00:00
if ( rc = = kOkRC )
2019-12-24 15:05:24 +00:00
cwLogInfo ( " new state:%i \n " , thread : : state ( h ) ) ;
2019-12-19 03:24:12 +00:00
else
{
cwLogError ( rc , " threadPause() test failed. " ) ;
goto errLabel ;
}
}
break ;
case ' q ' :
break ;
//default:
//cwLogInfo("Unknown:%c\n",c);
}
}
errLabel :
2019-12-24 15:05:24 +00:00
rc_t rc0 = rc = thread : : destroy ( h ) ;
2019-12-19 03:24:12 +00:00
return rc = = kOkRC ? rc0 : rc ;
}