|  | //===-- PThreadEvent.cpp ----------------------------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | //  Created by Greg Clayton on 6/16/07. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "PThreadEvent.h" | 
|  | #include "errno.h" | 
|  | #include "DNBLog.h" | 
|  |  | 
|  | PThreadEvent::PThreadEvent(uint32_t bits, uint32_t validBits) : | 
|  | m_mutex(), | 
|  | m_set_condition(), | 
|  | m_reset_condition(), | 
|  | m_bits(bits), | 
|  | m_validBits(validBits), | 
|  | m_reset_ack_mask(0) | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, 0x%8.8x)", this, __FUNCTION__, bits, validBits); | 
|  | } | 
|  |  | 
|  | PThreadEvent::~PThreadEvent() | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__); | 
|  | } | 
|  |  | 
|  |  | 
|  | uint32_t | 
|  | PThreadEvent::NewEventBit() | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__); | 
|  | PTHREAD_MUTEX_LOCKER (locker, m_mutex); | 
|  | uint32_t mask = 1; | 
|  | while (mask & m_validBits) | 
|  | mask <<= 1; | 
|  | m_validBits |= mask; | 
|  | return mask; | 
|  | } | 
|  |  | 
|  | void | 
|  | PThreadEvent::FreeEventBits(const uint32_t mask) | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask); | 
|  | if (mask) | 
|  | { | 
|  | PTHREAD_MUTEX_LOCKER (locker, m_mutex); | 
|  | m_bits &= ~mask; | 
|  | m_validBits &= ~mask; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | uint32_t | 
|  | PThreadEvent::GetEventBits() const | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, __PRETTY_FUNCTION__); | 
|  | PTHREAD_MUTEX_LOCKER (locker, m_mutex); | 
|  | uint32_t bits = m_bits; | 
|  | return bits; | 
|  | } | 
|  |  | 
|  | // Replace the event bits with a new bitmask value | 
|  | void | 
|  | PThreadEvent::ReplaceEventBits(const uint32_t bits) | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, bits); | 
|  | PTHREAD_MUTEX_LOCKER (locker, m_mutex); | 
|  | // Make sure we have some bits and that they aren't already set... | 
|  | if (m_bits != bits) | 
|  | { | 
|  | // Figure out which bits are changing | 
|  | uint32_t changed_bits = m_bits ^ bits; | 
|  | // Set the new bit values | 
|  | m_bits = bits; | 
|  | // If any new bits are set, then broadcast | 
|  | if (changed_bits & m_bits) | 
|  | m_set_condition.Broadcast(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Set one or more event bits and broadcast if any new event bits get set | 
|  | // that weren't already set. | 
|  |  | 
|  | void | 
|  | PThreadEvent::SetEvents(const uint32_t mask) | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask); | 
|  | // Make sure we have some bits to set | 
|  | if (mask) | 
|  | { | 
|  | PTHREAD_MUTEX_LOCKER (locker, m_mutex); | 
|  | // Save the old event bit state so we can tell if things change | 
|  | uint32_t old = m_bits; | 
|  | // Set the all event bits that are set in 'mask' | 
|  | m_bits |= mask; | 
|  | // Broadcast only if any extra bits got set. | 
|  | if (old != m_bits) | 
|  | m_set_condition.Broadcast(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Reset one or more event bits | 
|  | void | 
|  | PThreadEvent::ResetEvents(const uint32_t mask) | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this, __FUNCTION__, mask); | 
|  | if (mask) | 
|  | { | 
|  | PTHREAD_MUTEX_LOCKER (locker, m_mutex); | 
|  |  | 
|  | // Save the old event bit state so we can tell if things change | 
|  | uint32_t old = m_bits; | 
|  | // Clear the all event bits that are set in 'mask' | 
|  | m_bits &= ~mask; | 
|  | // Broadcast only if any extra bits got reset. | 
|  | if (old != m_bits) | 
|  | m_reset_condition.Broadcast(); | 
|  | } | 
|  | } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Wait until 'timeout_abstime' for any events that are set in | 
|  | // 'mask'. If 'timeout_abstime' is NULL, then wait forever. | 
|  | //---------------------------------------------------------------------- | 
|  | uint32_t | 
|  | PThreadEvent::WaitForSetEvents(const uint32_t mask, const struct timespec *timeout_abstime) const | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime); | 
|  | int err = 0; | 
|  | // pthread_cond_timedwait() or pthread_cond_wait() will atomically | 
|  | // unlock the mutex and wait for the condition to be set. When either | 
|  | // function returns, they will re-lock the mutex. We use an auto lock/unlock | 
|  | // class (PThreadMutex::Locker) to allow us to return at any point in this | 
|  | // function and not have to worry about unlocking the mutex. | 
|  | PTHREAD_MUTEX_LOCKER (locker, m_mutex); | 
|  | do | 
|  | { | 
|  | // Check our predicate (event bits) in case any are already set | 
|  | if (mask & m_bits) | 
|  | { | 
|  | uint32_t bits_set = mask & m_bits; | 
|  | // Our PThreadMutex::Locker will automatically unlock our mutex | 
|  | return bits_set; | 
|  | } | 
|  | if (timeout_abstime) | 
|  | { | 
|  | // Wait for condition to get broadcast, or for a timeout. If we get | 
|  | // a timeout we will drop out of the do loop and return false which | 
|  | // is what we want. | 
|  | err = ::pthread_cond_timedwait (m_set_condition.Condition(), m_mutex.Mutex(), timeout_abstime); | 
|  | // Retest our predicate in case of a race condition right at the end | 
|  | // of the timeout. | 
|  | if (err == ETIMEDOUT) | 
|  | { | 
|  | uint32_t bits_set = mask & m_bits; | 
|  | return bits_set; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | // Wait for condition to get broadcast. The only error this function | 
|  | // should return is if | 
|  | err = ::pthread_cond_wait (m_set_condition.Condition(), m_mutex.Mutex()); | 
|  | } | 
|  | } while (err == 0); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | //---------------------------------------------------------------------- | 
|  | // Wait until 'timeout_abstime' for any events in 'mask' to reset. | 
|  | // If 'timeout_abstime' is NULL, then wait forever. | 
|  | //---------------------------------------------------------------------- | 
|  | uint32_t | 
|  | PThreadEvent::WaitForEventsToReset(const uint32_t mask, const struct timespec *timeout_abstime) const | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime); | 
|  | int err = 0; | 
|  | // pthread_cond_timedwait() or pthread_cond_wait() will atomically | 
|  | // unlock the mutex and wait for the condition to be set. When either | 
|  | // function returns, they will re-lock the mutex. We use an auto lock/unlock | 
|  | // class (PThreadMutex::Locker) to allow us to return at any point in this | 
|  | // function and not have to worry about unlocking the mutex. | 
|  | PTHREAD_MUTEX_LOCKER (locker, m_mutex); | 
|  | do | 
|  | { | 
|  | // Check our predicate (event bits) each time through this do loop | 
|  | if ((mask & m_bits) == 0) | 
|  | { | 
|  | // All the bits requested have been reset, return zero indicating | 
|  | // which bits from the mask were still set (none of them) | 
|  | return 0; | 
|  | } | 
|  | if (timeout_abstime) | 
|  | { | 
|  | // Wait for condition to get broadcast, or for a timeout. If we get | 
|  | // a timeout we will drop out of the do loop and return false which | 
|  | // is what we want. | 
|  | err = ::pthread_cond_timedwait (m_reset_condition.Condition(), m_mutex.Mutex(), timeout_abstime); | 
|  | } | 
|  | else | 
|  | { | 
|  | // Wait for condition to get broadcast. The only error this function | 
|  | // should return is if | 
|  | err = ::pthread_cond_wait (m_reset_condition.Condition(), m_mutex.Mutex()); | 
|  | } | 
|  | } while (err == 0); | 
|  | // Return a mask indicating which bits (if any) were still set | 
|  | return mask & m_bits; | 
|  | } | 
|  |  | 
|  | uint32_t | 
|  | PThreadEvent::WaitForResetAck (const uint32_t mask, const struct timespec *timeout_abstime) const | 
|  | { | 
|  | if (mask & m_reset_ack_mask) | 
|  | { | 
|  | // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this, __FUNCTION__, mask, timeout_abstime); | 
|  | return WaitForEventsToReset (mask & m_reset_ack_mask, timeout_abstime); | 
|  | } | 
|  | return 0; | 
|  | } |