Skip to content

Commit 87f2c25

Browse files
authored
[lldb] Unify WaitForSetEvents and WaitForEventsToReset (#99997)
Unify the implementations of WaitForSetEvents and WaitForEventsToReset. The former deals with the possibility of a race between the timeout and the predicate while the latter does not. The functions were also inconsistent in when they would recompute the mask. This patch unifies the two implementations and make them behave exactly the same modulo the predicate. rdar://130562344
1 parent c2e4386 commit 87f2c25

File tree

2 files changed

+36
-53
lines changed

2 files changed

+36
-53
lines changed

lldb/tools/debugserver/source/PThreadEvent.cpp

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -108,79 +108,55 @@ void PThreadEvent::ResetEvents(const uint32_t mask) {
108108
// Wait until 'timeout_abstime' for any events that are set in
109109
// 'mask'. If 'timeout_abstime' is NULL, then wait forever.
110110
uint32_t
111-
PThreadEvent::WaitForSetEvents(const uint32_t mask,
112-
const struct timespec *timeout_abstime) const {
111+
PThreadEvent::WaitForEventsImpl(const uint32_t mask,
112+
const struct timespec *timeout_abstime,
113+
std::function<bool()> predicate) const {
113114
// DNBLogThreadedIf(LOG_EVENTS, "%p PThreadEvent::%s (0x%8.8x, %p)", this,
114115
// __FUNCTION__, mask, timeout_abstime);
116+
115117
int err = 0;
118+
116119
// pthread_cond_timedwait() or pthread_cond_wait() will atomically
117120
// unlock the mutex and wait for the condition to be set. When either
118121
// function returns, they will re-lock the mutex. We use an auto lock/unlock
119122
// class (PThreadMutex::Locker) to allow us to return at any point in this
120123
// function and not have to worry about unlocking the mutex.
121124
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-
}
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) {
129129
if (timeout_abstime) {
130130
// 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.
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.
133134
err = ::pthread_cond_timedwait(m_set_condition.Condition(),
134135
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-
}
141136
} else {
142-
// Wait for condition to get broadcast. The only error this function
143-
// should return is if
137+
// Wait for condition to get broadcast.
144138
err = ::pthread_cond_wait(m_set_condition.Condition(), m_mutex.Mutex());
145139
}
146-
} while (err == 0);
147-
return 0;
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);
148154
}
149155

150-
// Wait until 'timeout_abstime' for any events in 'mask' to reset.
151-
// If 'timeout_abstime' is NULL, then wait forever.
152156
uint32_t PThreadEvent::WaitForEventsToReset(
153157
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;
158+
auto predicate = [&]() -> uint32_t { return GetBitsMasked(mask) == 0; };
159+
return WaitForEventsImpl(mask, timeout_abstime, predicate);
184160
}
185161

186162
uint32_t

lldb/tools/debugserver/source/PThreadEvent.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "PThreadMutex.h"
1717
#include <cstdint>
1818
#include <ctime>
19+
#include <functional>
1920

2021
class PThreadEvent {
2122
public:
@@ -53,6 +54,12 @@ class PThreadEvent {
5354
uint32_t m_validBits;
5455
uint32_t m_reset_ack_mask;
5556

57+
uint32_t GetBitsMasked(uint32_t mask) const { return mask & m_bits; }
58+
59+
uint32_t WaitForEventsImpl(const uint32_t mask,
60+
const struct timespec *timeout_abstime,
61+
std::function<bool()> predicate) const;
62+
5663
private:
5764
PThreadEvent(const PThreadEvent &) = delete;
5865
PThreadEvent &operator=(const PThreadEvent &rhs) = delete;

0 commit comments

Comments
 (0)