// PThreadMutex.cxx #include "dataset_util/PThreadMutex.h" #include "dataset_util/FileStatus.h" #include #include #include #include using std::string; using std::ostream; using std::cerr; using std::endl; using std::setw; //********************************************************************** // Implementation class. //********************************************************************** // Local definitions. void msg(const PThreadMutex* pmtx, int lock_count, string txt) { if ( FileStatus("debug_PThreadMutex").exists() ) { cerr << "PM: " << setw(10) << pthread_self() << " mtx= " << setw(10) << pmtx << " #lock=" << lock_count << " " << txt << endl; } } //********************************************************************** // Implementation class. //********************************************************************** class PThreadMutex::Imp { public: pthread_mutex_t internal_mutex; pthread_mutex_t mtx; pthread_t owner; int err_no; int count; void internal_lock() { int stat = pthread_mutex_lock(&internal_mutex); assert( stat == 0 ); } void internal_unlock() { int stat = pthread_mutex_unlock(&internal_mutex); assert( stat == 0 ); } }; //********************************************************************** // Member functions. //********************************************************************** // Return the mutex. pthread_mutex_t& PThreadMutex::mutex() { return pimp->mtx; } //********************************************************************** // Constructor. PThreadMutex::PThreadMutex() : pimp(new Imp) { pimp->err_no = 0; pimp->owner = 0; pimp->count = 0; int stat = pthread_mutex_init(&pimp->internal_mutex, 0); if ( stat != 0 ) { pimp->err_no = errno; } stat = pthread_mutex_init(&pimp->mtx, 0); if ( stat != 0 ) { pimp->err_no = errno; } } //********************************************************************** // Destructor PThreadMutex::~PThreadMutex() { lock(); unlock(); pthread_mutex_destroy(&pimp->mtx); pthread_mutex_destroy(&pimp->internal_mutex); delete pimp; } //********************************************************************** // Lock. int PThreadMutex::lock() { pimp->internal_lock(); pthread_t mythread = pthread_self(); // If this thread has already locked, then increase the lock count // and return. if ( pimp->count>0 && mythread==pimp->owner ) { ++pimp->count; msg(this, pimp->count, " lock: count incremented"); pimp->internal_unlock(); return 0; } // Try to lock. int stat = pthread_mutex_trylock(&pimp->mtx); // If thread was already locked, then suspend internal lock and // wait for mutex to be available. bool busy = false; if ( stat == EBUSY ) { busy = true; msg(this, pimp->count, " lock: waiting..."); pimp->internal_unlock(); stat = pthread_mutex_lock(&pimp->mtx); pimp->internal_lock(); } // If lock was successful. if ( stat == 0 ) { pimp->owner = mythread; pimp->count = 1; } else { pimp->err_no = stat; assert(false); } if ( busy ) { msg(this, pimp->count, " lock: ...acquired"); } else { msg(this, pimp->count, " lock: acquired"); } pimp->internal_unlock(); return 0; } //********************************************************************** // Unlock. int PThreadMutex::unlock() { pimp->internal_lock(); pthread_t mythread = pthread_self(); int stat = 0; if ( pimp->count==0 || pimp->owner!=mythread ) { stat = EPERM; msg(this, pimp->count, "unlock: error 1"); } else { if ( pimp->count <= 0 ) { pimp->err_no = EINVAL; assert(false); pimp->count = 0; msg(this, pimp->count, "unlock: error 2"); } else { --pimp->count; if ( pimp->count ) { msg(this, pimp->count, "unlock: count decremented"); } else { msg(this, pimp->count, "unlock: released"); } } if ( pimp->count == 0 ) { stat = pthread_mutex_unlock(&pimp->mtx); if ( stat == 0 ) { pimp->owner = 0; } else { assert(false); } } } pimp->internal_unlock(); return stat; } //********************************************************************** // Validity. bool PThreadMutex::is_valid() const { return pimp->err_no==0 && pimp->count>=0; } //********************************************************************** // Error status. int PThreadMutex::error() const { pimp->internal_lock(); int err = pimp->err_no; pimp->internal_unlock(); return err; } //********************************************************************** // Lock status. bool PThreadMutex::is_locked() const { pimp->internal_lock(); bool stat = pimp->err_no==0 && pimp->count>0; pimp->internal_unlock(); return stat; } //********************************************************************** // Unlock status. bool PThreadMutex::is_unlocked() const { pimp->internal_lock(); bool stat = pimp->err_no==0 && pimp->count==0; pimp->internal_unlock(); return stat; } //********************************************************************** // Return the lock count. int PThreadMutex::lock_count() const { pimp->internal_lock(); int count = pimp->count; pimp->internal_unlock(); return count; } //********************************************************************** // Return the owner. pthread_t PThreadMutex::owner() const { pimp->internal_lock(); pthread_t owner = pimp->owner; pimp->internal_unlock(); return owner; } //********************************************************************** // Free functions. //********************************************************************** // Output stream. ostream& operator<<(ostream& lhs, const PThreadMutex& rhs) { lhs << "Mutex is "; if ( rhs.is_valid() ) { if ( rhs.is_unlocked() ) lhs << "un"; lhs << "locked"; if ( rhs.is_locked() ) { lhs << ": Owner " << rhs.owner() << " has " << rhs.lock_count() << " locks"; } } else { lhs << "invalid"; } return lhs; } //**********************************************************************