Home | History | Annotate | Download | only in source
      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