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