//| Copyright: (C) 2020-2024 Kevin Larke //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file. #include "cwCommon.h" #include "cwLog.h" #include "cwCommonImpl.h" #include "cwTest.h" #include "cwMem.h" #include "cwMutex.h" #include "cwTime.h" #include namespace cw { namespace mutex { typedef struct mutex_str { pthread_mutex_t mutex; pthread_cond_t cvar; } mutex_t; mutex_t* _handleToPtr( handle_t h ) { return handleToPtr(h); } rc_t _destroy( mutex_t* p ) { rc_t rc = kOkRC; int sysRC; if((sysRC = pthread_cond_destroy(&p->cvar)) != 0) rc = cwLogSysError(kObjFreeFailRC,sysRC,"Thread condition var. destroy failed."); else if((sysRC = pthread_mutex_destroy(&p->mutex)) != 0) rc = cwLogSysError(kObjFreeFailRC,sysRC,"Thread mutex destroy failed."); else mem::release(p); return rc; } time::spec_t _future_ms(unsigned timeout_milliseconds) { time::spec_t ts; clock_gettime(CLOCK_REALTIME,&ts); time::advanceMs(ts,timeout_milliseconds); return ts; } } } cw::rc_t cw::mutex::create( handle_t& h ) { rc_t rc; if((rc = destroy(h)) != kOkRC ) return rc; mutex_t* p = mem::allocZ(); int sysRC; if((sysRC = pthread_mutex_init(&p->mutex,NULL)) != 0 ) cwLogSysError(kObjAllocFailRC,sysRC,"Thread mutex create failed."); else if((sysRC = pthread_cond_init(&p->cvar,NULL)) != 0 ) cwLogSysError(kObjAllocFailRC,sysRC,"Thread Condition var. create failed."); h.set(p); return rc; } cw::rc_t cw::mutex::destroy( handle_t& h ) { rc_t rc = kOkRC; if( !h.isValid() ) return rc; mutex_t* p = _handleToPtr(h); if((rc = _destroy(p)) != kOkRC ) return rc; h.clear(); return rc; } cw::rc_t cw::mutex::tryLock( handle_t h, bool& lockFlRef ) { rc_t rc = kOkRC; mutex_t* p = _handleToPtr(h); int sysRc = pthread_mutex_trylock(&p->mutex); switch(sysRc) { case EBUSY: lockFlRef = false; break; case 0: lockFlRef = true; break; default: rc = cwLogSysError(kInvalidOpRC,sysRc,"Thread mutex try-lock failed."); } return rc; } cw::rc_t cw::mutex::lock( handle_t h, unsigned timeout_milliseconds ) { rc_t rc = kOkRC; mutex_t* p = _handleToPtr(h); int sysRc; time::spec_t ts = _future_ms(timeout_milliseconds); // Apparently timedlock depends on using CLOCK_REALTIME vs CLOCK_MONOTONIC // so we can't call to time::current_time() which uses CLOCK_MONOTONIC. // TODO: See this: https://stackoverflow.com/questions/14248033/clock-monotonic-and-pthread-mutex-timedlock-pthread-cond-timedwait // and consider changing this to use CLOCK_MONOTONIC. //clock_gettime(CLOCK_REALTIME,&ts); //time::advanceMs(ts,timeout_milliseconds); switch(sysRc = pthread_mutex_timedlock(&p->mutex,&ts) ) { case 0: rc = kOkRC; break; case ETIMEDOUT: rc = kTimeOutRC; break; default: rc = cwLogSysError(kInvalidOpRC,sysRc,"Lock failed."); } return rc; } cw::rc_t cw::mutex::lock( handle_t h ) { rc_t rc = kOkRC; mutex_t* p = _handleToPtr(h); int sysRc; if((sysRc = pthread_mutex_lock(&p->mutex)) != 0) rc = cwLogSysError(kInvalidOpRC,sysRc,"Lock failed."); return rc; } cw::rc_t cw::mutex::unlock( handle_t h ) { rc_t rc = kOkRC; mutex_t* p = _handleToPtr(h); int sysRc; if((sysRc = pthread_mutex_unlock(&p->mutex)) != 0) return cwLogSysError(kInvalidOpRC,sysRc,"Unlock failed."); return rc; } cw::rc_t cw::mutex::waitOnCondVar( handle_t h, bool lockThenWaitFl, unsigned timeOutMs ) { rc_t rc = kOkRC; mutex_t* p = _handleToPtr(h); int sysRC; if( lockThenWaitFl ) if((sysRC = pthread_mutex_lock(&p->mutex)) != 0) return cwLogSysError(kInvalidOpRC,sysRC,"Lock failed."); // if no timeout was given .... if( timeOutMs == 0) { // ... then wait until the cond-var is triggered if((sysRC = pthread_cond_wait(&p->cvar,&p->mutex)) != 0 ) return cwLogSysError(kInvalidOpRC,sysRC,"Thread cond. var. wait failed."); } else // ... otherwise use the cond. var. wait with timeout API { struct timespec ts = _future_ms(timeOutMs); //if((rc = time::futureMs(ts,timeOutMs)) == kOkRC ) if((sysRC = pthread_cond_timedwait(&p->cvar,&p->mutex,&ts)) != 0 ) { if( sysRC == ETIMEDOUT ) rc = kTimeOutRC; else return cwLogSysError(kInvalidOpRC,sysRC,"Thread cond. var. wait failed."); } } return rc; } cw::rc_t cw::mutex::signalCondVar( handle_t h) { rc_t rc = kOkRC; mutex_t* p = _handleToPtr(h); int sysRC; if((sysRC = pthread_cond_signal(&p->cvar)) != 0 ) rc = cwLogSysError(kOpFailRC,sysRC,"Thread cond var. signaling failed."); return rc; }