1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9
10#ifndef _LIBCPP_LATCH
11#define _LIBCPP_LATCH
12
13/*
14 latch synopsis
15
16namespace std
17{
18
19 class latch // since C++20
20 {
21 public:
22 static constexpr ptrdiff_t max() noexcept;
23
24 constexpr explicit latch(ptrdiff_t __expected);
25 ~latch();
26
27 latch(const latch&) = delete;
28 latch& operator=(const latch&) = delete;
29
30 void count_down(ptrdiff_t __update = 1);
31 bool try_wait() const noexcept;
32 void wait() const;
33 void arrive_and_wait(ptrdiff_t __update = 1);
34
35 private:
36 ptrdiff_t __counter; // exposition only
37 };
38
39}
40
41*/
42
43#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
44# include <__cxx03/__config>
45#else
46# include <__config>
47
48# if _LIBCPP_HAS_THREADS
49
50# include <__assert>
51# include <__atomic/atomic.h>
52# include <__atomic/atomic_sync.h>
53# include <__atomic/memory_order.h>
54# include <__cstddef/ptrdiff_t.h>
55# include <limits>
56# include <version>
57
58# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
59# pragma GCC system_header
60# endif
61
62_LIBCPP_PUSH_MACROS
63# include <__undef_macros>
64
65# if _LIBCPP_STD_VER >= 20
66
67_LIBCPP_BEGIN_NAMESPACE_STD
68
69class latch {
70 atomic<ptrdiff_t> __a_;
71
72public:
73 [[nodiscard]] static _LIBCPP_HIDE_FROM_ABI constexpr ptrdiff_t max() noexcept {
74 return numeric_limits<ptrdiff_t>::max();
75 }
76
77 inline _LIBCPP_HIDE_FROM_ABI constexpr explicit latch(ptrdiff_t __expected) : __a_(__expected) {
78 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
79 __expected >= 0,
80 "latch::latch(ptrdiff_t): latch cannot be "
81 "initialized with a negative value");
82 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
83 __expected <= max(),
84 "latch::latch(ptrdiff_t): latch cannot be "
85 "initialized with a value greater than max()");
86 }
87
88 _LIBCPP_HIDE_FROM_ABI ~latch() = default;
89 latch(const latch&) = delete;
90 latch& operator=(const latch&) = delete;
91
92 inline _LIBCPP_HIDE_FROM_ABI void count_down(ptrdiff_t __update = 1) {
93 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__update >= 0, "latch::count_down called with a negative value");
94 auto const __old = __a_.fetch_sub(__update, memory_order_release);
95 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
96 __update <= __old,
97 "latch::count_down called with a value greater "
98 "than the internal counter");
99 if (__old == __update)
100 __a_.notify_all();
101 }
102 [[nodiscard]] inline _LIBCPP_HIDE_FROM_ABI bool try_wait() const noexcept {
103 auto __value = __a_.load(memory_order_acquire);
104 return try_wait_impl(__value);
105 }
106 inline _LIBCPP_HIDE_FROM_ABI void wait() const {
107 std::__atomic_wait_unless(__a_, memory_order_acquire, [this](ptrdiff_t& __value) -> bool {
108 return try_wait_impl(__value);
109 });
110 }
111 inline _LIBCPP_HIDE_FROM_ABI void arrive_and_wait(ptrdiff_t __update = 1) {
112 _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__update >= 0, "latch::arrive_and_wait called with a negative value");
113 // other preconditions on __update are checked in count_down()
114
115 count_down(__update);
116 wait();
117 }
118
119private:
120 _LIBCPP_HIDE_FROM_ABI bool try_wait_impl(ptrdiff_t& __value) const noexcept { return __value == 0; }
121};
122
123_LIBCPP_END_NAMESPACE_STD
124
125# endif // _LIBCPP_STD_VER >= 20
126
127_LIBCPP_POP_MACROS
128
129# endif // _LIBCPP_HAS_THREADS
130
131# if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
132# include <atomic>
133# include <cstddef>
134# endif
135#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
136
137#endif // _LIBCPP_LATCH