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 {
18 public:
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
38 private:
39 const Data* GetOnRvalue() {
40 static_assert(!sizeof(Data),
41 "Don't use temporary LockedPtr, store it to a variable");
42 std::abort();
43 }
44
45 Lock lock_;
46 Data& data_;
47};
48
49/// @ingroup userver_concurrency userver_containers
50///
51/// Container for shared data protected with a mutex of any type
52/// (mutex, shared mutex, etc.).
53/// ## Example usage:
54///
55/// @snippet concurrent/variable_test.cpp Sample concurrent::Variable usage
56///
57/// @see @ref scripts/docs/en/userver/synchronization.md
58template <typename Data, typename Mutex = engine::Mutex>
59class Variable final {
60 public:
61 template <typename... Arg>
62 Variable(Arg&&... arg) : data_(std::forward<Arg>(arg)...) {}
63
64 LockedPtr<std::unique_lock<Mutex>, Data> UniqueLock() {
65 return {mutex_, data_};
66 }
67
68 LockedPtr<std::unique_lock<Mutex>, const Data> UniqueLock() const {
69 return {mutex_, data_};
70 }
71
72 std::optional<LockedPtr<std::unique_lock<Mutex>, const Data>> UniqueLock(
73 std::try_to_lock_t) const {
74 return DoUniqueLock(*this, std::try_to_lock);
75 }
76
77 std::optional<LockedPtr<std::unique_lock<Mutex>, Data>> UniqueLock(
78 std::try_to_lock_t) {
79 return DoUniqueLock(*this, std::try_to_lock);
80 }
81
82 std::optional<LockedPtr<std::unique_lock<Mutex>, const Data>> UniqueLock(
83 std::chrono::milliseconds try_duration) const {
84 return DoUniqueLock(*this, try_duration);
85 }
86
87 std::optional<LockedPtr<std::unique_lock<Mutex>, Data>> UniqueLock(
88 std::chrono::milliseconds try_duration) {
89 return DoUniqueLock(*this, try_duration);
90 }
91
92 LockedPtr<std::shared_lock<Mutex>, const Data> SharedLock() const {
93 return {mutex_, data_};
94 }
95
96 LockedPtr<std::lock_guard<Mutex>, Data> Lock() { return {mutex_, data_}; }
97
98 LockedPtr<std::lock_guard<Mutex>, const Data> Lock() const {
99 return {mutex_, data_};
100 }
101
102 /// Get raw mutex. Use with caution. For simple use cases call Lock(),
103 /// UniqueLock(), SharedLock() instead.
104 Mutex& GetMutexUnsafe() const { return mutex_; }
105
106 /// Get raw data. Use with extreme caution, only for cases where it is
107 /// impossible to access data with safe methods (e.g. std::scoped_lock with
108 /// multiple mutexes). For simple use cases call Lock(), UniqueLock(),
109 /// SharedLock() instead.
110 Data& GetDataUnsafe() { return data_; }
111
112 const Data& GetDataUnsafe() const { return data_; }
113
114 private:
115 mutable Mutex mutex_;
116 Data data_;
117
118 /// We need this function to work around const/non-const methods. This
119 /// helper accepts concurrent variables as template parameter and thus
120 /// will accept both const and non-const vars. And Data typename will
121 /// resolve to const/non-const accordingly.
122 template <typename VariableType, typename... StdUniqueLockArgs>
123 static auto DoUniqueLock(VariableType& concurrent_variable,
124 StdUniqueLockArgs&&... args) {
125 std::unique_lock<Mutex> lock(concurrent_variable.mutex_,
126 std::forward<StdUniqueLockArgs>(args)...);
127 return lock ? std::optional{LockedPtr{std::move(lock),
128 concurrent_variable.data_}}
129 : std::nullopt;
130 }
131};
132
133} // namespace concurrent
134
135USERVER_NAMESPACE_END