xref: /llvm-project/libc/src/__support/threads/linux/CndVar.cpp (revision 5ff3ff33ff930e4ec49da7910612d8a41eb068cb)
15442e15aSNick Desaulniers (paternity leave) //===-- Utility condition variable class ------------------------*- C++ -*-===//
25442e15aSNick Desaulniers (paternity leave) //
35442e15aSNick Desaulniers (paternity leave) // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45442e15aSNick Desaulniers (paternity leave) // See https://llvm.org/LICENSE.txt for license information.
55442e15aSNick Desaulniers (paternity leave) // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65442e15aSNick Desaulniers (paternity leave) //
75442e15aSNick Desaulniers (paternity leave) //===----------------------------------------------------------------------===//
85442e15aSNick Desaulniers (paternity leave) 
95442e15aSNick Desaulniers (paternity leave) #include "src/__support/threads/CndVar.h"
100694552cSSchrodinger ZHU Yifan #include "src/__support/CPP/mutex.h"
115442e15aSNick Desaulniers (paternity leave) #include "src/__support/OSUtil/syscall.h"           // syscall_impl
12*5ff3ff33SPetr Hosek #include "src/__support/macros/config.h"
135442e15aSNick Desaulniers (paternity leave) #include "src/__support/threads/linux/futex_word.h" // FutexWordType
14142afde0SSchrodinger ZHU Yifan #include "src/__support/threads/linux/raw_mutex.h"  // RawMutex
150694552cSSchrodinger ZHU Yifan #include "src/__support/threads/mutex.h"            // Mutex
165442e15aSNick Desaulniers (paternity leave) 
175442e15aSNick Desaulniers (paternity leave) #include <sys/syscall.h> // For syscall numbers.
185442e15aSNick Desaulniers (paternity leave) 
19*5ff3ff33SPetr Hosek namespace LIBC_NAMESPACE_DECL {
205442e15aSNick Desaulniers (paternity leave) 
215442e15aSNick Desaulniers (paternity leave) int CndVar::wait(Mutex *m) {
225442e15aSNick Desaulniers (paternity leave)   // The goal is to perform "unlock |m| and wait" in an
235442e15aSNick Desaulniers (paternity leave)   // atomic operation. However, it is not possible to do it
245442e15aSNick Desaulniers (paternity leave)   // in the true sense so we do it in spirit. Before unlocking
255442e15aSNick Desaulniers (paternity leave)   // |m|, a new waiter object is added to the waiter queue with
265442e15aSNick Desaulniers (paternity leave)   // the waiter queue locked. Iff a signalling thread signals
275442e15aSNick Desaulniers (paternity leave)   // the waiter before the waiter actually starts waiting, the
285442e15aSNick Desaulniers (paternity leave)   // wait operation will not begin at all and the waiter immediately
295442e15aSNick Desaulniers (paternity leave)   // returns.
305442e15aSNick Desaulniers (paternity leave) 
315442e15aSNick Desaulniers (paternity leave)   CndWaiter waiter;
325442e15aSNick Desaulniers (paternity leave)   {
330694552cSSchrodinger ZHU Yifan     cpp::lock_guard ml(qmtx);
345442e15aSNick Desaulniers (paternity leave)     CndWaiter *old_back = nullptr;
355442e15aSNick Desaulniers (paternity leave)     if (waitq_front == nullptr) {
365442e15aSNick Desaulniers (paternity leave)       waitq_front = waitq_back = &waiter;
375442e15aSNick Desaulniers (paternity leave)     } else {
385442e15aSNick Desaulniers (paternity leave)       old_back = waitq_back;
395442e15aSNick Desaulniers (paternity leave)       waitq_back->next = &waiter;
405442e15aSNick Desaulniers (paternity leave)       waitq_back = &waiter;
415442e15aSNick Desaulniers (paternity leave)     }
425442e15aSNick Desaulniers (paternity leave) 
435442e15aSNick Desaulniers (paternity leave)     if (m->unlock() != MutexError::NONE) {
445442e15aSNick Desaulniers (paternity leave)       // If we do not remove the queued up waiter before returning,
455442e15aSNick Desaulniers (paternity leave)       // then another thread can potentially signal a non-existing
465442e15aSNick Desaulniers (paternity leave)       // waiter. Note also that we do this with |qmtx| locked. This
475442e15aSNick Desaulniers (paternity leave)       // ensures that another thread will not signal the withdrawing
485442e15aSNick Desaulniers (paternity leave)       // waiter.
495442e15aSNick Desaulniers (paternity leave)       waitq_back = old_back;
505442e15aSNick Desaulniers (paternity leave)       if (waitq_back == nullptr)
515442e15aSNick Desaulniers (paternity leave)         waitq_front = nullptr;
525442e15aSNick Desaulniers (paternity leave)       else
535442e15aSNick Desaulniers (paternity leave)         waitq_back->next = nullptr;
545442e15aSNick Desaulniers (paternity leave) 
555442e15aSNick Desaulniers (paternity leave)       return -1;
565442e15aSNick Desaulniers (paternity leave)     }
575442e15aSNick Desaulniers (paternity leave)   }
585442e15aSNick Desaulniers (paternity leave) 
595442e15aSNick Desaulniers (paternity leave)   waiter.futex_word.wait(WS_Waiting, cpp::nullopt, true);
605442e15aSNick Desaulniers (paternity leave) 
615442e15aSNick Desaulniers (paternity leave)   // At this point, if locking |m| fails, we can simply return as the
625442e15aSNick Desaulniers (paternity leave)   // queued up waiter would have been removed from the queue.
635442e15aSNick Desaulniers (paternity leave)   auto err = m->lock();
645442e15aSNick Desaulniers (paternity leave)   return err == MutexError::NONE ? 0 : -1;
655442e15aSNick Desaulniers (paternity leave) }
665442e15aSNick Desaulniers (paternity leave) 
675442e15aSNick Desaulniers (paternity leave) void CndVar::notify_one() {
685442e15aSNick Desaulniers (paternity leave)   // We don't use an RAII locker in this method as we want to unlock
695442e15aSNick Desaulniers (paternity leave)   // |qmtx| and signal the waiter using a single FUTEX_WAKE_OP signal.
705442e15aSNick Desaulniers (paternity leave)   qmtx.lock();
715442e15aSNick Desaulniers (paternity leave)   if (waitq_front == nullptr)
725442e15aSNick Desaulniers (paternity leave)     qmtx.unlock();
735442e15aSNick Desaulniers (paternity leave) 
745442e15aSNick Desaulniers (paternity leave)   CndWaiter *first = waitq_front;
755442e15aSNick Desaulniers (paternity leave)   waitq_front = waitq_front->next;
765442e15aSNick Desaulniers (paternity leave)   if (waitq_front == nullptr)
775442e15aSNick Desaulniers (paternity leave)     waitq_back = nullptr;
785442e15aSNick Desaulniers (paternity leave) 
79142afde0SSchrodinger ZHU Yifan   qmtx.reset();
805442e15aSNick Desaulniers (paternity leave) 
815442e15aSNick Desaulniers (paternity leave)   // this is a special WAKE_OP, so we use syscall directly
825442e15aSNick Desaulniers (paternity leave)   LIBC_NAMESPACE::syscall_impl<long>(
83142afde0SSchrodinger ZHU Yifan       FUTEX_SYSCALL_ID, &qmtx.get_raw_futex(), FUTEX_WAKE_OP, 1, 1,
845442e15aSNick Desaulniers (paternity leave)       &first->futex_word.val,
855442e15aSNick Desaulniers (paternity leave)       FUTEX_OP(FUTEX_OP_SET, WS_Signalled, FUTEX_OP_CMP_EQ, WS_Waiting));
865442e15aSNick Desaulniers (paternity leave) }
875442e15aSNick Desaulniers (paternity leave) 
885442e15aSNick Desaulniers (paternity leave) void CndVar::broadcast() {
890694552cSSchrodinger ZHU Yifan   cpp::lock_guard ml(qmtx);
905442e15aSNick Desaulniers (paternity leave)   uint32_t dummy_futex_word;
915442e15aSNick Desaulniers (paternity leave)   CndWaiter *waiter = waitq_front;
925442e15aSNick Desaulniers (paternity leave)   waitq_front = waitq_back = nullptr;
935442e15aSNick Desaulniers (paternity leave)   while (waiter != nullptr) {
945442e15aSNick Desaulniers (paternity leave)     // FUTEX_WAKE_OP is used instead of just FUTEX_WAKE as it allows us to
955442e15aSNick Desaulniers (paternity leave)     // atomically update the waiter status to WS_Signalled before waking
965442e15aSNick Desaulniers (paternity leave)     // up the waiter. A dummy location is used for the other futex of
975442e15aSNick Desaulniers (paternity leave)     // FUTEX_WAKE_OP.
985442e15aSNick Desaulniers (paternity leave)     LIBC_NAMESPACE::syscall_impl<long>(
995442e15aSNick Desaulniers (paternity leave)         FUTEX_SYSCALL_ID, &dummy_futex_word, FUTEX_WAKE_OP, 1, 1,
1005442e15aSNick Desaulniers (paternity leave)         &waiter->futex_word.val,
1015442e15aSNick Desaulniers (paternity leave)         FUTEX_OP(FUTEX_OP_SET, WS_Signalled, FUTEX_OP_CMP_EQ, WS_Waiting));
1025442e15aSNick Desaulniers (paternity leave)     waiter = waiter->next;
1035442e15aSNick Desaulniers (paternity leave)   }
1045442e15aSNick Desaulniers (paternity leave) }
1055442e15aSNick Desaulniers (paternity leave) 
106*5ff3ff33SPetr Hosek } // namespace LIBC_NAMESPACE_DECL
107