brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · be74c18 Raw
107 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "src/__support/threads/CndVar.h"10#include "src/__support/CPP/mutex.h"11#include "src/__support/OSUtil/syscall.h"           // syscall_impl12#include "src/__support/macros/config.h"13#include "src/__support/threads/linux/futex_word.h" // FutexWordType14#include "src/__support/threads/linux/raw_mutex.h"  // RawMutex15#include "src/__support/threads/mutex.h"            // Mutex16 17#include <sys/syscall.h> // For syscall numbers.18 19namespace LIBC_NAMESPACE_DECL {20 21int CndVar::wait(Mutex *m) {22  // The goal is to perform "unlock |m| and wait" in an23  // atomic operation. However, it is not possible to do it24  // in the true sense so we do it in spirit. Before unlocking25  // |m|, a new waiter object is added to the waiter queue with26  // the waiter queue locked. Iff a signalling thread signals27  // the waiter before the waiter actually starts waiting, the28  // wait operation will not begin at all and the waiter immediately29  // returns.30 31  CndWaiter waiter;32  {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;41    }42 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-existing46      // waiter. Note also that we do this with |qmtx| locked. This47      // ensures that another thread will not signal the withdrawing48      // waiter.49      waitq_back = old_back;50      if (waitq_back == nullptr)51        waitq_front = nullptr;52      else53        waitq_back->next = nullptr;54 55      return -1;56    }57  }58 59  waiter.futex_word.wait(WS_Waiting, cpp::nullopt, true);60 61  // At this point, if locking |m| fails, we can simply return as the62  // queued up waiter would have been removed from the queue.63  auto err = m->lock();64  return err == MutexError::NONE ? 0 : -1;65}66 67void CndVar::notify_one() {68  // We don't use an RAII locker in this method as we want to unlock69  // |qmtx| and signal the waiter using a single FUTEX_WAKE_OP signal.70  qmtx.lock();71  if (waitq_front == nullptr)72    qmtx.unlock();73 74  CndWaiter *first = waitq_front;75  waitq_front = waitq_front->next;76  if (waitq_front == nullptr)77    waitq_back = nullptr;78 79  qmtx.reset();80 81  // this is a special WAKE_OP, so we use syscall directly82  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));86}87 88void 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 to95    // atomically update the waiter status to WS_Signalled before waking96    // up the waiter. A dummy location is used for the other futex of97    // 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;103  }104}105 106} // namespace LIBC_NAMESPACE_DECL107