1 //===-- PThreadEvent.cpp ----------------------------------------*- 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 //  Created by Greg Clayton on 6/16/07.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "PThreadEvent.h"
14 #include "DNBLog.h"
15 #include "errno.h"
16 
17 PThreadEvent::PThreadEvent(uint32_t bits, uint32_t validBits)
18     : m_mutex(), m_set_condition(), m_reset_condition(), m_bits(bits),
19       m_validBits(validBits), m_reset_ack_mask(0) {
20   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, 0x%8.8x)",
21   // this, __FUNCTION__, bits, validBits);
22 }
23 
24 PThreadEvent::~PThreadEvent() {
25   // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
26 }
27 
28 uint32_t PThreadEvent::NewEventBit() {
29   // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
30   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
31   uint32_t mask = 1;
32   while (mask & m_validBits)
33     mask <<= 1;
34   m_validBits |= mask;
35   return mask;
36 }
37 
38 void PThreadEvent::FreeEventBits(const uint32_t mask) {
39   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
40   // __FUNCTION__, mask);
41   if (mask) {
42     PTHREAD_MUTEX_LOCKER(locker, m_mutex);
43     m_bits &= ~mask;
44     m_validBits &= ~mask;
45   }
46 }
47 
48 uint32_t PThreadEvent::GetEventBits() const {
49   // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
50   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
51   uint32_t bits = m_bits;
52   return bits;
53 }
54 
55 // Replace the event bits with a new bitmask value
56 void PThreadEvent::ReplaceEventBits(const uint32_t bits) {
57   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
58   // __FUNCTION__, bits);
59   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
60   // Make sure we have some bits and that they aren't already set...
61   if (m_bits != bits) {
62     // Figure out which bits are changing
63     uint32_t changed_bits = m_bits ^ bits;
64     // Set the new bit values
65     m_bits = bits;
66     // If any new bits are set, then broadcast
67     if (changed_bits & m_bits)
68       m_set_condition.Broadcast();
69   }
70 }
71 
72 // Set one or more event bits and broadcast if any new event bits get set
73 // that weren't already set.
74 
75 void PThreadEvent::SetEvents(const uint32_t mask) {
76   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
77   // __FUNCTION__, mask);
78   // Make sure we have some bits to set
79   if (mask) {
80     PTHREAD_MUTEX_LOCKER(locker, m_mutex);
81     // Save the old event bit state so we can tell if things change
82     uint32_t old = m_bits;
83     // Set the all event bits that are set in 'mask'
84     m_bits |= mask;
85     // Broadcast only if any extra bits got set.
86     if (old != m_bits)
87       m_set_condition.Broadcast();
88   }
89 }
90 
91 // Reset one or more event bits
92 void PThreadEvent::ResetEvents(const uint32_t mask) {
93   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
94   // __FUNCTION__, mask);
95   if (mask) {
96     PTHREAD_MUTEX_LOCKER(locker, m_mutex);
97 
98     // Save the old event bit state so we can tell if things change
99     uint32_t old = m_bits;
100     // Clear the all event bits that are set in 'mask'
101     m_bits &= ~mask;
102     // Broadcast only if any extra bits got reset.
103     if (old != m_bits)
104       m_reset_condition.Broadcast();
105   }
106 }
107 
108 //----------------------------------------------------------------------
109 // Wait until 'timeout_abstime' for any events that are set in
110 // 'mask'. If 'timeout_abstime' is NULL, then wait forever.
111 //----------------------------------------------------------------------
112 uint32_t
113 PThreadEvent::WaitForSetEvents(const uint32_t mask,
114                                const struct timespec *timeout_abstime) const {
115   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
116   // __FUNCTION__, mask, timeout_abstime);
117   int err = 0;
118   // pthread_cond_timedwait() or pthread_cond_wait() will atomically
119   // unlock the mutex and wait for the condition to be set. When either
120   // function returns, they will re-lock the mutex. We use an auto lock/unlock
121   // class (PThreadMutex::Locker) to allow us to return at any point in this
122   // function and not have to worry about unlocking the mutex.
123   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
124   do {
125     // Check our predicate (event bits) in case any are already set
126     if (mask & m_bits) {
127       uint32_t bits_set = mask & m_bits;
128       // Our PThreadMutex::Locker will automatically unlock our mutex
129       return bits_set;
130     }
131     if (timeout_abstime) {
132       // Wait for condition to get broadcast, or for a timeout. If we get
133       // a timeout we will drop out of the do loop and return false which
134       // is what we want.
135       err = ::pthread_cond_timedwait(m_set_condition.Condition(),
136                                      m_mutex.Mutex(), timeout_abstime);
137       // Retest our predicate in case of a race condition right at the end
138       // of the timeout.
139       if (err == ETIMEDOUT) {
140         uint32_t bits_set = mask & m_bits;
141         return bits_set;
142       }
143     } else {
144       // Wait for condition to get broadcast. The only error this function
145       // should return is if
146       err = ::pthread_cond_wait(m_set_condition.Condition(), m_mutex.Mutex());
147     }
148   } while (err == 0);
149   return 0;
150 }
151 
152 //----------------------------------------------------------------------
153 // Wait until 'timeout_abstime' for any events in 'mask' to reset.
154 // If 'timeout_abstime' is NULL, then wait forever.
155 //----------------------------------------------------------------------
156 uint32_t PThreadEvent::WaitForEventsToReset(
157     const uint32_t mask, const struct timespec *timeout_abstime) const {
158   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
159   // __FUNCTION__, mask, timeout_abstime);
160   int err = 0;
161   // pthread_cond_timedwait() or pthread_cond_wait() will atomically
162   // unlock the mutex and wait for the condition to be set. When either
163   // function returns, they will re-lock the mutex. We use an auto lock/unlock
164   // class (PThreadMutex::Locker) to allow us to return at any point in this
165   // function and not have to worry about unlocking the mutex.
166   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
167   do {
168     // Check our predicate (event bits) each time through this do loop
169     if ((mask & m_bits) == 0) {
170       // All the bits requested have been reset, return zero indicating
171       // which bits from the mask were still set (none of them)
172       return 0;
173     }
174     if (timeout_abstime) {
175       // Wait for condition to get broadcast, or for a timeout. If we get
176       // a timeout we will drop out of the do loop and return false which
177       // is what we want.
178       err = ::pthread_cond_timedwait(m_reset_condition.Condition(),
179                                      m_mutex.Mutex(), timeout_abstime);
180     } else {
181       // Wait for condition to get broadcast. The only error this function
182       // should return is if
183       err = ::pthread_cond_wait(m_reset_condition.Condition(), m_mutex.Mutex());
184     }
185   } while (err == 0);
186   // Return a mask indicating which bits (if any) were still set
187   return mask & m_bits;
188 }
189 
190 uint32_t
191 PThreadEvent::WaitForResetAck(const uint32_t mask,
192                               const struct timespec *timeout_abstime) const {
193   if (mask & m_reset_ack_mask) {
194     // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
195     // __FUNCTION__, mask, timeout_abstime);
196     return WaitForEventsToReset(mask & m_reset_ack_mask, timeout_abstime);
197   }
198   return 0;
199 }
200