summaryrefslogtreecommitdiff
path: root/libc/src/__support/threads/linux/futex_utils.h
blob: 943a99ab38c8ca917829e3d59f054b75b626a9a9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
//===--- Futex Wrapper ------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H
#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H

#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/limits.h"
#include "src/__support/CPP/optional.h"
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/macros/attributes.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/linux/futex_word.h"
#include "src/__support/time/linux/abs_timeout.h"
#include <linux/errno.h>
#include <linux/futex.h>

namespace LIBC_NAMESPACE_DECL {
class Futex : public cpp::Atomic<FutexWordType> {
public:
  using Timeout = internal::AbsTimeout;
  LIBC_INLINE constexpr Futex(FutexWordType value)
      : cpp::Atomic<FutexWordType>(value) {}
  LIBC_INLINE Futex &operator=(FutexWordType value) {
    cpp::Atomic<FutexWordType>::store(value);
    return *this;
  }
  LIBC_INLINE long wait(FutexWordType expected,
                        cpp::optional<Timeout> timeout = cpp::nullopt,
                        bool is_shared = false) {
    // use bitset variants to enforce abs_time
    uint32_t op = is_shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE;
    if (timeout && timeout->is_realtime()) {
      op |= FUTEX_CLOCK_REALTIME;
    }
    for (;;) {
      if (this->load(cpp::MemoryOrder::RELAXED) != expected)
        return 0;

      long ret = syscall_impl<long>(
          /* syscall number */ FUTEX_SYSCALL_ID,
          /* futex address */ this,
          /* futex operation  */ op,
          /* expected value */ expected,
          /* timeout */ timeout ? &timeout->get_timespec() : nullptr,
          /* ignored */ nullptr,
          /* bitset */ FUTEX_BITSET_MATCH_ANY);

      // continue waiting if interrupted; otherwise return the result
      // which should normally be 0 or -ETIMEOUT
      if (ret == -EINTR)
        continue;

      return ret;
    }
  }
  LIBC_INLINE long notify_one(bool is_shared = false) {
    return syscall_impl<long>(
        /* syscall number */ FUTEX_SYSCALL_ID,
        /* futex address */ this,
        /* futex operation  */ is_shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE,
        /* wake up limit */ 1,
        /* ignored */ nullptr,
        /* ignored */ nullptr,
        /* ignored */ 0);
  }
  LIBC_INLINE long notify_all(bool is_shared = false) {
    return syscall_impl<long>(
        /* syscall number */ FUTEX_SYSCALL_ID,
        /* futex address */ this,
        /* futex operation  */ is_shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE,
        /* wake up limit */ cpp::numeric_limits<int>::max(),
        /* ignored */ nullptr,
        /* ignored */ nullptr,
        /* ignored */ 0);
  }
};

static_assert(__is_standard_layout(Futex),
              "Futex must be a standard layout type.");
} // namespace LIBC_NAMESPACE_DECL

#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_FUTEX_UTILS_H