summaryrefslogtreecommitdiff
path: root/absl/synchronization/internal/futex.h
blob: 55078f14c0e13928e178fa97fef6ee13bffa9d47 (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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2020 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_
#define ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_

#include "absl/base/config.h"

#ifndef _WIN32
#include <sys/time.h>
#include <unistd.h>
#endif

#ifdef __linux__
#include <linux/futex.h>
#include <sys/syscall.h>
#endif

#include <errno.h>
#include <stdio.h>
#include <time.h>

#include <atomic>
#include <cstdint>

#include "absl/base/optimization.h"
#include "absl/synchronization/internal/kernel_timeout.h"

#ifdef ABSL_INTERNAL_HAVE_FUTEX
#error ABSL_INTERNAL_HAVE_FUTEX may not be set on the command line
#elif defined(__BIONIC__)
// Bionic supports all the futex operations we need even when some of the futex
// definitions are missing.
#define ABSL_INTERNAL_HAVE_FUTEX
#elif defined(__linux__) && defined(FUTEX_CLOCK_REALTIME)
// FUTEX_CLOCK_REALTIME requires Linux >= 2.6.28.
#define ABSL_INTERNAL_HAVE_FUTEX
#endif

#ifdef ABSL_INTERNAL_HAVE_FUTEX

namespace absl {
ABSL_NAMESPACE_BEGIN
namespace synchronization_internal {

// Some Android headers are missing these definitions even though they
// support these futex operations.
#ifdef __BIONIC__
#ifndef SYS_futex
#define SYS_futex __NR_futex
#endif
#ifndef FUTEX_WAIT_BITSET
#define FUTEX_WAIT_BITSET 9
#endif
#ifndef FUTEX_PRIVATE_FLAG
#define FUTEX_PRIVATE_FLAG 128
#endif
#ifndef FUTEX_CLOCK_REALTIME
#define FUTEX_CLOCK_REALTIME 256
#endif
#ifndef FUTEX_BITSET_MATCH_ANY
#define FUTEX_BITSET_MATCH_ANY 0xFFFFFFFF
#endif
#endif

#if defined(__NR_futex_time64) && !defined(SYS_futex_time64)
#define SYS_futex_time64 __NR_futex_time64
#endif

#if defined(SYS_futex_time64) && !defined(SYS_futex)
#define SYS_futex SYS_futex_time64
#endif

class FutexImpl {
 public:
  // Atomically check that `*v == val`, and if it is, then sleep until the until
  // woken by `Wake()`.
  static int Wait(std::atomic<int32_t>* v, int32_t val) {
    return WaitAbsoluteTimeout(v, val, nullptr);
  }

  // Atomically check that `*v == val`, and if it is, then sleep until
  // CLOCK_REALTIME reaches `*abs_timeout`, or until woken by `Wake()`.
  static int WaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val,
                                 const struct timespec* abs_timeout) {
    // https://locklessinc.com/articles/futex_cheat_sheet/
    // Unlike FUTEX_WAIT, FUTEX_WAIT_BITSET uses absolute time.
    auto err =
        syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
                FUTEX_WAIT_BITSET | FUTEX_PRIVATE_FLAG | FUTEX_CLOCK_REALTIME,
                val, abs_timeout, nullptr, FUTEX_BITSET_MATCH_ANY);
    if (err != 0) {
      return -errno;
    }
    return 0;
  }

  // Atomically check that `*v == val`, and if it is, then sleep until
  // `*rel_timeout` has elapsed, or until woken by `Wake()`.
  static int WaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val,
                                 const struct timespec* rel_timeout) {
    // Atomically check that the futex value is still 0, and if it
    // is, sleep until abs_timeout or until woken by FUTEX_WAKE.
    auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
                       FUTEX_PRIVATE_FLAG, val, rel_timeout);
    if (err != 0) {
      return -errno;
    }
    return 0;
  }

  // Wakes at most `count` waiters that have entered the sleep state on `v`.
  static int Wake(std::atomic<int32_t>* v, int32_t count) {
    auto err = syscall(SYS_futex, reinterpret_cast<int32_t*>(v),
                       FUTEX_WAKE | FUTEX_PRIVATE_FLAG, count);
    if (ABSL_PREDICT_FALSE(err < 0)) {
      return -errno;
    }
    return 0;
  }
};

class Futex : public FutexImpl {};

}  // namespace synchronization_internal
ABSL_NAMESPACE_END
}  // namespace absl

#endif  // ABSL_INTERNAL_HAVE_FUTEX

#endif  // ABSL_SYNCHRONIZATION_INTERNAL_FUTEX_H_