--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This module provides a traits class for describing properties about mutex
+ * classes.
+ *
+ * This is a primitive for building higher-level abstractions that can work
+ * with a variety of mutex classes. For instance, this allows
+ * folly::Synchronized to support a number of different mutex types.
+ */
+#pragma once
+
+#include <chrono>
+#include <type_traits>
+
+// Android, OSX, and Cygwin don't have timed mutexes
+#if defined(ANDROID) || defined(__ANDROID__) || defined(__APPLE__) || \
+ defined(__CYGWIN__)
+#define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 0
+#else
+#define FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES 1
+#endif
+
+namespace folly {
+namespace detail {
+
+/**
+ * An internal helper class for identifying if a lock type supports
+ * lock_shared() and try_lock_for() methods.
+ */
+template <class Mutex>
+class LockTraitsImpl {
+ private:
+ // 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(...);
+
+ public:
+ static constexpr bool has_timed_lock =
+ decltype(timed_lock_test<Mutex>(0))::value;
+ static constexpr bool has_lock_shared =
+ decltype(lock_shared_test<Mutex>(0))::value;
+};
+
+template <class Mutex>
+struct LockTraitsUniqueBase {
+ /**
+ * Acquire the lock exclusively.
+ */
+ static void lock(Mutex& mutex) {
+ mutex.lock();
+ }
+
+ /**
+ * Release an exclusively-held lock.
+ */
+ static void unlock(Mutex& mutex) {
+ mutex.unlock();
+ }
+};
+
+template <class Mutex>
+struct LockTraitsSharedBase : public LockTraitsUniqueBase<Mutex> {
+ /**
+ * Acquire the lock in shared (read) mode.
+ */
+ static void lock_shared(Mutex& mutex) {
+ mutex.lock_shared();
+ }
+
+ /**
+ * Release a lock held in shared mode.
+ */
+ static void unlock_shared(Mutex& mutex) {
+ mutex.unlock_shared();
+ }
+};
+
+template <class Mutex, bool is_shared, bool is_timed>
+struct LockTraitsBase {};
+
+template <class Mutex>
+struct LockTraitsBase<Mutex, false, false>
+ : public LockTraitsUniqueBase<Mutex> {
+ static constexpr bool is_shared = false;
+ static constexpr bool is_timed = false;
+};
+
+template <class Mutex>
+struct LockTraitsBase<Mutex, true, false> : public LockTraitsSharedBase<Mutex> {
+ static constexpr bool is_shared = true;
+ static constexpr bool is_timed = false;
+};
+
+template <class Mutex>
+struct LockTraitsBase<Mutex, false, true> : public LockTraitsUniqueBase<Mutex> {
+ static constexpr bool is_shared = false;
+ static constexpr bool is_timed = true;
+
+ /**
+ * Acquire the lock exclusively, with a timeout.
+ *
+ * Returns true or false indicating if the lock was acquired or not.
+ */
+ template <class Rep, class Period>
+ static bool try_lock_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>& timeout) {
+ return mutex.try_lock_for(timeout);
+ }
+};
+
+template <class Mutex>
+struct LockTraitsBase<Mutex, true, true> : public LockTraitsSharedBase<Mutex> {
+ static constexpr bool is_shared = true;
+ static constexpr bool is_timed = true;
+
+ /**
+ * Acquire the lock exclusively, with a timeout.
+ *
+ * Returns true or false indicating if the lock was acquired or not.
+ */
+ template <class Rep, class Period>
+ static bool try_lock_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>& timeout) {
+ return mutex.try_lock_for(timeout);
+ }
+
+ /**
+ * Acquire the lock in shared (read) mode, with a timeout.
+ *
+ * Returns true or false indicating if the lock was acquired or not.
+ */
+ template <class Rep, class Period>
+ static bool try_lock_shared_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>& timeout) {
+ return mutex.try_lock_shared_for(timeout);
+ }
+};
+} // detail
+
+/**
+ * LockTraits describes details about a particular mutex type.
+ *
+ * The default implementation automatically attempts to detect traits
+ * based on the presence of various member functions.
+ *
+ * You can specialize LockTraits to provide custom behavior for lock
+ * classes that do not use the standard method names
+ * (lock()/unlock()/lock_shared()/unlock_shared()/try_lock_for())
+ *
+ *
+ * LockTraits contains the following members variables:
+ * - static constexpr bool is_shared
+ * True if the lock supports separate shared vs exclusive locking states.
+ * - static constexpr bool is_timed
+ * True if the lock supports acquiring the lock with a timeout.
+ *
+ * The following static methods always exist:
+ * - lock(Mutex& mutex)
+ * - unlock(Mutex& mutex)
+ *
+ * The following static methods may exist, depending on is_shared and
+ * is_timed:
+ * - try_lock_for(Mutex& mutex, <std_chrono_duration> timeout)
+ * - lock_shared(Mutex& mutex)
+ * - unlock_shared(Mutex& mutex)
+ * - try_lock_shared_for(Mutex& mutex, <std_chrono_duration> timeout)
+ */
+template <class Mutex>
+struct LockTraits : public detail::LockTraitsBase<
+ Mutex,
+ detail::LockTraitsImpl<Mutex>::has_lock_shared,
+ detail::LockTraitsImpl<Mutex>::has_timed_lock> {};
+
+/**
+ * If the lock is a shared lock, acquire it in shared mode.
+ * Otherwise, for plain (exclusive-only) locks, perform a normal acquire.
+ */
+template <class Mutex>
+typename std::enable_if<LockTraits<Mutex>::is_shared>::type
+lock_shared_or_unique(Mutex& mutex) {
+ LockTraits<Mutex>::lock_shared(mutex);
+}
+template <class Mutex>
+typename std::enable_if<!LockTraits<Mutex>::is_shared>::type
+lock_shared_or_unique(Mutex& mutex) {
+ LockTraits<Mutex>::lock(mutex);
+}
+
+/**
+ * If the lock is a shared lock, try to acquire it in shared mode, for up to
+ * the given timeout. Otherwise, for plain (exclusive-only) locks, try to
+ * perform a normal acquire.
+ *
+ * Returns true if the lock was acquired, or false on time out.
+ */
+template <class Mutex, class Rep, class Period>
+typename std::enable_if<LockTraits<Mutex>::is_shared, bool>::type
+try_lock_shared_or_unique_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>& timeout) {
+ return LockTraits<Mutex>::try_lock_shared_for(mutex, timeout);
+}
+template <class Mutex, class Rep, class Period>
+typename std::enable_if<!LockTraits<Mutex>::is_shared, bool>::type
+try_lock_shared_or_unique_for(
+ Mutex& mutex,
+ const std::chrono::duration<Rep, Period>& timeout) {
+ return LockTraits<Mutex>::try_lock_for(mutex, timeout);
+}
+
+/**
+ * Release a lock acquired with lock_shared_or_unique()
+ */
+template <class Mutex>
+typename std::enable_if<LockTraits<Mutex>::is_shared>::type
+unlock_shared_or_unique(Mutex& mutex) {
+ LockTraits<Mutex>::unlock_shared(mutex);
+}
+template <class Mutex>
+typename std::enable_if<!LockTraits<Mutex>::is_shared>::type
+unlock_shared_or_unique(Mutex& mutex) {
+ LockTraits<Mutex>::unlock(mutex);
+}
+} // folly
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This file contains LockTraits specializations for boost mutex types.
+ *
+ * These need to be specialized simply due to the fact that the timed
+ * methods take boost::chrono arguments instead of std::chrono.
+ */
+#pragma once
+
+#include <folly/LockTraits.h>
+
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+
+#include <boost/thread.hpp>
+
+namespace folly {
+
+/**
+ * LockTraits specialization for boost::shared_mutex
+ */
+template <>
+struct LockTraits<boost::shared_mutex>
+ : public folly::detail::LockTraitsSharedBase<boost::shared_mutex> {
+ static constexpr bool is_shared = true;
+ static constexpr bool is_timed = true;
+
+ static bool try_lock_for(
+ boost::shared_mutex& mutex,
+ std::chrono::milliseconds timeout) {
+ // Convert the std::chrono argument to boost::chrono
+ return mutex.try_lock_for(boost::chrono::milliseconds(timeout.count()));
+ }
+
+ static bool try_lock_shared_for(
+ boost::shared_mutex& mutex,
+ std::chrono::milliseconds timeout) {
+ // Convert the std::chrono argument to boost::chrono
+ return mutex.try_lock_shared_for(
+ boost::chrono::milliseconds(timeout.count()));
+ }
+};
+
+/**
+ * LockTraits specialization for boost::timed_mutex
+ */
+template <>
+struct LockTraits<boost::timed_mutex>
+ : public folly::detail::LockTraitsUniqueBase<boost::timed_mutex> {
+ static constexpr bool is_shared = false;
+ static constexpr bool is_timed = true;
+
+ static bool try_lock_for(
+ boost::timed_mutex& mutex,
+ std::chrono::milliseconds timeout) {
+ // Convert the std::chrono argument to boost::chrono
+ return mutex.try_lock_for(boost::chrono::milliseconds(timeout.count()));
+ }
+};
+
+/**
+ * LockTraits specialization for boost::recursive_timed_mutex
+ */
+template <>
+struct LockTraits<boost::recursive_timed_mutex>
+ : public folly::detail::LockTraitsUniqueBase<boost::recursive_timed_mutex> {
+ static constexpr bool is_shared = false;
+ static constexpr bool is_timed = true;
+
+ static bool try_lock_for(
+ boost::recursive_timed_mutex& mutex,
+ std::chrono::milliseconds timeout) {
+ // Convert the std::chrono argument to boost::chrono
+ return mutex.try_lock_for(boost::chrono::milliseconds(timeout.count()));
+ }
+};
+} // folly
+
+#endif // FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
Lazy.h \
LifoSem.h \
Likely.h \
+ LockTraits.h \
+ LockTraitsBoost.h \
Logging.h \
MacAddress.h \
Malloc.h \
#pragma once
-#include <type_traits>
-#include <mutex>
#include <boost/thread.hpp>
+#include <folly/LockTraits.h>
#include <folly/Preprocessor.h>
#include <folly/SharedMutex.h>
#include <folly/Traits.h>
+#include <mutex>
+#include <type_traits>
+
+// Temporarily provide FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES under the legacy
+// FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES name. This definition will be
+// removed shortly in an upcoming diff to make Synchronized fully utilize
+// LockTraits.
+#define FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES \
+ FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
namespace folly {
* Free function adaptors for std:: and boost::
*/
-// Android, OSX, and Cygwin don't have timed mutexes
-#if defined(ANDROID) || defined(__ANDROID__) || \
- defined(__APPLE__) || defined(__CYGWIN__)
-# define FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES 0
-#else
-# define FOLLY_SYNCHRONIZED_HAVE_TIMED_MUTEXES 1
-#endif
-
/**
* Yields true iff T has .lock() and .unlock() member functions. This
* is done by simply enumerating the mutexes with this interface in
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <folly/LockTraits.h>
+#include <folly/LockTraitsBoost.h>
+
+#include <gtest/gtest.h>
+#include <mutex>
+
+#include <folly/RWSpinLock.h>
+#include <folly/SharedMutex.h>
+#include <folly/SpinLock.h>
+
+using namespace folly;
+
+TEST(LockTraits, std_mutex) {
+ using traits = LockTraits<std::mutex>;
+ static_assert(!traits::is_timed, "std:mutex is not a timed lock");
+ static_assert(!traits::is_shared, "std:mutex is not a shared lock");
+
+ std::mutex mutex;
+ traits::lock(mutex);
+ traits::unlock(mutex);
+
+ lock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+}
+
+TEST(LockTraits, SharedMutex) {
+ using traits = LockTraits<SharedMutex>;
+ static_assert(traits::is_timed, "SharedMutex is a timed lock");
+ static_assert(traits::is_shared, "SharedMutex is a shared lock");
+
+ SharedMutex mutex;
+ traits::lock(mutex);
+ traits::unlock(mutex);
+
+ traits::lock_shared(mutex);
+ traits::lock_shared(mutex);
+ traits::unlock_shared(mutex);
+ traits::unlock_shared(mutex);
+
+ lock_shared_or_unique(mutex);
+ lock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+}
+
+TEST(LockTraits, SpinLock) {
+ using traits = LockTraits<SpinLock>;
+ static_assert(!traits::is_timed, "folly::SpinLock is not a timed lock");
+ static_assert(!traits::is_shared, "folly::SpinLock is not a shared lock");
+
+ SpinLock mutex;
+ traits::lock(mutex);
+ traits::unlock(mutex);
+
+ lock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+}
+
+TEST(LockTraits, RWSpinLock) {
+ using traits = LockTraits<RWSpinLock>;
+ static_assert(!traits::is_timed, "folly::RWSpinLock is not a timed lock");
+ static_assert(traits::is_shared, "folly::RWSpinLock is a shared lock");
+
+ RWSpinLock mutex;
+ traits::lock(mutex);
+ traits::unlock(mutex);
+
+ traits::lock_shared(mutex);
+ traits::lock_shared(mutex);
+ traits::unlock_shared(mutex);
+ traits::unlock_shared(mutex);
+
+ lock_shared_or_unique(mutex);
+ lock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+}
+
+TEST(LockTraits, boost_mutex) {
+ using traits = LockTraits<boost::mutex>;
+ static_assert(!traits::is_timed, "boost::mutex is not a timed lock");
+ static_assert(!traits::is_shared, "boost::mutex is not a shared lock");
+
+ boost::mutex mutex;
+ traits::lock(mutex);
+ traits::unlock(mutex);
+
+ lock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+}
+
+TEST(LockTraits, boost_recursive_mutex) {
+ using traits = LockTraits<boost::recursive_mutex>;
+ static_assert(
+ !traits::is_timed, "boost::recursive_mutex is not a timed lock");
+ static_assert(
+ !traits::is_shared, "boost::recursive_mutex is not a shared lock");
+
+ boost::recursive_mutex mutex;
+ traits::lock(mutex);
+ traits::lock(mutex);
+ traits::unlock(mutex);
+ traits::unlock(mutex);
+
+ lock_shared_or_unique(mutex);
+ lock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+}
+
+#if FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
+TEST(LockTraits, timed_mutex) {
+ using traits = LockTraits<std::timed_mutex>;
+ static_assert(traits::is_timed, "std::timed_mutex is a timed lock");
+ static_assert(!traits::is_shared, "std::timed_mutex is not a shared lock");
+
+ std::timed_mutex mutex;
+ traits::lock(mutex);
+ bool gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
+ EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
+ << "timed_mutex a second time";
+ traits::unlock(mutex);
+
+ lock_shared_or_unique(mutex);
+ gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(1));
+ EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
+ << "timed_mutex a second time";
+ unlock_shared_or_unique(mutex);
+}
+
+TEST(LockTraits, recursive_timed_mutex) {
+ using traits = LockTraits<std::recursive_timed_mutex>;
+ static_assert(traits::is_timed, "std::recursive_timed_mutex is a timed lock");
+ static_assert(
+ !traits::is_shared, "std::recursive_timed_mutex is not a shared lock");
+
+ std::recursive_timed_mutex mutex;
+ traits::lock(mutex);
+ auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(10));
+ EXPECT_TRUE(gotLock) << "should have been able to acquire the "
+ << "recursive_timed_mutex a second time";
+ traits::unlock(mutex);
+ traits::unlock(mutex);
+
+ lock_shared_or_unique(mutex);
+ gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(10));
+ EXPECT_TRUE(gotLock) << "should have been able to acquire the "
+ << "recursive_timed_mutex a second time";
+ unlock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+}
+
+TEST(LockTraits, boost_shared_mutex) {
+ using traits = LockTraits<boost::shared_mutex>;
+ static_assert(traits::is_timed, "boost::shared_mutex is a timed lock");
+ static_assert(traits::is_shared, "boost::shared_mutex is a shared lock");
+
+ boost::shared_mutex mutex;
+ traits::lock(mutex);
+ auto gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
+ EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
+ << "shared_mutex a second time";
+ gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(1));
+ EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
+ << "shared_mutex a second time";
+ traits::unlock(mutex);
+
+ traits::lock_shared(mutex);
+ gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
+ EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
+ << "shared_mutex a second time";
+ gotLock = traits::try_lock_shared_for(mutex, std::chrono::milliseconds(10));
+ EXPECT_TRUE(gotLock) << "should have been able to acquire the "
+ << "shared_mutex a second time in shared mode";
+ traits::unlock_shared(mutex);
+ traits::unlock_shared(mutex);
+
+ lock_shared_or_unique(mutex);
+ gotLock = traits::try_lock_for(mutex, std::chrono::milliseconds(1));
+ EXPECT_FALSE(gotLock) << "should not have been able to acquire the "
+ << "shared_mutex a second time";
+ gotLock = try_lock_shared_or_unique_for(mutex, std::chrono::milliseconds(10));
+ EXPECT_TRUE(gotLock) << "should have been able to acquire the "
+ << "shared_mutex a second time in shared mode";
+ unlock_shared_or_unique(mutex);
+ unlock_shared_or_unique(mutex);
+}
+#endif // FOLLY_LOCK_TRAITS_HAVE_TIMED_MUTEXES
synchronized_test_LDADD = libfollytestmain.la
TESTS += synchronized_test
+lock_traits_test_SOURCES = LockTraitsTest.cpp
+lock_traits_test_LDADD = libfollytestmain.la
+TESTS += lock_traits_test
+
concurrent_skiplist_test_SOURCES = ConcurrentSkipListTest.cpp
concurrent_skiplist_test_LDADD = libfollytestmain.la
TESTS += concurrent_skiplist_test