/*
- * Copyright 2016 Facebook, Inc.
+ * Copyright 2016-present Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <chrono>
#include <type_traits>
+#include <folly/functional/Invoke.h>
+
// Android, OSX, and Cygwin don't have timed mutexes
#if defined(ANDROID) || defined(__ANDROID__) || defined(__APPLE__) || \
defined(__CYGWIN__)
namespace folly {
namespace detail {
+namespace member {
+FOLLY_CREATE_MEMBER_INVOKE_TRAITS(lock, lock);
+FOLLY_CREATE_MEMBER_INVOKE_TRAITS(try_lock_for, try_lock_for);
+FOLLY_CREATE_MEMBER_INVOKE_TRAITS(lock_shared, lock_shared);
+FOLLY_CREATE_MEMBER_INVOKE_TRAITS(lock_upgrade, lock_upgrade);
+} // namespace member
+
/**
* An enum to describe the "level" of a mutex. The supported levels are
* Unique - a normal mutex that supports only exclusive locking
* mutex. This is used in conjunction with the above MutexLevel
* specializations and the LockTraitsImpl to determine what functions are
* supported by objects of type Mutex
- *
- * The implementation uses SINAE in the return value with trailing return
- * types to figure out what level a mutex is
*/
template <class Mutex>
class LockInterfaceDispatcher {
private:
// assert that the mutex type has basic lock and unlock functions
static_assert(
- std::is_same<decltype(std::declval<Mutex>().lock()), void>::value,
+ member::lock::is_invocable<Mutex>::value,
"The mutex type must support lock and unlock functions");
- // Helper functions for implementing the traits using SFINAE
- template <class T>
- static auto timed_lock_test(T*) -> typename std::is_same<
- decltype(std::declval<T>().try_lock_for(std::chrono::milliseconds(0))),
- bool>::type;
- template <class T>
- static std::false_type timed_lock_test(...);
-
- template <class T>
- static auto lock_shared_test(T*) -> typename std::
- is_same<decltype(std::declval<T>().lock_shared()), void>::type;
- template <class T>
- static std::false_type lock_shared_test(...);
-
- template <class T>
- static auto lock_upgrade_test(T*) -> typename std::
- is_same<decltype(std::declval<T>().lock_upgrade()), void>::type;
- template <class T>
- static std::false_type lock_upgrade_test(...);
+ using duration = std::chrono::milliseconds;
public:
static constexpr bool has_lock_unique = true;
static constexpr bool has_lock_timed =
- decltype(timed_lock_test<Mutex>(0))::value;
+ member::try_lock_for::is_invocable<Mutex, duration>::value;
static constexpr bool has_lock_shared =
- decltype(lock_shared_test<Mutex>(0))::value;
+ member::lock_shared::is_invocable<Mutex>::value;
static constexpr bool has_lock_upgrade =
- decltype(lock_upgrade_test<Mutex>(0))::value;
+ member::lock_upgrade::is_invocable<Mutex>::value;
};
/**
}
};
-} // detail
+} // namespace detail
/**
* LockTraits describes details about a particular mutex type.
/**
* A lock policy that performs exclusive lock operations.
*/
-class LockPolicyExclusive {
- public:
+struct LockPolicyExclusive {
template <class Mutex>
static void lock(Mutex& mutex) {
LockTraits<Mutex>::lock(mutex);
* A lock policy that performs shared lock operations.
* This policy only works with shared mutex types.
*/
-class LockPolicyShared {
- public:
+struct LockPolicyShared {
template <class Mutex>
static void lock(Mutex& mutex) {
LockTraits<Mutex>::lock_shared(mutex);
* A lock policy that performs a shared lock operation if a shared mutex type
* is given, or a normal exclusive lock operation on non-shared mutex types.
*/
-class LockPolicyShareable {
- public:
+struct LockPolicyShareable {
template <class Mutex>
static void lock(Mutex& mutex) {
lock_shared_or_unique(mutex);
}
};
-} // folly
+/**
+ * A lock policy with the following mapping
+ *
+ * lock() -> lock_upgrade()
+ * unlock() -> unlock_upgrade()
+ * try_lock_for -> try_lock_upgrade_for()
+ */
+struct LockPolicyUpgrade {
+ template <class Mutex>
+ static void lock(Mutex& mutex) {
+ LockTraits<Mutex>::lock_upgrade(mutex);
+ }
+ template <class Mutex, class Rep, class Period>
+ static bool try_lock_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>& timeout) {
+ return LockTraits<Mutex>::try_lock_upgrade_for(mutex, timeout);
+ }
+ template <class Mutex>
+ static void unlock(Mutex& mutex) {
+ LockTraits<Mutex>::unlock_upgrade(mutex);
+ }
+};
+
+/*****************************************************************************
+ * Policies for all the transitions from possible mutex levels
+ ****************************************************************************/
+/**
+ * A lock policy with the following mapping
+ *
+ * lock() -> unlock_upgrade_and_lock()
+ * unlock() -> unlock()
+ * try_lock_for -> try_unlock_upgrade_and_lock_for()
+ */
+struct LockPolicyFromUpgradeToExclusive : public LockPolicyExclusive {
+ template <class Mutex>
+ static void lock(Mutex& mutex) {
+ LockTraits<Mutex>::unlock_upgrade_and_lock(mutex);
+ }
+ template <class Mutex, class Rep, class Period>
+ static bool try_lock_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>& timeout) {
+ return LockTraits<Mutex>::try_unlock_upgrade_and_lock_for(mutex, timeout);
+ }
+};
+
+/**
+ * A lock policy with the following mapping
+ *
+ * lock() -> unlock_and_lock_upgrade()
+ * unlock() -> unlock_upgrade()
+ * try_lock_for -> unlock_and_lock_upgrade()
+ */
+struct LockPolicyFromExclusiveToUpgrade : public LockPolicyUpgrade {
+ template <class Mutex>
+ static void lock(Mutex& mutex) {
+ LockTraits<Mutex>::unlock_and_lock_upgrade(mutex);
+ }
+ template <class Mutex, class Rep, class Period>
+ static bool try_lock_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>&) {
+ LockTraits<Mutex>::unlock_and_lock_upgrade(mutex);
+
+ // downgrade should be non blocking and should succeed
+ return true;
+ }
+};
+
+/**
+ * A lock policy with the following mapping
+ *
+ * lock() -> unlock_upgrade_and_lock_shared()
+ * unlock() -> unlock_shared()
+ * try_lock_for -> unlock_upgrade_and_lock_shared()
+ */
+struct LockPolicyFromUpgradeToShared : public LockPolicyShared {
+ template <class Mutex>
+ static void lock(Mutex& mutex) {
+ LockTraits<Mutex>::unlock_upgrade_and_lock_shared(mutex);
+ }
+ template <class Mutex, class Rep, class Period>
+ static bool try_lock_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>&) {
+ LockTraits<Mutex>::unlock_upgrade_and_lock_shared(mutex);
+
+ // downgrade should be non blocking and should succeed
+ return true;
+ }
+};
+
+/**
+ * A lock policy with the following mapping
+ *
+ * lock() -> unlock_and_lock_shared()
+ * unlock() -> unlock_shared()
+ * try_lock_for() -> unlock_and_lock_shared()
+ */
+struct LockPolicyFromExclusiveToShared : public LockPolicyShared {
+ template <class Mutex>
+ static void lock(Mutex& mutex) {
+ LockTraits<Mutex>::unlock_and_lock_shared(mutex);
+ }
+ template <class Mutex, class Rep, class Period>
+ static bool try_lock_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>&) {
+ LockTraits<Mutex>::unlock_and_lock_shared(mutex);
+
+ // downgrade should be non blocking and should succeed
+ return true;
+ }
+};
+
+} // namespace folly