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() {
}
template <typename Rep, typename Period>
bool TimedMutex::timed_lock(
const std::chrono::duration<Rep, Period>& duration) {
bool TimedMutex::try_lock_for(
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) {
if (!waiter.baton.try_wait_for(duration)) {
if (!waiter.baton.try_wait_until(deadline)) {
// We timed out. Two cases:
// 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
......@@ -159,7 +165,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::shouldReadersWait() const {
}
template <bool ReaderPriority, typename BatonType>
void TimedRWMutexImpl<ReaderPriority, BatonType>::read_lock() {
void TimedRWMutexImpl<ReaderPriority, BatonType>::lock_shared() {
std::unique_lock<folly::SpinLock> ulock{lock_};
if (shouldReadersWait()) {
MutexWaiter waiter;
......@@ -179,15 +185,22 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::read_lock() {
template <bool ReaderPriority, typename BatonType>
template <typename Rep, typename Period>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_read_lock(
const std::chrono::duration<Rep, Period>& duration) {
bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_shared_for(
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_};
if (shouldReadersWait()) {
MutexWaiter waiter;
read_waiters_.push_back(waiter);
ulock.unlock();
if (!waiter.baton.try_wait_for(duration)) {
if (!waiter.baton.try_wait_until(deadline)) {
// We timed out. Two cases:
// 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
......@@ -211,7 +224,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_read_lock(
}
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_};
if (!shouldReadersWait()) {
assert(
......@@ -226,7 +239,16 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_read_lock() {
}
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_};
if (state_ == State::UNLOCKED) {
verify_unlocked_properties();
......@@ -241,8 +263,15 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::write_lock() {
template <bool ReaderPriority, typename BatonType>
template <typename Rep, typename Period>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_write_lock(
const std::chrono::duration<Rep, Period>& duration) {
bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock_for(
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_};
if (state_ == State::UNLOCKED) {
verify_unlocked_properties();
......@@ -253,7 +282,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_write_lock(
write_waiters_.push_back(waiter);
ulock.unlock();
if (!waiter.baton.try_wait_for(duration)) {
if (!waiter.baton.try_wait_until(deadline)) {
// We timed out. Two cases:
// 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
......@@ -270,7 +299,7 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::timed_write_lock(
}
template <bool ReaderPriority, typename BatonType>
bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_write_lock() {
bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_lock() {
std::lock_guard<SpinLock> guard{lock_};
if (state_ == State::UNLOCKED) {
verify_unlocked_properties();
......@@ -282,6 +311,15 @@ bool TimedRWMutexImpl<ReaderPriority, BatonType>::try_write_lock() {
template <bool ReaderPriority, typename BatonType>
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_};
assert(state_ != State::UNLOCKED);
assert(
......@@ -322,7 +360,7 @@ void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock() {
}
template <bool ReaderPriority, typename BatonType>
void TimedRWMutexImpl<ReaderPriority, BatonType>::downgrade() {
void TimedRWMutexImpl<ReaderPriority, BatonType>::unlock_and_lock_shared() {
std::lock_guard<SpinLock> guard{lock_};
assert(state_ == State::WRITE_LOCKED && readers_ == 0);
state_ = State::READ_LOCKED;
......
......@@ -46,11 +46,17 @@ class TimedMutex {
// Lock the mutex. The thread / fiber is blocked until the mutex is free
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
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
bool try_lock();
......@@ -110,36 +116,47 @@ class TimedRWMutexImpl {
// Lock for shared access. The thread / fiber is blocked until the lock
// 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.
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.
// @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
// 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.
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.
// @return true if lock was acquired, false otherwise.
bool try_write_lock();
// Wrapper for write_lock() for compatibility with Mutex
void lock() {
write_lock();
}
bool try_lock();
// 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.
......@@ -148,17 +165,17 @@ class TimedRWMutexImpl {
// Downgrade the lock. The thread / fiber will wake up all readers if there
// are any.
void downgrade();
void unlock_and_lock_shared();
class FOLLY_NODISCARD ReadHolder {
public:
explicit ReadHolder(TimedRWMutexImpl& lock) : lock_(&lock) {
lock_->read_lock();
lock_->lock_shared();
}
~ReadHolder() {
if (lock_) {
lock_->unlock();
lock_->unlock_shared();
}
}
......@@ -174,7 +191,7 @@ class TimedRWMutexImpl {
class FOLLY_NODISCARD WriteHolder {
public:
explicit WriteHolder(TimedRWMutexImpl& lock) : lock_(&lock) {
lock_->write_lock();
lock_->lock();
}
~WriteHolder() {
......@@ -202,6 +219,8 @@ class TimedRWMutexImpl {
bool shouldReadersWait() const;
void unlock_();
// Different states the lock can be in
enum class State {
UNLOCKED,
......
......@@ -2222,7 +2222,7 @@ TEST(TimedMutex, ThreadFiberDeadlockOrder) {
fm.addTask([&] { std::lock_guard<TimedMutex> lg(mutex); });
fm.addTask([&] {
runInMainContext([&] {
auto locked = mutex.timed_lock(std::chrono::seconds{1});
auto locked = mutex.try_lock_for(std::chrono::seconds{1});
EXPECT_TRUE(locked);
if (locked) {
mutex.unlock();
......@@ -2244,7 +2244,7 @@ TEST(TimedMutex, ThreadFiberDeadlockRace) {
mutex.lock();
fm.addTask([&] {
auto locked = mutex.timed_lock(std::chrono::seconds{1});
auto locked = mutex.try_lock_for(std::chrono::seconds{1});
EXPECT_TRUE(locked);
if (locked) {
mutex.unlock();
......@@ -2253,7 +2253,7 @@ TEST(TimedMutex, ThreadFiberDeadlockRace) {
fm.addTask([&] {
mutex.unlock();
runInMainContext([&] {
auto locked = mutex.timed_lock(std::chrono::seconds{1});
auto locked = mutex.try_lock_for(std::chrono::seconds{1});
EXPECT_TRUE(locked);
if (locked) {
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