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