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