Skip to content

Commit 05ddf87

Browse files
address CRs
1 parent 47a79ab commit 05ddf87

File tree

8 files changed

+114
-86
lines changed

8 files changed

+114
-86
lines changed

libc/include/llvm-libc-types/pthread_rwlock_t.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ typedef struct {
1515
unsigned __is_pshared : 1;
1616
unsigned __preference : 1;
1717
int __state;
18-
pid_t __writier_tid;
18+
pid_t __writer_tid;
1919
__futex_word __wait_queue_mutex;
2020
__futex_word __pending_readers;
2121
__futex_word __pending_writers;

libc/src/__support/threads/linux/rwlock.h

Lines changed: 58 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class WaitingQueue final : private RawMutex {
112112
}
113113
};
114114

115-
// The State of the RwLock is stored in an integer word, consisting of the
115+
// The RwState of the RwLock is stored in an integer word, consisting of the
116116
// following components:
117117
// -----------------------------------------------
118118
// | Range | Description |
@@ -125,8 +125,7 @@ class WaitingQueue final : private RawMutex {
125125
// -----------------------------------------------
126126
// | MSB | Active Writer Bit |
127127
// -----------------------------------------------
128-
class State {
129-
128+
class RwState {
130129
// Shift amounts to access the components of the state.
131130
LIBC_INLINE_VAR static constexpr int PENDING_READER_SHIFT = 0;
132131
LIBC_INLINE_VAR static constexpr int PENDING_WRITER_SHIFT = 1;
@@ -153,7 +152,7 @@ class State {
153152

154153
public:
155154
// Construction and conversion functions.
156-
LIBC_INLINE constexpr State(int state = 0) : state(state) {}
155+
LIBC_INLINE constexpr RwState(int state = 0) : state(state) {}
157156
LIBC_INLINE constexpr operator int() const { return state; }
158157

159158
// Utilities to check the state of the RwLock.
@@ -174,8 +173,8 @@ class State {
174173
return state & PENDING_MASK;
175174
}
176175

177-
LIBC_INLINE constexpr State set_writer_bit() const {
178-
return State(state | ACTIVE_WRITER_BIT);
176+
LIBC_INLINE constexpr RwState set_writer_bit() const {
177+
return RwState(state | ACTIVE_WRITER_BIT);
179178
}
180179

181180
// The preference parameter changes the behavior of the lock acquisition
@@ -196,61 +195,52 @@ class State {
196195

197196
// This function check if it is possible to grow the reader count without
198197
// overflowing the state.
199-
LIBC_INLINE cpp::optional<State> try_increase_reader_count() const {
198+
LIBC_INLINE cpp::optional<RwState> try_increase_reader_count() const {
200199
LIBC_ASSERT(!has_active_writer() &&
201200
"try_increase_reader_count shall only be called when there "
202201
"is no active writer.");
203-
State res;
202+
RwState res;
204203
if (LIBC_UNLIKELY(__builtin_sadd_overflow(state, ACTIVE_READER_COUNT_UNIT,
205204
&res.state)))
206205
return cpp::nullopt;
207206
return res;
208207
}
209208

210209
// Utilities to do atomic operations on the state.
211-
LIBC_INLINE static State
212-
fetch_sub_reader_count(cpp::Atomic<int> &target,
213-
cpp::MemoryOrder order = cpp::MemoryOrder::SEQ_CST) {
214-
return State(target.fetch_sub(ACTIVE_READER_COUNT_UNIT, order));
210+
LIBC_INLINE static RwState fetch_sub_reader_count(cpp::Atomic<int> &target,
211+
cpp::MemoryOrder order) {
212+
return RwState(target.fetch_sub(ACTIVE_READER_COUNT_UNIT, order));
215213
}
216214

217-
LIBC_INLINE static State
218-
load(cpp::Atomic<int> &target,
219-
cpp::MemoryOrder order = cpp::MemoryOrder::SEQ_CST) {
220-
return State(target.load(order));
215+
LIBC_INLINE static RwState load(cpp::Atomic<int> &target,
216+
cpp::MemoryOrder order) {
217+
return RwState(target.load(order));
221218
}
222219

223220
template <Role role>
224-
LIBC_INLINE static State
225-
fetch_set_pending_bit(cpp::Atomic<int> &target,
226-
cpp::MemoryOrder order = cpp::MemoryOrder::SEQ_CST) {
221+
LIBC_INLINE static RwState fetch_set_pending_bit(cpp::Atomic<int> &target,
222+
cpp::MemoryOrder order) {
227223
if constexpr (role == Role::Reader)
228-
return State(target.fetch_or(PENDING_READER_BIT, order));
224+
return RwState(target.fetch_or(PENDING_READER_BIT, order));
229225
else
230-
return State(target.fetch_or(PENDING_WRITER_BIT, order));
226+
return RwState(target.fetch_or(PENDING_WRITER_BIT, order));
231227
}
232228
template <Role role>
233-
LIBC_INLINE static State
234-
fetch_clear_pending_bit(cpp::Atomic<int> &target,
235-
cpp::MemoryOrder order = cpp::MemoryOrder::SEQ_CST) {
229+
LIBC_INLINE static RwState fetch_clear_pending_bit(cpp::Atomic<int> &target,
230+
cpp::MemoryOrder order) {
236231
if constexpr (role == Role::Reader)
237-
return State(target.fetch_and(~PENDING_READER_BIT, order));
232+
return RwState(target.fetch_and(~PENDING_READER_BIT, order));
238233
else
239-
return State(target.fetch_and(~PENDING_WRITER_BIT, order));
240-
}
241-
LIBC_INLINE static State
242-
fetch_set_active_writer(cpp::Atomic<int> &target,
243-
cpp::MemoryOrder order = cpp::MemoryOrder::SEQ_CST) {
244-
return State(target.fetch_or(ACTIVE_WRITER_BIT, order));
234+
return RwState(target.fetch_and(~PENDING_WRITER_BIT, order));
245235
}
246-
LIBC_INLINE static State fetch_clear_active_writer(
247-
cpp::Atomic<int> &target,
248-
cpp::MemoryOrder order = cpp::MemoryOrder::SEQ_CST) {
249-
return State(target.fetch_and(~ACTIVE_WRITER_BIT, order));
236+
237+
LIBC_INLINE static RwState fetch_clear_active_writer(cpp::Atomic<int> &target,
238+
cpp::MemoryOrder order) {
239+
return RwState(target.fetch_and(~ACTIVE_WRITER_BIT, order));
250240
}
251241

252242
LIBC_INLINE bool compare_exchange_weak_with(cpp::Atomic<int> &target,
253-
State desired,
243+
RwState desired,
254244
cpp::MemoryOrder success_order,
255245
cpp::MemoryOrder failure_order) {
256246
return target.compare_exchange_weak(state, desired, success_order,
@@ -260,10 +250,10 @@ class State {
260250
// Utilities to spin and reload the state.
261251
private:
262252
template <class F>
263-
LIBC_INLINE static State spin_reload_until(cpp::Atomic<int> &target, F &&func,
264-
unsigned spin_count) {
253+
LIBC_INLINE static RwState spin_reload_until(cpp::Atomic<int> &target,
254+
F &&func, unsigned spin_count) {
265255
for (;;) {
266-
auto state = State::load(target, cpp::MemoryOrder::RELAXED);
256+
auto state = RwState::load(target, cpp::MemoryOrder::RELAXED);
267257
if (func(state) || spin_count == 0)
268258
return state;
269259
sleep_briefly();
@@ -273,38 +263,38 @@ class State {
273263

274264
public:
275265
template <Role role>
276-
LIBC_INLINE static State spin_reload(cpp::Atomic<int> &target,
277-
Role preference, unsigned spin_count) {
266+
LIBC_INLINE static RwState spin_reload(cpp::Atomic<int> &target,
267+
Role preference, unsigned spin_count) {
278268
if constexpr (role == Role::Reader) {
279269
// Return the reader state if either the lock is available or there is
280-
// any
281-
// ongoing contention.
270+
// any ongoing contention.
282271
return spin_reload_until(
283272
target,
284-
[=](State state) {
273+
[=](RwState state) {
285274
return state.can_acquire<Role::Reader>(preference) ||
286275
state.has_pending();
287276
},
288277
spin_count);
289278
} else {
290279
// Return the writer state if either the lock is available or there is
291-
// any
292-
// contention *between writers*. Since writers can be way less than
280+
// any contention *between writers*. Since writers can be way less than
293281
// readers, we allow them to spin more to improve the fairness.
294282
return spin_reload_until(
295283
target,
296-
[=](State state) {
284+
[=](RwState state) {
297285
return state.can_acquire<Role::Writer>(preference) ||
298286
state.has_pending_writer();
299287
},
300288
spin_count);
301289
}
302290
}
291+
292+
friend class RwLockTester;
303293
};
304294
} // namespace rwlock
305295

306296
class RwLock {
307-
using State = rwlock::State;
297+
using RwState = rwlock::RwState;
308298
using Role = rwlock::Role;
309299
using WaitingQueue = rwlock::WaitingQueue;
310300

@@ -330,13 +320,13 @@ class RwLock {
330320
// Reader/Writer preference.
331321
LIBC_PREFERED_TYPE(Role)
332322
unsigned preference : 1;
333-
// State to keep track of the RwLock.
323+
// RwState to keep track of the RwLock.
334324
cpp::Atomic<int> state;
335325
// writer_tid is used to keep track of the thread id of the writer. Notice
336326
// that TLS address is not a good idea here since it may remains the same
337327
// across forked processes.
338328
cpp::Atomic<pid_t> writer_tid;
339-
// Waiting queue to keep track of the pending readers and writers.
329+
// Waiting queue to keep track of the readers and writers.
340330
WaitingQueue queue;
341331

342332
private:
@@ -347,10 +337,10 @@ class RwLock {
347337
// TODO: use cached thread id once implemented.
348338
LIBC_INLINE static pid_t gettid() { return syscall_impl<pid_t>(SYS_gettid); }
349339

350-
template <Role role> LIBC_INLINE LockResult try_lock(State &old) {
340+
template <Role role> LIBC_INLINE LockResult try_lock(RwState &old) {
351341
if constexpr (role == Role::Reader) {
352342
while (LIBC_LIKELY(old.can_acquire<Role::Reader>(get_preference()))) {
353-
cpp::optional<State> next = old.try_increase_reader_count();
343+
cpp::optional<RwState> next = old.try_increase_reader_count();
354344
if (!next)
355345
return LockResult::Overflow;
356346
if (LIBC_LIKELY(old.compare_exchange_weak_with(
@@ -385,12 +375,12 @@ class RwLock {
385375

386376
[[nodiscard]]
387377
LIBC_INLINE LockResult try_read_lock() {
388-
State old = State::load(state, cpp::MemoryOrder::RELAXED);
378+
RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED);
389379
return try_lock<Role::Reader>(old);
390380
}
391381
[[nodiscard]]
392382
LIBC_INLINE LockResult try_write_lock() {
393-
State old = State::load(state, cpp::MemoryOrder::RELAXED);
383+
RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED);
394384
return try_lock<Role::Writer>(old);
395385
}
396386

@@ -412,7 +402,8 @@ class RwLock {
412402

413403
// Phase 3: spin to get the initial state. We ignore the timing due to
414404
// spin since it should end quickly.
415-
State old = State::spin_reload<role>(state, get_preference(), spin_count);
405+
RwState old =
406+
RwState::spin_reload<role>(state, get_preference(), spin_count);
416407

417408
// Enter the main acquisition loop.
418409
for (;;) {
@@ -421,7 +412,7 @@ class RwLock {
421412
if (result != LockResult::Busy)
422413
return result;
423414

424-
// Phase 5: register ourselves as a pending reader.
415+
// Phase 5: register ourselves as a reader.
425416
int serial_number;
426417
{
427418
// The queue need to be protected by a mutex since the operations in
@@ -436,8 +427,8 @@ class RwLock {
436427
// on the state. The pending flag update should be visible to any
437428
// succeeding unlock events. Or, if a unlock does happen before we
438429
// sleep on the futex, we can avoid such waiting.
439-
old = State::fetch_set_pending_bit<role>(state,
440-
cpp::MemoryOrder::RELAXED);
430+
old = RwState::fetch_set_pending_bit<role>(state,
431+
cpp::MemoryOrder::RELAXED);
441432
// no need to use atomic since it is already protected by the mutex.
442433
serial_number = guard.serialization<role>();
443434
}
@@ -449,7 +440,7 @@ class RwLock {
449440
timeout_flag = (queue.wait<role>(serial_number, timeout, is_pshared) ==
450441
-ETIMEDOUT);
451442

452-
// Phase 7: unregister ourselves as a pending reader.
443+
// Phase 7: unregister ourselves as a pending reader/writer.
453444
{
454445
// Similarly, the unregister operation should also be an atomic
455446
// transaction.
@@ -459,16 +450,16 @@ class RwLock {
459450
// cleared otherwise operations like trylock may fail even though
460451
// there is no competitors.
461452
if (guard.pending_count<role>() == 0)
462-
State::fetch_clear_pending_bit<role>(state,
463-
cpp::MemoryOrder::RELAXED);
453+
RwState::fetch_clear_pending_bit<role>(state,
454+
cpp::MemoryOrder::RELAXED);
464455
}
465456

466457
// Phase 8: exit the loop is timeout is reached.
467458
if (timeout_flag)
468459
return LockResult::TimedOut;
469460

470461
// Phase 9: reload the state and retry the acquisition.
471-
old = State::spin_reload<role>(state, get_preference(), spin_count);
462+
old = RwState::spin_reload<role>(state, get_preference(), spin_count);
472463
}
473464
}
474465

@@ -522,7 +513,7 @@ class RwLock {
522513
public:
523514
[[nodiscard]]
524515
LIBC_INLINE LockResult unlock() {
525-
State old = State::load(state, cpp::MemoryOrder::RELAXED);
516+
RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED);
526517
if (old.has_active_writer()) {
527518
// The lock is held by a writer.
528519
// Check if we are the owner of the lock.
@@ -531,14 +522,15 @@ class RwLock {
531522
// clear writer tid.
532523
writer_tid.store(0, cpp::MemoryOrder::RELAXED);
533524
// clear the writer bit.
534-
old = State::fetch_clear_active_writer(state, cpp::MemoryOrder::RELEASE);
525+
old =
526+
RwState::fetch_clear_active_writer(state, cpp::MemoryOrder::RELEASE);
535527
// If there is no pending readers or writers, we are done.
536528
if (!old.has_pending())
537529
return LockResult::Success;
538530
} else if (old.has_active_reader()) {
539531
// The lock is held by readers.
540532
// Decrease the reader count.
541-
old = State::fetch_sub_reader_count(state, cpp::MemoryOrder::RELEASE);
533+
old = RwState::fetch_sub_reader_count(state, cpp::MemoryOrder::RELEASE);
542534
// If there is no pending readers or writers, we are done.
543535
if (!old.has_last_reader() || !old.has_pending())
544536
return LockResult::Success;
@@ -553,7 +545,7 @@ class RwLock {
553545
// will only check if the lock is currently held by any thread.
554546
[[nodiscard]]
555547
LIBC_INLINE LockResult check_for_destroy() {
556-
State old = State::load(state, cpp::MemoryOrder::RELAXED);
548+
RwState old = RwState::load(state, cpp::MemoryOrder::RELAXED);
557549
if (old.has_acitve_owner())
558550
return LockResult::Busy;
559551
return LockResult::Success;

libc/src/pthread/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ add_entrypoint_object(
531531
DEPENDS
532532
libc.include.pthread
533533
libc.src.__support.threads.linux.rwlock
534+
libc.src.__support.CPP.new
534535
)
535536

536537
add_entrypoint_object(

libc/src/pthread/pthread_rwlock_init.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,14 @@
88

99
#include "src/pthread/pthread_rwlock_init.h"
1010

11+
#include "src/__support/CPP/new.h"
1112
#include "src/__support/common.h"
1213
#include "src/__support/libc_assert.h"
1314
#include "src/__support/threads/linux/rwlock.h"
1415

1516
#include <errno.h>
1617
#include <pthread.h>
1718

18-
LIBC_INLINE void *operator new(size_t, pthread_rwlock_t *addr) noexcept {
19-
return addr;
20-
}
21-
2219
namespace LIBC_NAMESPACE {
2320

2421
static_assert(

libc/src/pthread/pthread_rwlock_timedrdlock.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ LLVM_LIBC_FUNCTION(int, pthread_rwlock_timedrdlock,
4242
return EINVAL;
4343
case internal::AbsTimeout::Error::BeforeEpoch:
4444
return ETIMEDOUT;
45+
// default: unreachable, all two cases are covered.
4546
}
4647
}
4748

libc/src/pthread/pthread_rwlock_timedwrlock.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ LLVM_LIBC_FUNCTION(int, pthread_rwlock_timedwrlock,
3636
return EINVAL;
3737
case internal::AbsTimeout::Error::BeforeEpoch:
3838
return ETIMEDOUT;
39+
// default: unreachable, all two cases are covered.
3940
}
4041
}
4142

libc/test/integration/src/pthread/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ add_integration_test(
4242
libc.src.pthread.pthread_rwlockattr_destroy
4343
libc.src.pthread.pthread_rwlockattr_setpshared
4444
libc.src.pthread.pthread_rwlockattr_setkind_np
45+
libc.src.__support.threads.linux.raw_mutex
46+
libc.src.stdio.printf
47+
libc.src.stdlib.getenv
4548
libc.src.sys.mman.mmap
4649
libc.src.sys.mman.munmap
4750
libc.src.time.clock_gettime

0 commit comments

Comments
 (0)