Commit d4774463 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook Github Bot

Use std interface in fibers mutex types

Summary:
[Folly] Use std interface in fibers mutex types: `try_lock_for`, `lock_shared`, etc.

This makes fibers mutex types play well with `Synchronized`.

Reviewed By: mnv104

Differential Revision: D15440755

fbshipit-source-id: 068b0c0b973577729f76f78c9063ff2ec06f4d90
parent 45f4b89d
...@@ -91,10 +91,16 @@ inline void TimedMutex::lock() { ...@@ -91,10 +91,16 @@ inline void TimedMutex::lock() {
} }
template <typename Rep, typename Period> template <typename Rep, typename Period>
bool TimedMutex::timed_lock( bool TimedMutex::try_lock_for(
const std::chrono::duration<Rep, Period>& duration) { const std::chrono::duration<Rep, Period>& timeout) {
return try_lock_until(std::chrono::steady_clock::now() + timeout);
}
template <typename Clock, typename Duration>
bool TimedMutex::try_lock_until(
const std::chrono::time_point<Clock, Duration>& deadline) {
auto result = lockHelper([&](MutexWaiter& waiter) { auto result = lockHelper([&](MutexWaiter& waiter) {
if (!waiter.baton.try_wait_for(duration)) { if (!waiter.baton.try_wait_until(deadline)) {
// We timed out. Two cases: // We timed out. Two cases:
// 1. We're still in the waiter list and we truly timed out // 1. We're still in the waiter list and we truly timed out
// 2. We're not in the waiter list anymore. This could happen if the baton // 2. We're not in the waiter list anymore. This could happen if the baton
...@@ -159,7 +165,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::shouldReadersWait() const { ...@@ -159,7 +165,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::shouldReadersWait() const {
} }
template <bool ReaderPriority, typename BatonType> template <bool ReaderPriority, typename BatonType>
void TimedRWMutexImpl<ReaderPriority, BatonType>::read_lock() { void TimedRWMutexImpl<ReaderPriority, BatonType>::lock_shared() {
std::unique_lock<folly::SpinLock> ulock{lock_}; std::unique_lock<folly::SpinLock> ulock{lock_};
if (shouldReadersWait()) { if (shouldReadersWait()) {
MutexWaiter waiter; MutexWaiter waiter;
...@@ -179,15 +185,22 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::read_lock() { ...@@ -179,15 +185,22 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::read_lock() {
template <bool ReaderPriority, typename BatonType> template <bool ReaderPriority, typename BatonType>
template <typename Rep, typename Period> template <typename Rep, typename Period>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_read_lock( bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared_for(
const std::chrono::duration<Rep, Period>& duration) { const std::chrono::duration<Rep, Period>& timeout) {
return try_lock_shared_until(std::chrono::steady_clock::now() + timeout);
}
template <bool ReaderPriority, typename BatonType>
template <typename Clock, typename Duration>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared_until(
const std::chrono::time_point<Clock, Duration>& deadline) {
std::unique_lock<folly::SpinLock> ulock{lock_}; std::unique_lock<folly::SpinLock> ulock{lock_};
if (shouldReadersWait()) { if (shouldReadersWait()) {
MutexWaiter waiter; MutexWaiter waiter;
read_waiters_.push_back(waiter); read_waiters_.push_back(waiter);
ulock.unlock(); ulock.unlock();
if (!waiter.baton.try_wait_for(duration)) { if (!waiter.baton.try_wait_until(deadline)) {
// We timed out. Two cases: // We timed out. Two cases:
// 1. We're still in the waiter list and we truly timed out // 1. We're still in the waiter list and we truly timed out
// 2. We're not in the waiter list anymore. This could happen if the baton // 2. We're not in the waiter list anymore. This could happen if the baton
...@@ -211,7 +224,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_read_lock( ...@@ -211,7 +224,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_read_lock(
} }
template <bool ReaderPriority, typename BatonType> template <bool ReaderPriority, typename BatonType>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_read_lock() { bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared() {
std::lock_guard<SpinLock> guard{lock_}; std::lock_guard<SpinLock> guard{lock_};
if (!shouldReadersWait()) { if (!shouldReadersWait()) {
assert( assert(
...@@ -226,7 +239,16 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_read_lock() { ...@@ -226,7 +239,16 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_read_lock() {
} }
template <bool ReaderPriority, typename BatonType> template <bool ReaderPriority, typename BatonType>
void TimedRWMutexImpl<ReaderPriority, BatonType>::write_lock() { void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_shared() {
if (kIsDebug) {
std::unique_lock<folly::SpinLock> ulock{lock_};
assert(state_ == State::READ_LOCKED);
}
unlock_();
}
template <bool ReaderPriority, typename BatonType>
void TimedRWMutexImpl<ReaderPriority, BatonType>::lock() {
std::unique_lock<folly::SpinLock> ulock{lock_}; std::unique_lock<folly::SpinLock> ulock{lock_};
if (state_ == State::UNLOCKED) { if (state_ == State::UNLOCKED) {
verify_unlocked_properties(); verify_unlocked_properties();
...@@ -241,8 +263,15 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::write_lock() { ...@@ -241,8 +263,15 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::write_lock() {
template <bool ReaderPriority, typename BatonType> template <bool ReaderPriority, typename BatonType>
template <typename Rep, typename Period> template <typename Rep, typename Period>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_write_lock( bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_for(
const std::chrono::duration<Rep, Period>& duration) { const std::chrono::duration<Rep, Period>& timeout) {
return try_lock_until(std::chrono::steady_clock::now() + timeout);
}
template <bool ReaderPriority, typename BatonType>
template <typename Clock, typename Duration>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_until(
const std::chrono::time_point<Clock, Duration>& deadline) {
std::unique_lock<folly::SpinLock> ulock{lock_}; std::unique_lock<folly::SpinLock> ulock{lock_};
if (state_ == State::UNLOCKED) { if (state_ == State::UNLOCKED) {
verify_unlocked_properties(); verify_unlocked_properties();
...@@ -253,7 +282,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_write_lock( ...@@ -253,7 +282,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_write_lock(
write_waiters_.push_back(waiter); write_waiters_.push_back(waiter);
ulock.unlock(); ulock.unlock();
if (!waiter.baton.try_wait_for(duration)) { if (!waiter.baton.try_wait_until(deadline)) {
// We timed out. Two cases: // We timed out. Two cases:
// 1. We're still in the waiter list and we truly timed out // 1. We're still in the waiter list and we truly timed out
// 2. We're not in the waiter list anymore. This could happen if the baton // 2. We're not in the waiter list anymore. This could happen if the baton
...@@ -270,7 +299,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_write_lock( ...@@ -270,7 +299,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_write_lock(
} }
template <bool ReaderPriority, typename BatonType> template <bool ReaderPriority, typename BatonType>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_write_lock() { bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock() {
std::lock_guard<SpinLock> guard{lock_}; std::lock_guard<SpinLock> guard{lock_};
if (state_ == State::UNLOCKED) { if (state_ == State::UNLOCKED) {
verify_unlocked_properties(); verify_unlocked_properties();
...@@ -282,6 +311,15 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_write_lock() { ...@@ -282,6 +311,15 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_write_lock() {
template <bool ReaderPriority, typename BatonType> template <bool ReaderPriority, typename BatonType>
void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock() { void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock() {
if (kIsDebug) {
std::unique_lock<folly::SpinLock> ulock{lock_};
assert(state_ == State::WRITE_LOCKED);
}
unlock_();
}
template <bool ReaderPriority, typename BatonType>
void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_() {
std::lock_guard<SpinLock> guard{lock_}; std::lock_guard<SpinLock> guard{lock_};
assert(state_ != State::UNLOCKED); assert(state_ != State::UNLOCKED);
assert( assert(
...@@ -322,7 +360,7 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock() { ...@@ -322,7 +360,7 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock() {
} }
template <bool ReaderPriority, typename BatonType> template <bool ReaderPriority, typename BatonType>
void TimedRWMutexImpl<ReaderPriority, BatonType>::downgrade() { void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_and_lock_shared() {
std::lock_guard<SpinLock> guard{lock_}; std::lock_guard<SpinLock> guard{lock_};
assert(state_ == State::WRITE_LOCKED && readers_ == 0); assert(state_ == State::WRITE_LOCKED && readers_ == 0);
state_ = State::READ_LOCKED; state_ = State::READ_LOCKED;
......
...@@ -46,11 +46,17 @@ class TimedMutex { ...@@ -46,11 +46,17 @@ class TimedMutex {
// Lock the mutex. The thread / fiber is blocked until the mutex is free // Lock the mutex. The thread / fiber is blocked until the mutex is free
void lock(); void lock();
// Lock the mutex. The thread / fiber will be blocked for a time duration. // Lock the mutex. The thread / fiber will be blocked until a timeout elapses.
// //
// @return true if the mutex was locked, false otherwise // @return true if the mutex was locked, false otherwise
template <typename Rep, typename Period> template <typename Rep, typename Period>
bool timed_lock(const std::chrono::duration<Rep, Period>& duration); bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout);
// Lock the mutex. The thread / fiber will be blocked until a deadline
//
// @return true if the mutex was locked, false otherwise
template <typename Clock, typename Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& deadline);
// Try to obtain lock without blocking the thread or fiber // Try to obtain lock without blocking the thread or fiber
bool try_lock(); bool try_lock();
...@@ -110,36 +116,47 @@ class TimedRWMutexImpl { ...@@ -110,36 +116,47 @@ class TimedRWMutexImpl {
// Lock for shared access. The thread / fiber is blocked until the lock // Lock for shared access. The thread / fiber is blocked until the lock
// can be acquired. // can be acquired.
void read_lock(); void lock_shared();
// Like read_lock except the thread /fiber is blocked for a time duration // Like lock_shared except the thread / fiber is blocked until a timeout
// elapses
// @return true if locked successfully, false otherwise. // @return true if locked successfully, false otherwise.
template <typename Rep, typename Period> template <typename Rep, typename Period>
bool timed_read_lock(const std::chrono::duration<Rep, Period>& duration); bool try_lock_shared_for(const std::chrono::duration<Rep, Period>& timeout);
// Like read_lock but doesn't block the thread / fiber if the lock can't // Like lock_shared except the thread / fiber is blocked until a deadline
// @return true if locked successfully, false otherwise.
template <typename Clock, typename Duration>
bool try_lock_shared_until(
const std::chrono::time_point<Clock, Duration>& deadline);
// Like lock_shared but doesn't block the thread / fiber if the lock can't
// be acquired. // be acquired.
// @return true if lock was acquired, false otherwise. // @return true if lock was acquired, false otherwise.
bool try_read_lock(); bool try_lock_shared();
// Release the lock. The thread / fiber will wake up a writer if there is one
// and if this is the last concurrently-held read lock to be released.
void unlock_shared();
// Obtain an exclusive lock. The thread / fiber is blocked until the lock // Obtain an exclusive lock. The thread / fiber is blocked until the lock
// is available. // is available.
void write_lock(); void lock();
// Like write_lock except the thread / fiber is blocked for a time duration // Like lock except the thread / fiber is blocked until a timeout elapses
// @return true if locked successfully, false otherwise. // @return true if locked successfully, false otherwise.
template <typename Rep, typename Period> template <typename Rep, typename Period>
bool timed_write_lock(const std::chrono::duration<Rep, Period>& duration); bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout);
// Like write_lock but doesn't block the thread / fiber if the lock cant be // Like lock except the thread / fiber is blocked until a deadline
// @return true if locked successfully, false otherwise.
template <typename Clock, typename Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& deadline);
// Like lock but doesn't block the thread / fiber if the lock cant be
// obtained. // obtained.
// @return true if lock was acquired, false otherwise. // @return true if lock was acquired, false otherwise.
bool try_write_lock(); bool try_lock();
// Wrapper for write_lock() for compatibility with Mutex
void lock() {
write_lock();
}
// Realease the lock. The thread / fiber will wake up all readers if there are // Realease the lock. The thread / fiber will wake up all readers if there are
// any. If there are waiting writers then only one of them will be woken up. // any. If there are waiting writers then only one of them will be woken up.
...@@ -148,17 +165,17 @@ class TimedRWMutexImpl { ...@@ -148,17 +165,17 @@ class TimedRWMutexImpl {
// Downgrade the lock. The thread / fiber will wake up all readers if there // Downgrade the lock. The thread / fiber will wake up all readers if there
// are any. // are any.
void downgrade(); void unlock_and_lock_shared();
class FOLLY_NODISCARD ReadHolder { class FOLLY_NODISCARD ReadHolder {
public: public:
explicit ReadHolder(TimedRWMutexImpl& lock) : lock_(&lock) { explicit ReadHolder(TimedRWMutexImpl& lock) : lock_(&lock) {
lock_->read_lock(); lock_->lock_shared();
} }
~ReadHolder() { ~ReadHolder() {
if (lock_) { if (lock_) {
lock_->unlock(); lock_->unlock_shared();
} }
} }
...@@ -174,7 +191,7 @@ class TimedRWMutexImpl { ...@@ -174,7 +191,7 @@ class TimedRWMutexImpl {
class FOLLY_NODISCARD WriteHolder { class FOLLY_NODISCARD WriteHolder {
public: public:
explicit WriteHolder(TimedRWMutexImpl& lock) : lock_(&lock) { explicit WriteHolder(TimedRWMutexImpl& lock) : lock_(&lock) {
lock_->write_lock(); lock_->lock();
} }
~WriteHolder() { ~WriteHolder() {
...@@ -202,6 +219,8 @@ class TimedRWMutexImpl { ...@@ -202,6 +219,8 @@ class TimedRWMutexImpl {
bool shouldReadersWait() const; bool shouldReadersWait() const;
void unlock_();
// Different states the lock can be in // Different states the lock can be in
enum class State { enum class State {
UNLOCKED, UNLOCKED,
......
...@@ -2222,7 +2222,7 @@ TEST(TimedMutex, ThreadFiberDeadlockOrder) { ...@@ -2222,7 +2222,7 @@ TEST(TimedMutex, ThreadFiberDeadlockOrder) {
fm.addTask([&] { std::lock_guard<TimedMutex> lg(mutex); }); fm.addTask([&] { std::lock_guard<TimedMutex> lg(mutex); });
fm.addTask([&] { fm.addTask([&] {
runInMainContext([&] { runInMainContext([&] {
auto locked = mutex.timed_lock(std::chrono::seconds{1}); auto locked = mutex.try_lock_for(std::chrono::seconds{1});
EXPECT_TRUE(locked); EXPECT_TRUE(locked);
if (locked) { if (locked) {
mutex.unlock(); mutex.unlock();
...@@ -2244,7 +2244,7 @@ TEST(TimedMutex, ThreadFiberDeadlockRace) { ...@@ -2244,7 +2244,7 @@ TEST(TimedMutex, ThreadFiberDeadlockRace) {
mutex.lock(); mutex.lock();
fm.addTask([&] { fm.addTask([&] {
auto locked = mutex.timed_lock(std::chrono::seconds{1}); auto locked = mutex.try_lock_for(std::chrono::seconds{1});
EXPECT_TRUE(locked); EXPECT_TRUE(locked);
if (locked) { if (locked) {
mutex.unlock(); mutex.unlock();
...@@ -2253,7 +2253,7 @@ TEST(TimedMutex, ThreadFiberDeadlockRace) { ...@@ -2253,7 +2253,7 @@ TEST(TimedMutex, ThreadFiberDeadlockRace) {
fm.addTask([&] { fm.addTask([&] {
mutex.unlock(); mutex.unlock();
runInMainContext([&] { runInMainContext([&] {
auto locked = mutex.timed_lock(std::chrono::seconds{1}); auto locked = mutex.try_lock_for(std::chrono::seconds{1});
EXPECT_TRUE(locked); EXPECT_TRUE(locked);
if (locked) { if (locked) {
mutex.unlock(); mutex.unlock();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment