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