diff --git a/clang/docs/ThreadSafetyAnalysis.rst b/clang/docs/ThreadSafetyAnalysis.rst --- a/clang/docs/ThreadSafetyAnalysis.rst +++ b/clang/docs/ThreadSafetyAnalysis.rst @@ -402,6 +402,13 @@ and destructor refer to the capability via different names; see the ``MutexLocker`` class in :ref:`mutexheader`, below. +Scoped capabilities are treated as capabilities that are implicitly acquired +on construction and released on destruction. They are associated with +the set of (regular) capabilities named in thread safety attributes on the +constructor. Acquire-type attributes on other member functions are treated as +applying to that set of associated capabilities, while ``RELEASE`` implies that +a function releases all associated capabilities in whatever mode they're held. + TRY_ACQUIRE(, ...), TRY_ACQUIRE_SHARED(, ...) --------------------------------------------------------- @@ -886,19 +893,78 @@ const Mutex& operator!() const { return *this; } }; + // Tag types for selecting a constructor. + struct adopt_lock_t {} inline constexpr adopt_lock = {}; + struct defer_lock_t {} inline constexpr defer_lock = {}; + struct shared_lock_t {} inline constexpr shared_lock = {}; // MutexLocker is an RAII class that acquires a mutex in its constructor, and // releases it in its destructor. class SCOPED_CAPABILITY MutexLocker { private: Mutex* mut; + bool locked; public: - MutexLocker(Mutex *mu) ACQUIRE(mu) : mut(mu) { + // Acquire mu, implicitly acquire *this and associate it with mu. + MutexLocker(Mutex *mu) ACQUIRE(mu) : mut(mu), locked(true) { mu->Lock(); } + + // Assume mu is held, implicitly acquire *this and associate it with mu. + MutexLocker(Mutex *mu, adopt_lock_t) REQUIRES(mu) : mut(mu), locked(true) {} + + // Acquire mu in shared mode, implicitly acquire *this and associate it with mu. + MutexLocker(Mutex *mu, shared_lock_t) ACQUIRE_SHARED(mu) : mut(mu), locked(true) { + mu->ReaderLock(); + } + + // Assume mu is held in shared mode, implicitly acquire *this and associate it with mu. + MutexLocker(Mutex *mu, adopt_lock_t, shared_lock_t) REQUIRES_SHARED(mu) + : mut(mu), locked(true) {} + + // Assume mu is not held, implicitly acquire *this and associate it with mu. + MutexLocker(Mutex *mu, defer_lock_t) EXCLUDES(mu) : mut(mu), locked(false) {} + + // Release *this and all associated mutexes, if they are still held. + // There is no warning if the scope was already unlocked before. ~MutexLocker() RELEASE() { + if (locked) + mut->GenericUnlock(); + } + + // Acquire all associated mutexes exclusively. + void Lock() ACQUIRE() { + mut->Lock(); + locked = true; + } + + // Try to acquire all associated mutexes exclusively. + bool TryLock() TRY_ACQUIRE(true) { + return locked = mut->TryLock(); + } + + // Acquire all associated mutexes in shared mode. + void ReaderLock() ACQUIRE_SHARED() { + mut->ReaderLock(); + locked = true; + } + + // Try to acquire all associated mutexes in shared mode. + bool ReaderTryLock() TRY_ACQUIRE_SHARED(true) { + return locked = mut->ReaderTryLock(); + } + + // Release all associated mutexes. Warn on double unlock. + void Unlock() RELEASE() { mut->Unlock(); + locked = false; + } + + // Release all associated mutexes. Warn on double unlock. + void ReaderUnlock() RELEASE() { + mut->ReaderUnlock(); + locked = false; } };