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