#include "rwlock.h" #include <util/generic/yexception.h> #if defined(_unix_) #include <errno.h> #include <pthread.h> #endif #if defined(_win_) || defined(_darwin_) #include "mutex.h" #include "condvar.h" //darwin rwlocks not recursive class TRWMutex::TImpl { public: TImpl(); ~TImpl(); void AcquireRead() noexcept; bool TryAcquireRead() noexcept; void ReleaseRead() noexcept; void AcquireWrite() noexcept; bool TryAcquireWrite() noexcept; void ReleaseWrite() noexcept; void Release() noexcept; private: TMutex Lock_; int State_; TCondVar ReadCond_; TCondVar WriteCond_; int BlockedWriters_; }; TRWMutex::TImpl::TImpl() : State_(0) , BlockedWriters_(0) { } TRWMutex::TImpl::~TImpl() { Y_ABORT_UNLESS(State_ == 0, "failure, State_ != 0"); Y_ABORT_UNLESS(BlockedWriters_ == 0, "failure, BlockedWriters_ != 0"); } void TRWMutex::TImpl::AcquireRead() noexcept { with_lock (Lock_) { while (BlockedWriters_ || State_ < 0) { ReadCond_.Wait(Lock_); } ++State_; } ReadCond_.Signal(); } bool TRWMutex::TImpl::TryAcquireRead() noexcept { with_lock (Lock_) { if (BlockedWriters_ || State_ < 0) { return false; } ++State_; } return true; } void TRWMutex::TImpl::ReleaseRead() noexcept { Lock_.Acquire(); if (--State_ > 0) { Lock_.Release(); } else if (BlockedWriters_) { Lock_.Release(); WriteCond_.Signal(); } else { Lock_.Release(); } } void TRWMutex::TImpl::AcquireWrite() noexcept { with_lock (Lock_) { while (State_ != 0) { ++BlockedWriters_; WriteCond_.Wait(Lock_); --BlockedWriters_; } State_ = -1; } } bool TRWMutex::TImpl::TryAcquireWrite() noexcept { with_lock (Lock_) { if (State_ != 0) { return false; } State_ = -1; } return true; } void TRWMutex::TImpl::ReleaseWrite() noexcept { Lock_.Acquire(); State_ = 0; if (BlockedWriters_) { Lock_.Release(); WriteCond_.Signal(); } else { Lock_.Release(); ReadCond_.Signal(); } } void TRWMutex::TImpl::Release() noexcept { Lock_.Acquire(); if (State_ > 0) { if (--State_ > 0) { Lock_.Release(); } else if (BlockedWriters_) { Lock_.Release(); WriteCond_.Signal(); } } else { State_ = 0; if (BlockedWriters_) { Lock_.Release(); WriteCond_.Signal(); } else { Lock_.Release(); ReadCond_.Signal(); } } } #else /* POSIX threads */ class TRWMutex::TImpl { public: TImpl(); ~TImpl(); void AcquireRead() noexcept; bool TryAcquireRead() noexcept; void ReleaseRead() noexcept; void AcquireWrite() noexcept; bool TryAcquireWrite() noexcept; void ReleaseWrite() noexcept; void Release() noexcept; private: pthread_rwlock_t Lock_; }; TRWMutex::TImpl::TImpl() { int result = pthread_rwlock_init(&Lock_, nullptr); if (result != 0) { ythrow yexception() << "rwlock init failed (" << LastSystemErrorText(result) << ")"; } } TRWMutex::TImpl::~TImpl() { const int result = pthread_rwlock_destroy(&Lock_); Y_ABORT_UNLESS(result == 0, "rwlock destroy failed (%s)", LastSystemErrorText(result)); } void TRWMutex::TImpl::AcquireRead() noexcept { const int result = pthread_rwlock_rdlock(&Lock_); Y_ABORT_UNLESS(result == 0, "rwlock rdlock failed (%s)", LastSystemErrorText(result)); } bool TRWMutex::TImpl::TryAcquireRead() noexcept { const int result = pthread_rwlock_tryrdlock(&Lock_); Y_ABORT_UNLESS(result == 0 || result == EBUSY, "rwlock tryrdlock failed (%s)", LastSystemErrorText(result)); return result == 0; } void TRWMutex::TImpl::ReleaseRead() noexcept { const int result = pthread_rwlock_unlock(&Lock_); Y_ABORT_UNLESS(result == 0, "rwlock (read) unlock failed (%s)", LastSystemErrorText(result)); } void TRWMutex::TImpl::AcquireWrite() noexcept { const int result = pthread_rwlock_wrlock(&Lock_); Y_ABORT_UNLESS(result == 0, "rwlock wrlock failed (%s)", LastSystemErrorText(result)); } bool TRWMutex::TImpl::TryAcquireWrite() noexcept { const int result = pthread_rwlock_trywrlock(&Lock_); Y_ABORT_UNLESS(result == 0 || result == EBUSY, "rwlock trywrlock failed (%s)", LastSystemErrorText(result)); return result == 0; } void TRWMutex::TImpl::ReleaseWrite() noexcept { const int result = pthread_rwlock_unlock(&Lock_); Y_ABORT_UNLESS(result == 0, "rwlock (write) unlock failed (%s)", LastSystemErrorText(result)); } void TRWMutex::TImpl::Release() noexcept { const int result = pthread_rwlock_unlock(&Lock_); Y_ABORT_UNLESS(result == 0, "rwlock unlock failed (%s)", LastSystemErrorText(result)); } #endif TRWMutex::TRWMutex() : Impl_(new TImpl()) { } TRWMutex::~TRWMutex() = default; void TRWMutex::AcquireRead() noexcept { Impl_->AcquireRead(); } bool TRWMutex::TryAcquireRead() noexcept { return Impl_->TryAcquireRead(); } void TRWMutex::ReleaseRead() noexcept { Impl_->ReleaseRead(); } void TRWMutex::AcquireWrite() noexcept { Impl_->AcquireWrite(); } bool TRWMutex::TryAcquireWrite() noexcept { return Impl_->TryAcquireWrite(); } void TRWMutex::ReleaseWrite() noexcept { Impl_->ReleaseWrite(); } void TRWMutex::Release() noexcept { Impl_->Release(); }