/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// A RWLock is similar to a Mutex, but whereas a Mutex permits only a single // reader thread or a single writer thread to access a piece of data, a // RWLock distinguishes between readers and writers: you may have multiple // reader threads concurrently accessing a piece of data or a single writer // thread. This difference should guide your usage of RWLock: if you are not // reading the data from multiple threads simultaneously or you are writing // to the data roughly as often as read from it, then Mutex will suit your // purposes just fine. // // You should be using the AutoReadLock and AutoWriteLock classes, below, // for RAII read locking and write locking, respectively. If you really must // take a read lock manually, call the ReadLock method; to relinquish that // read lock, call the ReadUnlock method. Similarly, WriteLock and WriteUnlock // perform the same operations, but for write locks. // // It is unspecified what happens when a given thread attempts to acquire the // same lock in multiple ways; some underlying implementations of RWLock do // support acquiring a read lock multiple times on a given thread, but you // should not rely on this behavior. // // It is unspecified whether RWLock gives priority to waiting readers or // a waiting writer when unlocking. class MOZ_CAPABILITY("rwlock") RWLock : public detail::RWLockImpl, public BlockingResourceBase { public: explicit RWLock(constchar* aName);
#ifdef DEBUG // We record the owning thread for write locks only.
PRThread* mOwningThread; #endif
};
// We only use this once; not sure we can add thread safety attributions here template <typename T> class MOZ_RAII BaseAutoTryReadLock { public: explicit BaseAutoTryReadLock(T& aLock)
: mLock(aLock.TryReadLock() ? &aLock : nullptr) {}
~BaseAutoTryReadLock() { if (mLock) {
mLock->ReadUnlock();
}
}
// Not MOZ_RELEASE_SHARED(), which would make sense - apparently this trips // over a bug in clang's static analyzer and it says it expected an // exclusive unlock.
~BaseAutoReadLock() MOZ_RELEASE_GENERIC() { mLock->ReadUnlock(); }
// Read try-lock and unlock a RWLock with RAII semantics. Much preferred to // bare calls to TryReadLock() and ReadUnlock(). typedef BaseAutoTryReadLock<RWLock> AutoTryReadLock;
// Read lock and unlock a RWLock with RAII semantics. Much preferred to bare // calls to ReadLock() and ReadUnlock(). typedef BaseAutoReadLock<RWLock> AutoReadLock;
// Write try-lock and unlock a RWLock with RAII semantics. Much preferred to // bare calls to TryWriteLock() and WriteUnlock(). typedef BaseAutoTryWriteLock<RWLock> AutoTryWriteLock;
// Write lock and unlock a RWLock with RAII semantics. Much preferred to bare // calls to WriteLock() and WriteUnlock(). typedef BaseAutoWriteLock<RWLock> AutoWriteLock;
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS MOZ_CAPABILITY("rwlock")
StaticRWLock { public: // In debug builds, check that mLock is initialized for us as we expect by // the compiler. In non-debug builds, don't declare a constructor so that // the compiler can see that the constructor is trivial. #ifdef DEBUG
StaticRWLock() { MOZ_ASSERT(!mLock); } #endif
RWLock* lock = new RWLock("StaticRWLock"); if (!mLock.compareExchange(nullptr, lock)) { delete lock;
}
return mLock;
}
Atomic<RWLock*> mLock;
// Disallow copy constructor, but only in debug mode. We only define // a default constructor in debug mode (see above); if we declared // this constructor always, the compiler wouldn't generate a trivial // default constructor for us in non-debug mode. #ifdef DEBUG
StaticRWLock(const StaticRWLock& aOther); #endif