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::WaitForEventsImpl(const uint32_t mask, 112 const struct timespec *timeout_abstime, 113 std::function<bool()> predicate) const { 114 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, 115 // __FUNCTION__, mask, timeout_abstime); 116 117 int err = 0; 118 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 126 // Check the predicate and the error code. The functions below do not return 127 // EINTR so that's not something we need to handle. 128 while (!predicate() && err == 0) { 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 loop on the next iteration and we 132 // will recompute the mask in case of a race between the condition and the 133 // timeout. 134 err = ::pthread_cond_timedwait(m_set_condition.Condition(), 135 m_mutex.Mutex(), timeout_abstime); 136 } else { 137 // Wait for condition to get broadcast. 138 err = ::pthread_cond_wait(m_set_condition.Condition(), m_mutex.Mutex()); 139 } 140 } 141 142 // Either the predicate passed, we hit the specified timeout (ETIMEDOUT) or we 143 // encountered an unrecoverable error (EINVAL, EPERM). Regardless of how we 144 // got here, recompute and return the mask indicating which bits (if any) are 145 // set. 146 return GetBitsMasked(mask); 147 } 148 149 uint32_t 150 PThreadEvent::WaitForSetEvents(const uint32_t mask, 151 const struct timespec *timeout_abstime) const { 152 auto predicate = [&]() -> uint32_t { return GetBitsMasked(mask) != 0; }; 153 return WaitForEventsImpl(mask, timeout_abstime, predicate); 154 } 155 156 uint32_t PThreadEvent::WaitForEventsToReset( 157 const uint32_t mask, const struct timespec *timeout_abstime) const { 158 auto predicate = [&]() -> uint32_t { return GetBitsMasked(mask) == 0; }; 159 return WaitForEventsImpl(mask, timeout_abstime, predicate); 160 } 161 162 uint32_t 163 PThreadEvent::WaitForResetAck(const uint32_t mask, 164 const struct timespec *timeout_abstime) const { 165 if (mask & m_reset_ack_mask) { 166 // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, 167 // __FUNCTION__, mask, timeout_abstime); 168 return WaitForEventsToReset(mask & m_reset_ack_mask, timeout_abstime); 169 } 170 return 0; 171 } 172