userver: userver/concurrent/variable.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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