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
PThreadEvent(uint32_t bits,uint32_t validBits)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
~PThreadEvent()24 PThreadEvent::~PThreadEvent() {
25 // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
26 }
27
NewEventBit()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
FreeEventBits(const uint32_t mask)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
GetEventBits() const48 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
ReplaceEventBits(const uint32_t bits)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
SetEvents(const uint32_t mask)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
ResetEvents(const uint32_t mask)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 // Wait until 'timeout_abstime' for any events that are set in
109 // 'mask'. If 'timeout_abstime' is NULL, then wait forever.
110 uint32_t
WaitForSetEvents(const uint32_t mask,const struct timespec * timeout_abstime) const111 PThreadEvent::WaitForSetEvents(const uint32_t mask,
112 const struct timespec *timeout_abstime) const {
113 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
114 // __FUNCTION__, mask, timeout_abstime);
115 int err = 0;
116 // pthread_cond_timedwait() or pthread_cond_wait() will atomically
117 // unlock the mutex and wait for the condition to be set. When either
118 // function returns, they will re-lock the mutex. We use an auto lock/unlock
119 // class (PThreadMutex::Locker) to allow us to return at any point in this
120 // function and not have to worry about unlocking the mutex.
121 PTHREAD_MUTEX_LOCKER(locker, m_mutex);
122 do {
123 // Check our predicate (event bits) in case any are already set
124 if (mask & m_bits) {
125 uint32_t bits_set = mask & m_bits;
126 // Our PThreadMutex::Locker will automatically unlock our mutex
127 return bits_set;
128 }
129 if (timeout_abstime) {
130 // Wait for condition to get broadcast, or for a timeout. If we get
131 // a timeout we will drop out of the do loop and return false which
132 // is what we want.
133 err = ::pthread_cond_timedwait(m_set_condition.Condition(),
134 m_mutex.Mutex(), timeout_abstime);
135 // Retest our predicate in case of a race condition right at the end
136 // of the timeout.
137 if (err == ETIMEDOUT) {
138 uint32_t bits_set = mask & m_bits;
139 return bits_set;
140 }
141 } else {
142 // Wait for condition to get broadcast. The only error this function
143 // should return is if
144 err = ::pthread_cond_wait(m_set_condition.Condition(), m_mutex.Mutex());
145 }
146 } while (err == 0);
147 return 0;
148 }
149
150 // Wait until 'timeout_abstime' for any events in 'mask' to reset.
151 // If 'timeout_abstime' is NULL, then wait forever.
WaitForEventsToReset(const uint32_t mask,const struct timespec * timeout_abstime) const152 uint32_t PThreadEvent::WaitForEventsToReset(
153 const uint32_t mask, const struct timespec *timeout_abstime) const {
154 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
155 // __FUNCTION__, mask, timeout_abstime);
156 int err = 0;
157 // pthread_cond_timedwait() or pthread_cond_wait() will atomically
158 // unlock the mutex and wait for the condition to be set. When either
159 // function returns, they will re-lock the mutex. We use an auto lock/unlock
160 // class (PThreadMutex::Locker) to allow us to return at any point in this
161 // function and not have to worry about unlocking the mutex.
162 PTHREAD_MUTEX_LOCKER(locker, m_mutex);
163 do {
164 // Check our predicate (event bits) each time through this do loop
165 if ((mask & m_bits) == 0) {
166 // All the bits requested have been reset, return zero indicating
167 // which bits from the mask were still set (none of them)
168 return 0;
169 }
170 if (timeout_abstime) {
171 // Wait for condition to get broadcast, or for a timeout. If we get
172 // a timeout we will drop out of the do loop and return false which
173 // is what we want.
174 err = ::pthread_cond_timedwait(m_reset_condition.Condition(),
175 m_mutex.Mutex(), timeout_abstime);
176 } else {
177 // Wait for condition to get broadcast. The only error this function
178 // should return is if
179 err = ::pthread_cond_wait(m_reset_condition.Condition(), m_mutex.Mutex());
180 }
181 } while (err == 0);
182 // Return a mask indicating which bits (if any) were still set
183 return mask & m_bits;
184 }
185
186 uint32_t
WaitForResetAck(const uint32_t mask,const struct timespec * timeout_abstime) const187 PThreadEvent::WaitForResetAck(const uint32_t mask,
188 const struct timespec *timeout_abstime) const {
189 if (mask & m_reset_ack_mask) {
190 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
191 // __FUNCTION__, mask, timeout_abstime);
192 return WaitForEventsToReset(mask & m_reset_ack_mask, timeout_abstime);
193 }
194 return 0;
195 }
196