[TableGen] Remove unnecessary check before calling SmallVector::erase. NFC
[llvm-project.git] / libc / src / __support / threads / linux / CndVar.cpp
blobbe74c18dddf31aca0cad1a0f0d735ae9adfb737e
1 //===-- Utility condition variable class ------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
9 #include "src/__support/threads/CndVar.h"
10 #include "src/__support/CPP/mutex.h"
11 #include "src/__support/OSUtil/syscall.h" // syscall_impl
12 #include "src/__support/macros/config.h"
13 #include "src/__support/threads/linux/futex_word.h" // FutexWordType
14 #include "src/__support/threads/linux/raw_mutex.h" // RawMutex
15 #include "src/__support/threads/mutex.h" // Mutex
17 #include <sys/syscall.h> // For syscall numbers.
19 namespace LIBC_NAMESPACE_DECL {
21 int CndVar::wait(Mutex *m) {
22 // The goal is to perform "unlock |m| and wait" in an
23 // atomic operation. However, it is not possible to do it
24 // in the true sense so we do it in spirit. Before unlocking
25 // |m|, a new waiter object is added to the waiter queue with
26 // the waiter queue locked. Iff a signalling thread signals
27 // the waiter before the waiter actually starts waiting, the
28 // wait operation will not begin at all and the waiter immediately
29 // returns.
31 CndWaiter waiter;
33 cpp::lock_guard ml(qmtx);
34 CndWaiter *old_back = nullptr;
35 if (waitq_front == nullptr) {
36 waitq_front = waitq_back = &waiter;
37 } else {
38 old_back = waitq_back;
39 waitq_back->next = &waiter;
40 waitq_back = &waiter;
43 if (m->unlock() != MutexError::NONE) {
44 // If we do not remove the queued up waiter before returning,
45 // then another thread can potentially signal a non-existing
46 // waiter. Note also that we do this with |qmtx| locked. This
47 // ensures that another thread will not signal the withdrawing
48 // waiter.
49 waitq_back = old_back;
50 if (waitq_back == nullptr)
51 waitq_front = nullptr;
52 else
53 waitq_back->next = nullptr;
55 return -1;
59 waiter.futex_word.wait(WS_Waiting, cpp::nullopt, true);
61 // At this point, if locking |m| fails, we can simply return as the
62 // queued up waiter would have been removed from the queue.
63 auto err = m->lock();
64 return err == MutexError::NONE ? 0 : -1;
67 void CndVar::notify_one() {
68 // We don't use an RAII locker in this method as we want to unlock
69 // |qmtx| and signal the waiter using a single FUTEX_WAKE_OP signal.
70 qmtx.lock();
71 if (waitq_front == nullptr)
72 qmtx.unlock();
74 CndWaiter *first = waitq_front;
75 waitq_front = waitq_front->next;
76 if (waitq_front == nullptr)
77 waitq_back = nullptr;
79 qmtx.reset();
81 // this is a special WAKE_OP, so we use syscall directly
82 LIBC_NAMESPACE::syscall_impl<long>(
83 FUTEX_SYSCALL_ID, &qmtx.get_raw_futex(), FUTEX_WAKE_OP, 1, 1,
84 &first->futex_word.val,
85 FUTEX_OP(FUTEX_OP_SET, WS_Signalled, FUTEX_OP_CMP_EQ, WS_Waiting));
88 void CndVar::broadcast() {
89 cpp::lock_guard ml(qmtx);
90 uint32_t dummy_futex_word;
91 CndWaiter *waiter = waitq_front;
92 waitq_front = waitq_back = nullptr;
93 while (waiter != nullptr) {
94 // FUTEX_WAKE_OP is used instead of just FUTEX_WAKE as it allows us to
95 // atomically update the waiter status to WS_Signalled before waking
96 // up the waiter. A dummy location is used for the other futex of
97 // FUTEX_WAKE_OP.
98 LIBC_NAMESPACE::syscall_impl<long>(
99 FUTEX_SYSCALL_ID, &dummy_futex_word, FUTEX_WAKE_OP, 1, 1,
100 &waiter->futex_word.val,
101 FUTEX_OP(FUTEX_OP_SET, WS_Signalled, FUTEX_OP_CMP_EQ, WS_Waiting));
102 waiter = waiter->next;
106 } // namespace LIBC_NAMESPACE_DECL