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 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 29 PThreadEvent::~PThreadEvent() 30 { 31 // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__); 32 } 33 34 35 uint32_t 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 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 61 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 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 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 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 133 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 181 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 219 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