aboutsummaryrefslogtreecommitdiff
path: root/include/lldb/Host/Predicate.h
blob: 6ddf20b67c69c64ca7e3e968ac7c8c4f55491109 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
//===-- Predicate.h ---------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#ifndef liblldb_Predicate_h_
#define liblldb_Predicate_h_
#if defined(__cplusplus)

#include "lldb/Host/Mutex.h"
#include "lldb/Host/Condition.h"
#include <stdint.h>
#include <time.h>

//#define DB_PTHREAD_LOG_EVENTS

//----------------------------------------------------------------------
/// Enumerations for broadcasting.
//----------------------------------------------------------------------
namespace lldb_private {

typedef enum
{
    eBroadcastNever,    ///< No broadcast will be sent when the value is modified.
    eBroadcastAlways,   ///< Always send a broadcast when the value is modified.
    eBroadcastOnChange  ///< Only broadcast if the value changes when the value is modified.

} PredicateBroadcastType;

//----------------------------------------------------------------------
/// @class Predicate Predicate.h "lldb/Host/Predicate.h"
/// @brief A C++ wrapper class for providing threaded access to a value
/// of type T.
///
/// A templatized class that provides multi-threaded access to a value
/// of type T. Threads can efficiently wait for bits within T to be set
/// or reset, or wait for T to be set to be equal/not equal to a
/// specified values.
//----------------------------------------------------------------------
template <class T>
class Predicate
{
public:

    //------------------------------------------------------------------
    /// Default constructor.
    ///
    /// Initializes the mutex, condition and value with their default
    /// constructors.
    //------------------------------------------------------------------
    Predicate () :
        m_value(),
        m_mutex(),
        m_condition()
    {
    }

    //------------------------------------------------------------------
    /// Construct with initial T value \a initial_value.
    ///
    /// Initializes the mutex and condition with their default
    /// constructors, and initializes the value with \a initial_value.
    ///
    /// @param[in] initial_value
    ///     The initial value for our T object.
    //------------------------------------------------------------------
    Predicate (T initial_value)  :
        m_value(initial_value),
        m_mutex(),
        m_condition()
    {
    }

    //------------------------------------------------------------------
    /// Destructor.
    ///
    /// Destrory the condition, mutex, and T objects.
    //------------------------------------------------------------------
    ~Predicate ()
    {
    }


    //------------------------------------------------------------------
    /// Value get accessor.
    ///
    /// Copies the current \a m_value in a thread safe manor and returns
    /// the copied value.
    ///
    /// @return
    ///     A copy of the current value.
    //------------------------------------------------------------------
    T
    GetValue () const
    {
        Mutex::Locker locker(m_mutex);
        T value = m_value;
        return value;
    }

    //------------------------------------------------------------------
    /// Value set accessor.
    ///
    /// Set the contained \a m_value to \a new_value in a thread safe
    /// way and broadcast if needed.
    ///
    /// @param[in] value
    ///     The new value to set.
    ///
    /// @param[in] broadcast_type
    ///     A value indicating when and if to broadast. See the
    ///     PredicateBroadcastType enumeration for details.
    ///
    /// @see Predicate::Broadcast()
    //------------------------------------------------------------------
    void
    SetValue (T value, PredicateBroadcastType broadcast_type)
    {
        Mutex::Locker locker(m_mutex);
#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (value = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, value, broadcast_type);
#endif
        const T old_value = m_value;
        m_value = value;

        Broadcast(old_value, broadcast_type);
    }

    //------------------------------------------------------------------
    /// Set some bits in \a m_value.
    ///
    /// Logically set the bits \a bits in the contained \a m_value in a
    /// thread safe way and broadcast if needed.
    ///
    /// @param[in] bits
    ///     The bits to set in \a m_value.
    ///
    /// @param[in] broadcast_type
    ///     A value indicating when and if to broadast. See the
    ///     PredicateBroadcastType enumeration for details.
    ///
    /// @see Predicate::Broadcast()
    //------------------------------------------------------------------
    void
    SetValueBits (T bits, PredicateBroadcastType broadcast_type)
    {
        Mutex::Locker locker(m_mutex);
#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (bits = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, bits, broadcast_type);
#endif
        const T old_value = m_value;
        m_value |= bits;

        Broadcast(old_value, broadcast_type);
    }

    //------------------------------------------------------------------
    /// Reset some bits in \a m_value.
    ///
    /// Logically reset (clear) the bits \a bits in the contained
    /// \a m_value in a thread safe way and broadcast if needed.
    ///
    /// @param[in] bits
    ///     The bits to clear in \a m_value.
    ///
    /// @param[in] broadcast_type
    ///     A value indicating when and if to broadast. See the
    ///     PredicateBroadcastType enumeration for details.
    ///
    /// @see Predicate::Broadcast()
    //------------------------------------------------------------------
    void
    ResetValueBits (T bits, PredicateBroadcastType broadcast_type)
    {
        Mutex::Locker locker(m_mutex);
#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (bits = 0x%8.8x, broadcast_type = %i)\n", __FUNCTION__, bits, broadcast_type);
#endif
        const T old_value = m_value;
        m_value &= ~bits;

        Broadcast(old_value, broadcast_type);
    }

    //------------------------------------------------------------------
    /// Wait for bits to be set in \a m_value.
    ///
    /// Waits in a thread safe way for any bits in \a bits to get
    /// logically set in \a m_value. If any bits are already set in
    /// \a m_value, this function will return without waiting.
    ///
    /// It is possible for the value to be changed between the time
    /// the bits are set and the time the waiting thread wakes up.
    /// If the bits are no longer set when the waiting thread wakes
    /// up, it will go back into a wait state.  It may be necessary
    /// for the calling code to use additional thread synchronization
    /// methods to detect transitory states.
    ///
    /// @param[in] bits
    ///     The bits we are waiting to be set in \a m_value.
    ///
    /// @param[in] abstime
    ///     If non-NULL, the absolute time at which we should stop
    ///     waiting, else wait an infinite amount of time.
    ///
    /// @return
    ///     Any bits of the requested bits that actually were set within
    ///     the time specified. Zero if a timeout or unrecoverable error
    ///     occurred.
    //------------------------------------------------------------------
    T
    WaitForSetValueBits (T bits, const TimeValue *abstime = NULL)
    {
        int err = 0;
        // pthread_cond_timedwait() or pthread_cond_wait() will atomically
        // unlock the mutex and wait for the condition to be set. When either
        // function returns, they will re-lock the mutex. We use an auto lock/unlock
        // class (Mutex::Locker) to allow us to return at any point in this
        // function and not have to worry about unlocking the mutex.
        Mutex::Locker locker(m_mutex);
#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (bits = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, bits, abstime, m_value);
#endif
        while (err == 0 && ((m_value & bits) == 0))
        {
            err = m_condition.Wait (m_mutex, abstime);
        }
#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (bits = 0x%8.8x), m_value = 0x%8.8x, returning 0x%8.8x\n", __FUNCTION__, bits, m_value, m_value & bits);
#endif

        return m_value & bits;
    }

    //------------------------------------------------------------------
    /// Wait for bits to be reset in \a m_value.
    ///
    /// Waits in a thread safe way for any bits in \a bits to get
    /// logically reset in \a m_value. If all bits are already reset in
    /// \a m_value, this function will return without waiting.
    ///
    /// It is possible for the value to be changed between the time
    /// the bits are reset and the time the waiting thread wakes up.
    /// If the bits are no set when the waiting thread wakes up, it will
    /// go back into a wait state.  It may be necessary for the calling
    /// code to use additional thread synchronization methods to detect
    /// transitory states.
    ///
    /// @param[in] bits
    ///     The bits we are waiting to be reset in \a m_value.
    ///
    /// @param[in] abstime
    ///     If non-NULL, the absolute time at which we should stop
    ///     waiting, else wait an infinite amount of time.
    ///
    /// @return
    ///     Zero on successful waits, or non-zero if a timeout or
    ///     unrecoverable error occurs.
    //------------------------------------------------------------------
    T
    WaitForResetValueBits (T bits, const TimeValue *abstime = NULL)
    {
        int err = 0;

        // pthread_cond_timedwait() or pthread_cond_wait() will atomically
        // unlock the mutex and wait for the condition to be set. When either
        // function returns, they will re-lock the mutex. We use an auto lock/unlock
        // class (Mutex::Locker) to allow us to return at any point in this
        // function and not have to worry about unlocking the mutex.
        Mutex::Locker locker(m_mutex);

#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (bits = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, bits, abstime, m_value);
#endif
        while (err == 0 && ((m_value & bits) != 0))
        {
            err = m_condition.Wait (m_mutex, abstime);
        }

#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (bits = 0x%8.8x), m_value = 0x%8.8x, returning 0x%8.8x\n", __FUNCTION__, bits, m_value, m_value & bits);
#endif
        return m_value & bits;
    }

    //------------------------------------------------------------------
    /// Wait for \a m_value to be equal to \a value.
    ///
    /// Waits in a thread safe way for \a m_value to be equal to \a
    /// value. If \a m_value is already equal to \a value, this
    /// function will return without waiting.
    ///
    /// It is possible for the value to be changed between the time
    /// the value is set and the time the waiting thread wakes up.
    /// If the value no longer matches the requested value when the
    /// waiting thread wakes up, it will go back into a wait state.  It
    /// may be necessary for the calling code to use additional thread
    /// synchronization methods to detect transitory states.
    ///
    /// @param[in] value
    ///     The value we want \a m_value to be equal to.
    ///
    /// @param[in] abstime
    ///     If non-NULL, the absolute time at which we should stop
    ///     waiting, else wait an infinite amount of time.
    ///
    /// @param[out] timed_out
    ///     If not null, set to true if we return because of a time out,
    ///     and false if the value was set.
    ///
    /// @return
    ///     @li \b true if the \a m_value is equal to \a value
    ///     @li \b false otherwise
    //------------------------------------------------------------------
    bool
    WaitForValueEqualTo (T value, const TimeValue *abstime = NULL, bool *timed_out = NULL)
    {
        int err = 0;
        // pthread_cond_timedwait() or pthread_cond_wait() will atomically
        // unlock the mutex and wait for the condition to be set. When either
        // function returns, they will re-lock the mutex. We use an auto lock/unlock
        // class (Mutex::Locker) to allow us to return at any point in this
        // function and not have to worry about unlocking the mutex.
        Mutex::Locker locker(m_mutex);

#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, value, abstime, m_value);
#endif
        if (timed_out)
            *timed_out = false;

        while (err == 0 && m_value != value)
        {
            err = m_condition.Wait (m_mutex, abstime, timed_out);
        }

        return m_value == value;
    }

    //------------------------------------------------------------------
    /// Wait for \a m_value to be equal to \a value and then set it to
    /// a new value.
    ///
    /// Waits in a thread safe way for \a m_value to be equal to \a
    /// value and then sets \a m_value to \a new_value. If \a m_value
    /// is already equal to \a value, this function will immediately
    /// set \a m_value to \a new_value and return without waiting.
    ///
    /// It is possible for the value to be changed between the time
    /// the value is set and the time the waiting thread wakes up.
    /// If the value no longer matches the requested value when the
    /// waiting thread wakes up, it will go back into a wait state.  It
    /// may be necessary for the calling code to use additional thread
    /// synchronization methods to detect transitory states.
    ///
    /// @param[in] value
    ///     The value we want \a m_value to be equal to.
    ///
    /// @param[in] new_value
    ///     The value to which \a m_value will be set if \b true is
    ///     returned.
    ///
    /// @param[in] abstime
    ///     If non-NULL, the absolute time at which we should stop
    ///     waiting, else wait an infinite amount of time.
    ///
    /// @param[out] timed_out
    ///     If not null, set to true if we return because of a time out,
    ///     and false if the value was set.
    ///
    /// @return
    ///     @li \b true if the \a m_value became equal to \a value
    ///     @li \b false otherwise
    //------------------------------------------------------------------
    bool
    WaitForValueEqualToAndSetValueTo (T wait_value, T new_value, const TimeValue *abstime = NULL, bool *timed_out = NULL)
    {
        int err = 0;
        // pthread_cond_timedwait() or pthread_cond_wait() will atomically
        // unlock the mutex and wait for the condition to be set. When either
        // function returns, they will re-lock the mutex. We use an auto lock/unlock
        // class (Mutex::Locker) to allow us to return at any point in this
        // function and not have to worry about unlocking the mutex.
        Mutex::Locker locker(m_mutex);

#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (wait_value = 0x%8.8x, new_value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, wait_value, new_value, abstime, m_value);
#endif
        if (timed_out)
            *timed_out = false;

        while (err == 0 && m_value != wait_value)
        {
            err = m_condition.Wait (m_mutex, abstime, timed_out);
        }

        if (m_value == wait_value)
        {
            m_value = new_value;
            return true;
        }

        return false;
    }


    //------------------------------------------------------------------
    /// Wait for \a m_value to not be equal to \a value.
    ///
    /// Waits in a thread safe way for \a m_value to not be equal to \a
    /// value. If \a m_value is already not equal to \a value, this
    /// function will return without waiting.
    ///
    /// It is possible for the value to be changed between the time
    /// the value is set and the time the waiting thread wakes up.
    /// If the value is equal to the test value when the waiting thread
    /// wakes up, it will go back into a wait state.  It may be
    /// necessary for the calling code to use additional thread
    /// synchronization methods to detect transitory states.
    ///
    /// @param[in] value
    ///     The value we want \a m_value to not be equal to.
    ///
    /// @param[out] new_value
    ///     The new value if \b true is returned.
    ///
    /// @param[in] abstime
    ///     If non-NULL, the absolute time at which we should stop
    ///     waiting, else wait an infinite amount of time.
    ///
    /// @return
    ///     @li \b true if the \a m_value is equal to \a value
    ///     @li \b false otherwise
    //------------------------------------------------------------------
    bool
    WaitForValueNotEqualTo (T value, T &new_value, const TimeValue *abstime = NULL)
    {
        int err = 0;
        // pthread_cond_timedwait() or pthread_cond_wait() will atomically
        // unlock the mutex and wait for the condition to be set. When either
        // function returns, they will re-lock the mutex. We use an auto lock/unlock
        // class (Mutex::Locker) to allow us to return at any point in this
        // function and not have to worry about unlocking the mutex.
        Mutex::Locker locker(m_mutex);
#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (value = 0x%8.8x, abstime = %p), m_value = 0x%8.8x\n", __FUNCTION__, value, abstime, m_value);
#endif
        while (err == 0 && m_value == value)
        {
            err = m_condition.Wait (m_mutex, abstime);
        }

        if (m_value != value)
        {
            new_value = m_value;
            return true;
        }
        return false;
    }

protected:
    //----------------------------------------------------------------------
    // pthread condition and mutex variable to controll access and allow
    // blocking between the main thread and the spotlight index thread.
    //----------------------------------------------------------------------
    T           m_value;        ///< The templatized value T that we are protecting access to
    mutable Mutex m_mutex;      ///< The mutex to use when accessing the data
    Condition   m_condition;    ///< The pthread condition variable to use for signaling that data available or changed.

private:

    //------------------------------------------------------------------
    /// Broadcast if needed.
    ///
    /// Check to see if we need to broadcast to our condition variable
    /// depedning on the \a old_value and on the \a broadcast_type.
    ///
    /// If \a broadcast_type is eBroadcastNever, no broadcast will be
    /// sent.
    ///
    /// If \a broadcast_type is eBroadcastAlways, the condition variable
    /// will always be broadcast.
    ///
    /// If \a broadcast_type is eBroadcastOnChange, the condition
    /// variable be broadcast if the owned value changes.
    //------------------------------------------------------------------
    void
    Broadcast (T old_value, PredicateBroadcastType broadcast_type)
    {
        bool broadcast = (broadcast_type == eBroadcastAlways) || ((broadcast_type == eBroadcastOnChange) && old_value != m_value);
#ifdef DB_PTHREAD_LOG_EVENTS
        printf("%s (old_value = 0x%8.8x, broadcast_type = %i) m_value = 0x%8.8x, broadcast = %u\n", __FUNCTION__, old_value, broadcast_type, m_value, broadcast);
#endif
        if (broadcast)
            m_condition.Broadcast();
    }


    DISALLOW_COPY_AND_ASSIGN(Predicate);
};

} // namespace lldb_private

#endif  // #if defined(__cplusplus)
#endif // #ifndef liblldb_Predicate_h_