+
+ /*
+ * Note: C++ 17 adds guaranteed copy elision. (http://wg21.link/P0135)
+ * Once compilers support this, it would be nice to add wguard() and rguard()
+ * methods that return LockedGuardPtr objects.
+ */
+
+ /**
+ * Invoke a function while holding the lock exclusively.
+ *
+ * A reference to the datum will be passed into the function as its only
+ * argument.
+ *
+ * This can be used with a lambda argument for easily defining small critical
+ * sections in the code. For example:
+ *
+ * auto value = obj.withWLock([](auto& data) {
+ * data.doStuff();
+ * return data.getValue();
+ * });
+ */
+ template <class Function>
+ auto withWLock(Function&& function) {
+ LockedGuardPtr<Subclass, LockPolicyExclusive> guardPtr(
+ static_cast<Subclass*>(this));
+ return function(*guardPtr);
+ }
+ template <class Function>
+ auto withWLock(Function&& function) const {
+ LockedGuardPtr<const Subclass, LockPolicyExclusive> guardPtr(
+ static_cast<const Subclass*>(this));
+ return function(*guardPtr);
+ }
+
+ /**
+ * Invoke a function while holding the lock exclusively.
+ *
+ * This is similar to withWLock(), but the function will be passed a
+ * LockedPtr rather than a reference to the data itself.
+ *
+ * This allows scopedUnlock() to be called on the LockedPtr argument if
+ * desired.
+ */
+ template <class Function>
+ auto withWLockPtr(Function&& function) {
+ return function(wlock());
+ }
+ template <class Function>
+ auto withWLockPtr(Function&& function) const {
+ return function(wlock());
+ }
+
+ /**
+ * Invoke a function while holding an the lock in shared mode.
+ *
+ * A const reference to the datum will be passed into the function as its
+ * only argument.
+ */
+ template <class Function>
+ auto withRLock(Function&& function) const {
+ LockedGuardPtr<const Subclass, LockPolicyShared> guardPtr(
+ static_cast<const Subclass*>(this));
+ return function(*guardPtr);
+ }
+
+ template <class Function>
+ auto withRLockPtr(Function&& function) const {
+ return function(rlock());
+ }
+};
+
+/**
+ * SynchronizedBase specialization for upgrade mutex types.
+ *
+ * This class provides all the functionality provided by the SynchronizedBase
+ * specialization for shared mutexes and a ulock() method that returns an
+ * upgradable lock RAII proxy
+ */
+template <class Subclass>
+class SynchronizedBase<Subclass, detail::MutexLevel::UPGRADE>
+ : public SynchronizedBase<Subclass, detail::MutexLevel::SHARED> {
+ public:
+ using UpgradeLockedPtr = ::folly::LockedPtr<Subclass, LockPolicyUpgrade>;
+ using ConstUpgradeLockedPtr =
+ ::folly::LockedPtr<const Subclass, LockPolicyUpgrade>;
+ using UpgradeLockedGuardPtr =
+ ::folly::LockedGuardPtr<Subclass, LockPolicyUpgrade>;
+ using ConstUpgradeLockedGuardPtr =
+ ::folly::LockedGuardPtr<const Subclass, LockPolicyUpgrade>;
+
+ /**
+ * Acquire an upgrade lock and return a LockedPtr that can be used to safely
+ * access the datum
+ *
+ * And the const version
+ */
+ UpgradeLockedPtr ulock() {
+ return UpgradeLockedPtr(static_cast<Subclass*>(this));
+ }
+ ConstUpgradeLockedPtr ulock() const {
+ return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this));
+ }
+
+ /**
+ * Acquire an upgrade lock and return a LockedPtr that can be used to safely
+ * access the datum
+ *
+ * And the const version
+ */
+ template <class Rep, class Period>
+ UpgradeLockedPtr ulock(const std::chrono::duration<Rep, Period>& timeout) {
+ return UpgradeLockedPtr(static_cast<Subclass*>(this), timeout);
+ }
+ template <class Rep, class Period>
+ UpgradeLockedPtr ulock(
+ const std::chrono::duration<Rep, Period>& timeout) const {
+ return ConstUpgradeLockedPtr(static_cast<const Subclass*>(this), timeout);
+ }
+
+ /**
+ * Invoke a function while holding the lock.
+ *
+ * A reference to the datum will be passed into the function as its only
+ * argument.
+ *
+ * This can be used with a lambda argument for easily defining small critical
+ * sections in the code. For example:
+ *
+ * auto value = obj.withULock([](auto& data) {
+ * data.doStuff();
+ * return data.getValue();
+ * });
+ *
+ * This is probably not the function you want. If the intent is to read the
+ * data object and determine whether you should upgrade to a write lock then
+ * the withULockPtr() method should be called instead, since it gives access
+ * to the LockedPtr proxy (which can be upgraded via the
+ * moveFromUpgradeToWrite() method)
+ */
+ template <class Function>
+ auto withULock(Function&& function) const {
+ ConstUpgradeLockedGuardPtr guardPtr(static_cast<const Subclass*>(this));
+ return function(*guardPtr);
+ }
+
+ /**
+ * Invoke a function while holding the lock exclusively.
+ *
+ * This is similar to withULock(), but the function will be passed a
+ * LockedPtr rather than a reference to the data itself.
+ *
+ * This allows scopedUnlock() and getUniqueLock() to be called on the
+ * LockedPtr argument.
+ *
+ * This also allows you to upgrade the LockedPtr proxy to a write state so
+ * that changes can be made to the underlying data
+ */
+ template <class Function>
+ auto withULockPtr(Function&& function) {
+ return function(ulock());
+ }
+ template <class Function>
+ auto withULockPtr(Function&& function) const {
+ return function(ulock());
+ }