| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef COBALT_UTIL_PROTECTED_FIELDS_H_ |
| #define COBALT_UTIL_PROTECTED_FIELDS_H_ |
| |
| #include <chrono> |
| #include <condition_variable> |
| #include <mutex> |
| |
| namespace cobalt { |
| namespace util { |
| |
| // ProtectedFields is a useful abstraction for having an object that is |
| // protected by a mutex. |
| // |
| // Example usage: |
| // |
| // struct SafeField { |
| // int protected_int; |
| // }; |
| // ProtectedFields<SafeField> protected_fields; |
| // protected_fields.lock()->protected_int = 100; |
| // LOG(INFO) << "Current protected_int: " << |
| // protected_fields.const_lock()->protected_int; |
| // |
| template <class Fields> |
| class ProtectedFields { |
| public: |
| // ConstLockedFieldsPtr holds a pointer to Fields, as well a |
| // unique_lock<mutex>. |
| // |
| // The semantics of this object is similar to a pointer. |
| class LockedFieldsPtr { |
| public: |
| Fields* operator->() { return fields_; } |
| Fields& operator*() { return *fields_; } |
| |
| // Calls the wait() method of a condition variable with a reference to the |
| // mutex. |
| void wait_with(std::condition_variable* cv) { return cv->wait(lock_); } |
| |
| template <class Predicate> |
| void wait_with(std::condition_variable* cv, Predicate pred) { |
| return cv->wait(lock_, pred); |
| } |
| |
| // Calls the wait_for() method of a condition variable with a reference to |
| // the mutex. |
| template <class Rep, class Period> |
| std::cv_status wait_for_with( |
| std::condition_variable* cv, |
| const std::chrono::duration<Rep, Period>& rel_time) { |
| return cv->wait(lock_, rel_time); |
| } |
| |
| template <class Rep, class Period, class Predicate> |
| bool wait_for_with(std::condition_variable* cv, |
| const std::chrono::duration<Rep, Period>& rel_time, |
| Predicate pred) { |
| return cv->wait_for(lock_, rel_time, pred); |
| } |
| |
| // Calls the wait_until() method of a condition variable with a reference to |
| // the mutex. |
| template <class Clock, class Duration> |
| std::cv_status wait_until_with( |
| std::condition_variable* cv, |
| const std::chrono::time_point<Clock, Duration>& timeout_time) { |
| return cv->wait_until(lock_, timeout_time); |
| } |
| |
| template <class Clock, class Duration, class Predicate> |
| bool wait_until_with( |
| std::condition_variable* cv, |
| const std::chrono::time_point<Clock, Duration>& timeout_time, |
| Predicate pred) { |
| return cv->wait_until(lock_, timeout_time, pred); |
| } |
| |
| private: |
| friend class ProtectedFields; |
| LockedFieldsPtr(std::mutex* mutex, Fields* fields) |
| : lock_(*mutex), fields_(fields) {} |
| |
| std::unique_lock<std::mutex> lock_; |
| Fields* fields_; |
| |
| public: |
| // Disable copy/assign. Only allow move. |
| LockedFieldsPtr(LockedFieldsPtr&&); |
| LockedFieldsPtr& operator=(LockedFieldsPtr&&); |
| LockedFieldsPtr& operator=(const LockedFieldsPtr&) = delete; |
| LockedFieldsPtr(const LockedFieldsPtr&) = delete; |
| }; |
| |
| // ConstLockedFieldsPtr holds a const pointer to Fields, as well a |
| // unique_lock<mutex>. |
| // |
| // The semantics of this object is similar to a const pointer. |
| class ConstLockedFieldsPtr { |
| public: |
| const Fields* operator->() { return fields_; } |
| const Fields& operator*() { return *fields_; } |
| |
| // Calls the wait() method of a condition variable with a reference to the |
| // mutex. |
| void wait_with(std::condition_variable* cv) { return cv->wait(lock_); } |
| |
| template <class Predicate> |
| void wait_with(std::condition_variable* cv, Predicate pred) { |
| return cv->wait(lock_, pred); |
| } |
| |
| // Calls the wait_for() method of a condition variable with a reference to |
| // the mutex. |
| template <class Rep, class Period> |
| std::cv_status wait_for_with( |
| std::condition_variable* cv, |
| const std::chrono::duration<Rep, Period>& rel_time) { |
| return cv->wait(lock_, rel_time); |
| } |
| |
| template <class Rep, class Period, class Predicate> |
| bool wait_for_with(std::condition_variable* cv, |
| const std::chrono::duration<Rep, Period>& rel_time, |
| Predicate pred) { |
| return cv->wait_for(lock_, rel_time, pred); |
| } |
| |
| // Calls the wait_until() method of a condition variable with a reference to |
| // the mutex. |
| template <class Clock, class Duration> |
| std::cv_status wait_until_with( |
| std::condition_variable* cv, |
| const std::chrono::time_point<Clock, Duration>& timeout_time) { |
| return cv->wait_until(lock_, timeout_time); |
| } |
| |
| template <class Clock, class Duration, class Predicate> |
| bool wait_until_with( |
| std::condition_variable* cv, |
| const std::chrono::time_point<Clock, Duration>& timeout_time, |
| Predicate pred) { |
| return cv->wait_until(lock_, timeout_time, pred); |
| } |
| |
| private: |
| friend class ProtectedFields; |
| ConstLockedFieldsPtr(std::mutex* mutex, const Fields* fields) |
| : lock_(*mutex), fields_(fields) {} |
| |
| std::unique_lock<std::mutex> lock_; |
| const Fields* fields_; |
| |
| public: |
| // Disable copy/assign. Only allow move. |
| ConstLockedFieldsPtr(ConstLockedFieldsPtr&&); |
| ConstLockedFieldsPtr& operator=(ConstLockedFieldsPtr&&); |
| ConstLockedFieldsPtr& operator=(const ConstLockedFieldsPtr&) = delete; |
| ConstLockedFieldsPtr(const ConstLockedFieldsPtr&) = delete; |
| }; |
| |
| LockedFieldsPtr lock() { return LockedFieldsPtr(&mutex_, &fields_); } |
| ConstLockedFieldsPtr const_lock() const { |
| return ConstLockedFieldsPtr(&mutex_, &fields_); |
| } |
| |
| private: |
| mutable std::mutex mutex_; |
| Fields fields_; |
| |
| public: |
| ProtectedFields& operator=(const ProtectedFields&) = delete; |
| ProtectedFields(const ProtectedFields&) = delete; |
| ProtectedFields() {} |
| }; |
| |
| } // namespace util |
| } // namespace cobalt |
| |
| #endif // COBALT_UTIL_PROTECTED_FIELDS_H_ |