xref: /openbsd-src/gnu/llvm/lldb/tools/debugserver/source/PThreadEvent.cpp (revision be691f3bb6417f04a68938fadbcaee2d5795e764)
1061da546Spatrick //===-- PThreadEvent.cpp ----------------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick //
9061da546Spatrick //  Created by Greg Clayton on 6/16/07.
10061da546Spatrick //
11061da546Spatrick //===----------------------------------------------------------------------===//
12061da546Spatrick 
13061da546Spatrick #include "PThreadEvent.h"
14061da546Spatrick #include "DNBLog.h"
15*be691f3bSpatrick #include <cerrno>
16061da546Spatrick 
PThreadEvent(uint32_t bits,uint32_t validBits)17061da546Spatrick PThreadEvent::PThreadEvent(uint32_t bits, uint32_t validBits)
18061da546Spatrick     : m_mutex(), m_set_condition(), m_reset_condition(), m_bits(bits),
19061da546Spatrick       m_validBits(validBits), m_reset_ack_mask(0) {
20061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, 0x%8.8x)",
21061da546Spatrick   // this, __FUNCTION__, bits, validBits);
22061da546Spatrick }
23061da546Spatrick 
~PThreadEvent()24061da546Spatrick PThreadEvent::~PThreadEvent() {
25061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
26061da546Spatrick }
27061da546Spatrick 
NewEventBit()28061da546Spatrick uint32_t PThreadEvent::NewEventBit() {
29061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
30061da546Spatrick   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
31061da546Spatrick   uint32_t mask = 1;
32061da546Spatrick   while (mask & m_validBits)
33061da546Spatrick     mask <<= 1;
34061da546Spatrick   m_validBits |= mask;
35061da546Spatrick   return mask;
36061da546Spatrick }
37061da546Spatrick 
FreeEventBits(const uint32_t mask)38061da546Spatrick void PThreadEvent::FreeEventBits(const uint32_t mask) {
39061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
40061da546Spatrick   // __FUNCTION__, mask);
41061da546Spatrick   if (mask) {
42061da546Spatrick     PTHREAD_MUTEX_LOCKER(locker, m_mutex);
43061da546Spatrick     m_bits &= ~mask;
44061da546Spatrick     m_validBits &= ~mask;
45061da546Spatrick   }
46061da546Spatrick }
47061da546Spatrick 
GetEventBits() const48061da546Spatrick uint32_t PThreadEvent::GetEventBits() const {
49061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p %s", this, LLVM_PRETTY_FUNCTION);
50061da546Spatrick   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
51061da546Spatrick   uint32_t bits = m_bits;
52061da546Spatrick   return bits;
53061da546Spatrick }
54061da546Spatrick 
55061da546Spatrick // Replace the event bits with a new bitmask value
ReplaceEventBits(const uint32_t bits)56061da546Spatrick void PThreadEvent::ReplaceEventBits(const uint32_t bits) {
57061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
58061da546Spatrick   // __FUNCTION__, bits);
59061da546Spatrick   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
60061da546Spatrick   // Make sure we have some bits and that they aren't already set...
61061da546Spatrick   if (m_bits != bits) {
62061da546Spatrick     // Figure out which bits are changing
63061da546Spatrick     uint32_t changed_bits = m_bits ^ bits;
64061da546Spatrick     // Set the new bit values
65061da546Spatrick     m_bits = bits;
66061da546Spatrick     // If any new bits are set, then broadcast
67061da546Spatrick     if (changed_bits & m_bits)
68061da546Spatrick       m_set_condition.Broadcast();
69061da546Spatrick   }
70061da546Spatrick }
71061da546Spatrick 
72061da546Spatrick // Set one or more event bits and broadcast if any new event bits get set
73061da546Spatrick // that weren't already set.
74061da546Spatrick 
SetEvents(const uint32_t mask)75061da546Spatrick void PThreadEvent::SetEvents(const uint32_t mask) {
76061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
77061da546Spatrick   // __FUNCTION__, mask);
78061da546Spatrick   // Make sure we have some bits to set
79061da546Spatrick   if (mask) {
80061da546Spatrick     PTHREAD_MUTEX_LOCKER(locker, m_mutex);
81061da546Spatrick     // Save the old event bit state so we can tell if things change
82061da546Spatrick     uint32_t old = m_bits;
83061da546Spatrick     // Set the all event bits that are set in 'mask'
84061da546Spatrick     m_bits |= mask;
85061da546Spatrick     // Broadcast only if any extra bits got set.
86061da546Spatrick     if (old != m_bits)
87061da546Spatrick       m_set_condition.Broadcast();
88061da546Spatrick   }
89061da546Spatrick }
90061da546Spatrick 
91061da546Spatrick // Reset one or more event bits
ResetEvents(const uint32_t mask)92061da546Spatrick void PThreadEvent::ResetEvents(const uint32_t mask) {
93061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x)", this,
94061da546Spatrick   // __FUNCTION__, mask);
95061da546Spatrick   if (mask) {
96061da546Spatrick     PTHREAD_MUTEX_LOCKER(locker, m_mutex);
97061da546Spatrick 
98061da546Spatrick     // Save the old event bit state so we can tell if things change
99061da546Spatrick     uint32_t old = m_bits;
100061da546Spatrick     // Clear the all event bits that are set in 'mask'
101061da546Spatrick     m_bits &= ~mask;
102061da546Spatrick     // Broadcast only if any extra bits got reset.
103061da546Spatrick     if (old != m_bits)
104061da546Spatrick       m_reset_condition.Broadcast();
105061da546Spatrick   }
106061da546Spatrick }
107061da546Spatrick 
108061da546Spatrick // Wait until 'timeout_abstime' for any events that are set in
109061da546Spatrick // 'mask'. If 'timeout_abstime' is NULL, then wait forever.
110061da546Spatrick uint32_t
WaitForSetEvents(const uint32_t mask,const struct timespec * timeout_abstime) const111061da546Spatrick PThreadEvent::WaitForSetEvents(const uint32_t mask,
112061da546Spatrick                                const struct timespec *timeout_abstime) const {
113061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
114061da546Spatrick   // __FUNCTION__, mask, timeout_abstime);
115061da546Spatrick   int err = 0;
116061da546Spatrick   // pthread_cond_timedwait() or pthread_cond_wait() will atomically
117061da546Spatrick   // unlock the mutex and wait for the condition to be set. When either
118061da546Spatrick   // function returns, they will re-lock the mutex. We use an auto lock/unlock
119061da546Spatrick   // class (PThreadMutex::Locker) to allow us to return at any point in this
120061da546Spatrick   // function and not have to worry about unlocking the mutex.
121061da546Spatrick   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
122061da546Spatrick   do {
123061da546Spatrick     // Check our predicate (event bits) in case any are already set
124061da546Spatrick     if (mask & m_bits) {
125061da546Spatrick       uint32_t bits_set = mask & m_bits;
126061da546Spatrick       // Our PThreadMutex::Locker will automatically unlock our mutex
127061da546Spatrick       return bits_set;
128061da546Spatrick     }
129061da546Spatrick     if (timeout_abstime) {
130061da546Spatrick       // Wait for condition to get broadcast, or for a timeout. If we get
131061da546Spatrick       // a timeout we will drop out of the do loop and return false which
132061da546Spatrick       // is what we want.
133061da546Spatrick       err = ::pthread_cond_timedwait(m_set_condition.Condition(),
134061da546Spatrick                                      m_mutex.Mutex(), timeout_abstime);
135061da546Spatrick       // Retest our predicate in case of a race condition right at the end
136061da546Spatrick       // of the timeout.
137061da546Spatrick       if (err == ETIMEDOUT) {
138061da546Spatrick         uint32_t bits_set = mask & m_bits;
139061da546Spatrick         return bits_set;
140061da546Spatrick       }
141061da546Spatrick     } else {
142061da546Spatrick       // Wait for condition to get broadcast. The only error this function
143061da546Spatrick       // should return is if
144061da546Spatrick       err = ::pthread_cond_wait(m_set_condition.Condition(), m_mutex.Mutex());
145061da546Spatrick     }
146061da546Spatrick   } while (err == 0);
147061da546Spatrick   return 0;
148061da546Spatrick }
149061da546Spatrick 
150061da546Spatrick // Wait until 'timeout_abstime' for any events in 'mask' to reset.
151061da546Spatrick // If 'timeout_abstime' is NULL, then wait forever.
WaitForEventsToReset(const uint32_t mask,const struct timespec * timeout_abstime) const152061da546Spatrick uint32_t PThreadEvent::WaitForEventsToReset(
153061da546Spatrick     const uint32_t mask, const struct timespec *timeout_abstime) const {
154061da546Spatrick   // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
155061da546Spatrick   // __FUNCTION__, mask, timeout_abstime);
156061da546Spatrick   int err = 0;
157061da546Spatrick   // pthread_cond_timedwait() or pthread_cond_wait() will atomically
158061da546Spatrick   // unlock the mutex and wait for the condition to be set. When either
159061da546Spatrick   // function returns, they will re-lock the mutex. We use an auto lock/unlock
160061da546Spatrick   // class (PThreadMutex::Locker) to allow us to return at any point in this
161061da546Spatrick   // function and not have to worry about unlocking the mutex.
162061da546Spatrick   PTHREAD_MUTEX_LOCKER(locker, m_mutex);
163061da546Spatrick   do {
164061da546Spatrick     // Check our predicate (event bits) each time through this do loop
165061da546Spatrick     if ((mask & m_bits) == 0) {
166061da546Spatrick       // All the bits requested have been reset, return zero indicating
167061da546Spatrick       // which bits from the mask were still set (none of them)
168061da546Spatrick       return 0;
169061da546Spatrick     }
170061da546Spatrick     if (timeout_abstime) {
171061da546Spatrick       // Wait for condition to get broadcast, or for a timeout. If we get
172061da546Spatrick       // a timeout we will drop out of the do loop and return false which
173061da546Spatrick       // is what we want.
174061da546Spatrick       err = ::pthread_cond_timedwait(m_reset_condition.Condition(),
175061da546Spatrick                                      m_mutex.Mutex(), timeout_abstime);
176061da546Spatrick     } else {
177061da546Spatrick       // Wait for condition to get broadcast. The only error this function
178061da546Spatrick       // should return is if
179061da546Spatrick       err = ::pthread_cond_wait(m_reset_condition.Condition(), m_mutex.Mutex());
180061da546Spatrick     }
181061da546Spatrick   } while (err == 0);
182061da546Spatrick   // Return a mask indicating which bits (if any) were still set
183061da546Spatrick   return mask & m_bits;
184061da546Spatrick }
185061da546Spatrick 
186061da546Spatrick uint32_t
WaitForResetAck(const uint32_t mask,const struct timespec * timeout_abstime) const187061da546Spatrick PThreadEvent::WaitForResetAck(const uint32_t mask,
188061da546Spatrick                               const struct timespec *timeout_abstime) const {
189061da546Spatrick   if (mask & m_reset_ack_mask) {
190061da546Spatrick     // DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
191061da546Spatrick     // __FUNCTION__, mask, timeout_abstime);
192061da546Spatrick     return WaitForEventsToReset(mask & m_reset_ack_mask, timeout_abstime);
193061da546Spatrick   }
194061da546Spatrick   return 0;
195061da546Spatrick }
196