/*** Copyright (C) 2021-2024 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuRWLock.hpp Date: 2021-6-12 Author: Reece ***/ #pragma once #include "AuConditionVariable.Generic.hpp" #include "AuConditionMutex.Generic.hpp" #include "ThreadCookie.hpp" #if defined(AURORA_IS_LINUX_DERIVED) // Disable Windows XP - 7 keyedevent inline optimization (not that we couldn't use AURORA_RWLOCK_IS_CONDLESS on XP/7 with WOA_ALWAYS_DUMB_OS_TARGET). // Note that *a lot* of platforms may end up requiring all thread primitives to use more verbose primitives. // Keeping the condvar impl around is best in the event we need to optimize for unique platforms of sensitive schedulers. // The noisey use-case of the semaphore hash table isn't what we always want for persistent objects with QoS properties. // In terms of platform support/what-to-do-where: // Here's one example of a QoS senitive OS without a futex wait queue interface we might care about: > MACOS < // Now let's ignore the updoooters and support Windows XP - 7... // ...this is a Linux specific thing // // Windows XP-7: same as 11 (no such win32 alternative) (EOL support) // Windows 11 updoooters can cope with a 56-byte primitive in the worst case scenario (who even cares case?) // Generic: 64-byte (no config, maybe use AURORA_RWLOCK_IS_CONDLESS) (expected case) // Linux: AURORA_RWLOCK_IS_CONDLESS best case, 64-byte without (best case) // AURORA_RWLOCK_IS_CONDLESS: 48-byte (best case) // vs couple hundred byte primitives in the STL // in either case AURORA_RWLOCK_IS_CONDLESS doesnt matter so much (delta linux:16, win32: measly 8 bytes) // in either case performance is the same #define AURORA_RWLOCK_IS_CONDLESS #endif namespace Aurora::Threading::Primitives { template struct RWLockImpl; template struct RWLockAccessView final : IWaitable { #if defined(RWLOCK_VIEW_HAS_PARENT) RWLockAccessView(T &impl) : parent_(impl) { } #endif bool LockMS(AuUInt64 timeout) override; bool LockNS(AuUInt64 timeout) override; bool LockAbsMS(AuUInt64 timeout) override; bool LockAbsNS(AuUInt64 timeout) override; bool TryLock() override; bool HasOSHandle(AuMach &mach) override { return false; } bool HasLockImplementation() override { return true; } void Lock() override { SysAssert(LockNS(0)); } void Unlock() override; private: #if defined(RWLOCK_VIEW_HAS_PARENT) T &parent_; #endif }; template struct RWLockImpl final : IRWLock { RWLockImpl(); ~RWLockImpl(); // i dont think i'll expose a high performance interface yet auline bool LockReadNSAbs(AuUInt64 timeout);// override; auline bool LockReadNS(AuUInt64 timeout);// override; auline bool LockWriteNS(AuUInt64 timeout);// override; auline bool LockWriteNSAbs(AuUInt64 timeout);// override; auline bool TryLockRead();// override; template auline bool TryLockReadNoSpin(); auline void UnlockRead();// override; auline void UnlockWrite();// override; auline void WriterWake(); auline bool WriterSleep(AuUInt64 qwTimeoutNS); auline bool WriterTryLock(); auline void FutexWriterWake(); auline bool TryLockWriteMaybeSpin();// override; auline bool TryLockWriteNoSpin();// override; auline bool LockWriteNSAbsUnlocked(AuUInt64 qwTimeoutNS);// override; bool UpgradeReadToWrite(AuUInt64 timeout) override; bool DowngradeWriteToRead() override; auline bool UpgradeReadToWriteDoUpgrade(); IWaitable *AsReadable() override; IWaitable *AsWritable() override; bool CheckSelfThreadIsWriter() override; auline ConditionVariableInternal &GetCondition(); auline ConditionVariableInternal &GetConditionWriter(); auline AuUInt32 *GetFutexCondition(); auline AuUInt32 *GetFutexConditionWriter(); auline void SignalOneReader(); auline void SignalOneWriter(); auline void SignalManyReader(); auline void SignalManyWriter(int iBias = 0); auline AuUInt32 *GetReadSleepCounter(); public: // "private" RWLockAccessView read_; RWLockAccessView write_; private: ThreadCookie_t reentrantWriteLockHandle_ {}; #if defined(AURORA_RWLOCK_IS_CONDLESS) AuAUInt32 uSemaphore_ {}; AuAUInt32 uRDCounter_ {}; #else ConditionMutexInternal mutex_; #if defined(AURWLOCK_NO_SIZE_OPTIMIZED_CONDVAR) ConditionVariableInternal condition_; ConditionVariableInternal conditionWriter_; #else char conditionVariable_[kSizeOfDummyCondVar] {}; char conditionVariableWriter_[kSizeOfDummyCondVar] {}; #endif #endif AuAInt32 iState_ {}; AuAInt32 dwWritersPending_ {}; }; }