1 | //===-- Predicate.h ---------------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLDB_UTILITY_PREDICATE_H |
10 | #define LLDB_UTILITY_PREDICATE_H |
11 | |
12 | #include <cstdint> |
13 | #include <ctime> |
14 | |
15 | #include <condition_variable> |
16 | #include <mutex> |
17 | #include <optional> |
18 | |
19 | #include "lldb/Utility/Timeout.h" |
20 | #include "lldb/lldb-defines.h" |
21 | |
22 | //#define DB_PTHREAD_LOG_EVENTS |
23 | |
24 | /// Enumerations for broadcasting. |
25 | namespace lldb_private { |
26 | |
27 | enum PredicateBroadcastType { |
28 | eBroadcastNever, ///< No broadcast will be sent when the value is modified. |
29 | eBroadcastAlways, ///< Always send a broadcast when the value is modified. |
30 | eBroadcastOnChange ///< Only broadcast if the value changes when the value is |
31 | /// modified. |
32 | }; |
33 | |
34 | /// \class Predicate Predicate.h "lldb/Utility/Predicate.h" |
35 | /// A C++ wrapper class for providing threaded access to a value of |
36 | /// type T. |
37 | /// |
38 | /// A templatized class that provides multi-threaded access to a value |
39 | /// of type T. Threads can efficiently wait for bits within T to be set |
40 | /// or reset, or wait for T to be set to be equal/not equal to a |
41 | /// specified values. |
42 | template <class T> class Predicate { |
43 | public: |
44 | /// Default constructor. |
45 | /// |
46 | /// Initializes the mutex, condition and value with their default |
47 | /// constructors. |
48 | Predicate() : m_value() {} |
49 | |
50 | /// Construct with initial T value \a initial_value. |
51 | /// |
52 | /// Initializes the mutex and condition with their default |
53 | /// constructors, and initializes the value with \a initial_value. |
54 | /// |
55 | /// \param[in] initial_value |
56 | /// The initial value for our T object. |
57 | Predicate(T initial_value) : m_value(initial_value) {} |
58 | |
59 | /// Destructor. |
60 | /// |
61 | /// Destroy the condition, mutex, and T objects. |
62 | ~Predicate() = default; |
63 | |
64 | /// Value get accessor. |
65 | /// |
66 | /// Copies the current \a m_value in a thread safe manor and returns |
67 | /// the copied value. |
68 | /// |
69 | /// \return |
70 | /// A copy of the current value. |
71 | T GetValue() const { |
72 | std::lock_guard<std::mutex> guard(m_mutex); |
73 | T value = m_value; |
74 | return value; |
75 | } |
76 | |
77 | /// Value set accessor. |
78 | /// |
79 | /// Set the contained \a m_value to \a new_value in a thread safe |
80 | /// way and broadcast if needed. |
81 | /// |
82 | /// \param[in] value |
83 | /// The new value to set. |
84 | /// |
85 | /// \param[in] broadcast_type |
86 | /// A value indicating when and if to broadcast. See the |
87 | /// PredicateBroadcastType enumeration for details. |
88 | /// |
89 | /// \see Predicate::Broadcast() |
90 | void SetValue(T value, PredicateBroadcastType broadcast_type) { |
91 | std::lock_guard<std::mutex> guard(m_mutex); |
92 | #ifdef DB_PTHREAD_LOG_EVENTS |
93 | printf("%s (value = 0x%8.8x, broadcast_type = %i)\n" , __FUNCTION__, value, |
94 | broadcast_type); |
95 | #endif |
96 | const T old_value = m_value; |
97 | m_value = value; |
98 | |
99 | Broadcast(old_value, broadcast_type); |
100 | } |
101 | |
102 | /// Wait for Cond(m_value) to be true. |
103 | /// |
104 | /// Waits in a thread safe way for Cond(m_value) to be true. If Cond(m_value) |
105 | /// is already true, this function will return without waiting. |
106 | /// |
107 | /// It is possible for the value to be changed between the time the value is |
108 | /// set and the time the waiting thread wakes up. If the value no longer |
109 | /// satisfies the condition when the waiting thread wakes up, it will go back |
110 | /// into a wait state. It may be necessary for the calling code to use |
111 | /// additional thread synchronization methods to detect transitory states. |
112 | /// |
113 | /// \param[in] Cond |
114 | /// The condition we want \a m_value satisfy. |
115 | /// |
116 | /// \param[in] timeout |
117 | /// How long to wait for the condition to hold. |
118 | /// |
119 | /// \return |
120 | /// m_value if Cond(m_value) is true, std::nullopt otherwise (timeout |
121 | /// occurred). |
122 | template <typename C> |
123 | std::optional<T> WaitFor(C Cond, const Timeout<std::micro> &timeout) { |
124 | std::unique_lock<std::mutex> lock(m_mutex); |
125 | auto RealCond = [&] { return Cond(m_value); }; |
126 | if (!timeout) { |
127 | m_condition.wait(lock, RealCond); |
128 | return m_value; |
129 | } |
130 | if (m_condition.wait_for(lock, *timeout, RealCond)) |
131 | return m_value; |
132 | return std::nullopt; |
133 | } |
134 | /// Wait for \a m_value to be equal to \a value. |
135 | /// |
136 | /// Waits in a thread safe way for \a m_value to be equal to \a |
137 | /// value. If \a m_value is already equal to \a value, this |
138 | /// function will return without waiting. |
139 | /// |
140 | /// It is possible for the value to be changed between the time |
141 | /// the value is set and the time the waiting thread wakes up. |
142 | /// If the value no longer matches the requested value when the |
143 | /// waiting thread wakes up, it will go back into a wait state. It |
144 | /// may be necessary for the calling code to use additional thread |
145 | /// synchronization methods to detect transitory states. |
146 | /// |
147 | /// \param[in] value |
148 | /// The value we want \a m_value to be equal to. |
149 | /// |
150 | /// \param[in] timeout |
151 | /// How long to wait for the condition to hold. |
152 | /// |
153 | /// \return |
154 | /// true if the \a m_value is equal to \a value, false otherwise (timeout |
155 | /// occurred). |
156 | bool WaitForValueEqualTo(T value, |
157 | const Timeout<std::micro> &timeout = std::nullopt) { |
158 | return WaitFor([&value](T current) { return value == current; }, timeout) != |
159 | std::nullopt; |
160 | } |
161 | |
162 | /// Wait for \a m_value to not be equal to \a value. |
163 | /// |
164 | /// Waits in a thread safe way for \a m_value to not be equal to \a |
165 | /// value. If \a m_value is already not equal to \a value, this |
166 | /// function will return without waiting. |
167 | /// |
168 | /// It is possible for the value to be changed between the time |
169 | /// the value is set and the time the waiting thread wakes up. |
170 | /// If the value is equal to the test value when the waiting thread |
171 | /// wakes up, it will go back into a wait state. It may be |
172 | /// necessary for the calling code to use additional thread |
173 | /// synchronization methods to detect transitory states. |
174 | /// |
175 | /// \param[in] value |
176 | /// The value we want \a m_value to not be equal to. |
177 | /// |
178 | /// \param[in] timeout |
179 | /// How long to wait for the condition to hold. |
180 | /// |
181 | /// \return |
182 | /// m_value if m_value != value, std::nullopt otherwise (timeout |
183 | /// occurred). |
184 | std::optional<T> |
185 | WaitForValueNotEqualTo(T value, |
186 | const Timeout<std::micro> &timeout = std::nullopt) { |
187 | return WaitFor([&value](T current) { return value != current; }, timeout); |
188 | } |
189 | |
190 | protected: |
191 | // pthread condition and mutex variable to control access and allow blocking |
192 | // between the main thread and the spotlight index thread. |
193 | T m_value; ///< The templatized value T that we are protecting access to |
194 | mutable std::mutex m_mutex; ///< The mutex to use when accessing the data |
195 | std::condition_variable m_condition; ///< The pthread condition variable to |
196 | /// use for signaling that data available |
197 | /// or changed. |
198 | |
199 | private: |
200 | /// Broadcast if needed. |
201 | /// |
202 | /// Check to see if we need to broadcast to our condition variable |
203 | /// depending on the \a old_value and on the \a broadcast_type. |
204 | /// |
205 | /// If \a broadcast_type is eBroadcastNever, no broadcast will be |
206 | /// sent. |
207 | /// |
208 | /// If \a broadcast_type is eBroadcastAlways, the condition variable |
209 | /// will always be broadcast. |
210 | /// |
211 | /// If \a broadcast_type is eBroadcastOnChange, the condition |
212 | /// variable be broadcast if the owned value changes. |
213 | void Broadcast(T old_value, PredicateBroadcastType broadcast_type) { |
214 | bool broadcast = |
215 | (broadcast_type == eBroadcastAlways) || |
216 | ((broadcast_type == eBroadcastOnChange) && old_value != m_value); |
217 | #ifdef DB_PTHREAD_LOG_EVENTS |
218 | printf("%s (old_value = 0x%8.8x, broadcast_type = %i) m_value = 0x%8.8x, " |
219 | "broadcast = %u\n" , |
220 | __FUNCTION__, old_value, broadcast_type, m_value, broadcast); |
221 | #endif |
222 | if (broadcast) |
223 | m_condition.notify_all(); |
224 | } |
225 | |
226 | Predicate(const Predicate &) = delete; |
227 | const Predicate &operator=(const Predicate &) = delete; |
228 | }; |
229 | |
230 | } // namespace lldb_private |
231 | |
232 | #endif // LLDB_UTILITY_PREDICATE_H |
233 | |