summaryrefslogtreecommitdiffstats
path: root/util/system/rwlock.cpp
diff options
context:
space:
mode:
authorDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /util/system/rwlock.cpp
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'util/system/rwlock.cpp')
-rw-r--r--util/system/rwlock.cpp250
1 files changed, 250 insertions, 0 deletions
diff --git a/util/system/rwlock.cpp b/util/system/rwlock.cpp
new file mode 100644
index 00000000000..bb3dcbf1881
--- /dev/null
+++ b/util/system/rwlock.cpp
@@ -0,0 +1,250 @@
+#include "rwlock.h"
+#include "mutex.h"
+#include "condvar.h"
+
+#include <util/generic/yexception.h>
+
+#if defined(_unix_)
+ #include <errno.h>
+ #include <pthread.h>
+#endif
+
+#if defined(_win_) || defined(_darwin_)
+//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_VERIFY(State_ == 0, "failure, State_ != 0");
+ Y_VERIFY(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_VERIFY(result == 0, "rwlock destroy failed (%s)", LastSystemErrorText(result));
+}
+
+void TRWMutex::TImpl::AcquireRead() noexcept {
+ const int result = pthread_rwlock_rdlock(&Lock_);
+ Y_VERIFY(result == 0, "rwlock rdlock failed (%s)", LastSystemErrorText(result));
+}
+
+bool TRWMutex::TImpl::TryAcquireRead() noexcept {
+ const int result = pthread_rwlock_tryrdlock(&Lock_);
+ Y_VERIFY(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_VERIFY(result == 0, "rwlock (read) unlock failed (%s)", LastSystemErrorText(result));
+}
+
+void TRWMutex::TImpl::AcquireWrite() noexcept {
+ const int result = pthread_rwlock_wrlock(&Lock_);
+ Y_VERIFY(result == 0, "rwlock wrlock failed (%s)", LastSystemErrorText(result));
+}
+
+bool TRWMutex::TImpl::TryAcquireWrite() noexcept {
+ const int result = pthread_rwlock_trywrlock(&Lock_);
+ Y_VERIFY(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_VERIFY(result == 0, "rwlock (write) unlock failed (%s)", LastSystemErrorText(result));
+}
+
+void TRWMutex::TImpl::Release() noexcept {
+ const int result = pthread_rwlock_unlock(&Lock_);
+ Y_VERIFY(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();
+}