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