userver: userver/concurrent/variable.hpp Source File
Loading...
Searching...
No Matches
variable.hpp
1#pragma once
2
3#include <cstdlib>
4#include <mutex>
5#include <optional>
6#include <shared_mutex> // for shared_lock
7
8#include <userver/engine/mutex.hpp>
9
10USERVER_NAMESPACE_BEGIN
11
12/// Locking stuff
13namespace concurrent {
14
15/// Proxy class for locked access to data protected with locking::SharedLock<T>
16template <typename Lock, typename Data>
17class LockedPtr final {
18public:
19 using Mutex = typename Lock::mutex_type;
20
21 LockedPtr(Mutex& mutex, Data& data) : lock_(mutex), data_(data) {}
22 LockedPtr(Lock&& lock, Data& data) : lock_(std::move(lock)), data_(data) {}
23
24 Data& operator*() & { return data_; }
25 const Data& operator*() const& { return data_; }
26
27 /// Don't use *tmp for temporary value, store it to variable.
28 Data& operator*() && { return *GetOnRvalue(); }
29
30 Data* operator->() & { return &data_; }
31 const Data* operator->() const& { return &data_; }
32
33 /// Don't use tmp-> for temporary value, store it to variable.
34 Data* operator->() && { return GetOnRvalue(); }
35
36 Lock& GetLock() { return lock_; }
37
38private:
39 const Data* GetOnRvalue() {
40 static_assert(!sizeof(Data), "Don't use temporary LockedPtr, store it to a variable");
41 std::abort();
42 }
43
44 Lock lock_;
45 Data& data_;
46};
47
48/// @ingroup userver_concurrency userver_containers
49///
50/// Container for shared data protected with a mutex of any type
51/// (mutex, shared mutex, etc.).
52/// ## Example usage:
53///
54/// @snippet concurrent/variable_test.cpp Sample concurrent::Variable usage
55///
56/// @see @ref scripts/docs/en/userver/synchronization.md
57template <typename Data, typename Mutex = engine::Mutex>
58class Variable final {
59public:
60 template <typename... Arg>
61 Variable(Arg&&... arg) : data_(std::forward<Arg>(arg)...) {}
62
63 LockedPtr<std::unique_lock<Mutex>, Data> UniqueLock() { return {mutex_, data_}; }
64
65 LockedPtr<std::unique_lock<Mutex>, const Data> UniqueLock() const { return {mutex_, data_}; }
66
67 std::optional<LockedPtr<std::unique_lock<Mutex>, const Data>> UniqueLock(std::try_to_lock_t) const {
68 return DoUniqueLock(*this, std::try_to_lock);
69 }
70
71 std::optional<LockedPtr<std::unique_lock<Mutex>, Data>> UniqueLock(std::try_to_lock_t) {
72 return DoUniqueLock(*this, std::try_to_lock);
73 }
74
75 std::optional<LockedPtr<std::unique_lock<Mutex>, const Data>> UniqueLock(std::chrono::milliseconds try_duration
76 ) const {
77 return DoUniqueLock(*this, try_duration);
78 }
79
80 std::optional<LockedPtr<std::unique_lock<Mutex>, Data>> UniqueLock(std::chrono::milliseconds try_duration) {
81 return DoUniqueLock(*this, try_duration);
82 }
83
84 LockedPtr<std::shared_lock<Mutex>, const Data> SharedLock() const { return {mutex_, data_}; }
85
86 /// Useful for grabbing a reference to an object in a node-based container,
87 /// e.g. `std::unordered_map`. Values must support concurrent modification.
88 LockedPtr<std::shared_lock<Mutex>, Data> SharedMutableLockUnsafe() { return {mutex_, data_}; }
89
90 LockedPtr<std::lock_guard<Mutex>, Data> Lock() { return {mutex_, data_}; }
91
92 LockedPtr<std::lock_guard<Mutex>, const Data> Lock() const { return {mutex_, data_}; }
93
94 /// Get raw mutex. Use with caution. For simple use cases call Lock(),
95 /// UniqueLock(), SharedLock() instead.
96 Mutex& GetMutexUnsafe() const { return mutex_; }
97
98 /// Get raw data. Use with extreme caution, only for cases where it is
99 /// impossible to access data with safe methods (e.g. std::scoped_lock with
100 /// multiple mutexes). For simple use cases call Lock(), UniqueLock(),
101 /// SharedLock() instead.
102 Data& GetDataUnsafe() { return data_; }
103
104 const Data& GetDataUnsafe() const { return data_; }
105
106private:
107 mutable Mutex mutex_;
108 Data data_;
109
110 /// We need this function to work around const/non-const methods. This
111 /// helper accepts concurrent variables as template parameter and thus
112 /// will accept both const and non-const vars. And Data typename will
113 /// resolve to const/non-const accordingly.
114 template <typename VariableType, typename... StdUniqueLockArgs>
115 static auto DoUniqueLock(VariableType& concurrent_variable, StdUniqueLockArgs&&... args) {
116 std::unique_lock<Mutex> lock(concurrent_variable.mutex_, std::forward<StdUniqueLockArgs>(args)...);
117 return lock ? std::optional{LockedPtr{std::move(lock), concurrent_variable.data_}} : std::nullopt;
118 }
119};
120
121} // namespace concurrent
122
123USERVER_NAMESPACE_END