Loading src/rwlock.h +66 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,9 @@ #if defined(__GLIBC__) #include <pthread.h> #else #include <mutex> #include <condition_variable> #endif namespace netplus { Loading Loading @@ -74,8 +77,69 @@ private: }; #else // pthread_rwlockattr_setkind_np is a glibc extension, unavailable on // musl/BSD/Windows builds — fall back to the portable default there. using WriterPreferringSharedMutex = std::shared_mutex; // musl/BSD/Windows builds (musl's pthread_rwlock has no writer-preferring // mode at all). Same fairness fix as the glibc branch above, built instead // from std::mutex/std::condition_variable — the classic writer-preferring // readers-writers algorithm, portable to any platform with a C++11 // standard library. A waiting writer blocks new readers from acquiring // (lock_shared()'s wait predicate checks _waitingWriters), so a sustained // stream of readers can't starve it out; only readers already active when // the writer arrives are allowed to finish. class WriterPreferringSharedMutex { public: WriterPreferringSharedMutex() = default; WriterPreferringSharedMutex(const WriterPreferringSharedMutex&) = delete; WriterPreferringSharedMutex& operator=(const WriterPreferringSharedMutex&) = delete; void lock() { std::unique_lock<std::mutex> lk(_mtx); ++_waitingWriters; _cv.wait(lk, [this] { return !_writerActive && _activeReaders == 0; }); --_waitingWriters; _writerActive = true; } void unlock() { { std::lock_guard<std::mutex> lk(_mtx); _writerActive = false; } _cv.notify_all(); } bool try_lock() { std::lock_guard<std::mutex> lk(_mtx); if (_writerActive || _activeReaders > 0) return false; _writerActive = true; return true; } void lock_shared() { std::unique_lock<std::mutex> lk(_mtx); _cv.wait(lk, [this] { return !_writerActive && _waitingWriters == 0; }); ++_activeReaders; } void unlock_shared() { bool notify; { std::lock_guard<std::mutex> lk(_mtx); --_activeReaders; notify = (_activeReaders == 0); } if (notify) _cv.notify_all(); } bool try_lock_shared() { std::lock_guard<std::mutex> lk(_mtx); if (_writerActive || _waitingWriters > 0) return false; ++_activeReaders; return true; } private: std::mutex _mtx; std::condition_variable _cv; int _activeReaders = 0; int _waitingWriters = 0; bool _writerActive = false; }; #endif } // namespace netplus Loading
src/rwlock.h +66 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,9 @@ #if defined(__GLIBC__) #include <pthread.h> #else #include <mutex> #include <condition_variable> #endif namespace netplus { Loading Loading @@ -74,8 +77,69 @@ private: }; #else // pthread_rwlockattr_setkind_np is a glibc extension, unavailable on // musl/BSD/Windows builds — fall back to the portable default there. using WriterPreferringSharedMutex = std::shared_mutex; // musl/BSD/Windows builds (musl's pthread_rwlock has no writer-preferring // mode at all). Same fairness fix as the glibc branch above, built instead // from std::mutex/std::condition_variable — the classic writer-preferring // readers-writers algorithm, portable to any platform with a C++11 // standard library. A waiting writer blocks new readers from acquiring // (lock_shared()'s wait predicate checks _waitingWriters), so a sustained // stream of readers can't starve it out; only readers already active when // the writer arrives are allowed to finish. class WriterPreferringSharedMutex { public: WriterPreferringSharedMutex() = default; WriterPreferringSharedMutex(const WriterPreferringSharedMutex&) = delete; WriterPreferringSharedMutex& operator=(const WriterPreferringSharedMutex&) = delete; void lock() { std::unique_lock<std::mutex> lk(_mtx); ++_waitingWriters; _cv.wait(lk, [this] { return !_writerActive && _activeReaders == 0; }); --_waitingWriters; _writerActive = true; } void unlock() { { std::lock_guard<std::mutex> lk(_mtx); _writerActive = false; } _cv.notify_all(); } bool try_lock() { std::lock_guard<std::mutex> lk(_mtx); if (_writerActive || _activeReaders > 0) return false; _writerActive = true; return true; } void lock_shared() { std::unique_lock<std::mutex> lk(_mtx); _cv.wait(lk, [this] { return !_writerActive && _waitingWriters == 0; }); ++_activeReaders; } void unlock_shared() { bool notify; { std::lock_guard<std::mutex> lk(_mtx); --_activeReaders; notify = (_activeReaders == 0); } if (notify) _cv.notify_all(); } bool try_lock_shared() { std::lock_guard<std::mutex> lk(_mtx); if (_writerActive || _waitingWriters > 0) return false; ++_activeReaders; return true; } private: std::mutex _mtx; std::condition_variable _cv; int _activeReaders = 0; int _waitingWriters = 0; bool _writerActive = false; }; #endif } // namespace netplus