xref: /openbsd-src/gnu/llvm/libcxx/include/semaphore (revision 4bdff4bed0e3d54e55670334c7d0077db4170f86)
1037e7968Spatrick// -*- C++ -*-
2*4bdff4beSrobert//===----------------------------------------------------------------------===//
3037e7968Spatrick//
4037e7968Spatrick// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5037e7968Spatrick// See https://llvm.org/LICENSE.txt for license information.
6037e7968Spatrick// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7037e7968Spatrick//
8037e7968Spatrick//===----------------------------------------------------------------------===//
9037e7968Spatrick
10037e7968Spatrick#ifndef _LIBCPP_SEMAPHORE
11037e7968Spatrick#define _LIBCPP_SEMAPHORE
12037e7968Spatrick
13037e7968Spatrick/*
14037e7968Spatrick    semaphore synopsis
15037e7968Spatrick
16037e7968Spatricknamespace std {
17037e7968Spatrick
18037e7968Spatricktemplate<ptrdiff_t least_max_value = implementation-defined>
19037e7968Spatrickclass counting_semaphore
20037e7968Spatrick{
21037e7968Spatrickpublic:
22037e7968Spatrickstatic constexpr ptrdiff_t max() noexcept;
23037e7968Spatrick
24037e7968Spatrickconstexpr explicit counting_semaphore(ptrdiff_t desired);
25037e7968Spatrick~counting_semaphore();
26037e7968Spatrick
27037e7968Spatrickcounting_semaphore(const counting_semaphore&) = delete;
28037e7968Spatrickcounting_semaphore& operator=(const counting_semaphore&) = delete;
29037e7968Spatrick
30037e7968Spatrickvoid release(ptrdiff_t update = 1);
31037e7968Spatrickvoid acquire();
32037e7968Spatrickbool try_acquire() noexcept;
33037e7968Spatricktemplate<class Rep, class Period>
34037e7968Spatrick    bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time);
35037e7968Spatricktemplate<class Clock, class Duration>
36037e7968Spatrick    bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time);
37037e7968Spatrick
38037e7968Spatrickprivate:
39037e7968Spatrickptrdiff_t counter; // exposition only
40037e7968Spatrick};
41037e7968Spatrick
42037e7968Spatrickusing binary_semaphore = counting_semaphore<1>;
43037e7968Spatrick
44037e7968Spatrick}
45037e7968Spatrick
46037e7968Spatrick*/
47037e7968Spatrick
48*4bdff4beSrobert#include <__assert> // all public C++ headers provide the assertion handler
4976d0caaeSpatrick#include <__availability>
50*4bdff4beSrobert#include <__chrono/time_point.h>
51037e7968Spatrick#include <__config>
52*4bdff4beSrobert#include <__thread/timed_backoff_policy.h>
53037e7968Spatrick#include <__threading_support>
54037e7968Spatrick#include <atomic>
55*4bdff4beSrobert#include <limits>
56*4bdff4beSrobert#include <version>
57037e7968Spatrick
58037e7968Spatrick#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
59037e7968Spatrick#  pragma GCC system_header
60037e7968Spatrick#endif
61037e7968Spatrick
62037e7968Spatrick#ifdef _LIBCPP_HAS_NO_THREADS
63*4bdff4beSrobert# error "<semaphore> is not supported since libc++ has been configured without support for threads."
64037e7968Spatrick#endif
65037e7968Spatrick
6676d0caaeSpatrick_LIBCPP_PUSH_MACROS
6776d0caaeSpatrick#include <__undef_macros>
6876d0caaeSpatrick
69037e7968Spatrick#if _LIBCPP_STD_VER >= 14
70037e7968Spatrick
71037e7968Spatrick_LIBCPP_BEGIN_NAMESPACE_STD
72037e7968Spatrick
73037e7968Spatrick/*
74037e7968Spatrick
75*4bdff4beSrobert__atomic_semaphore_base is the general-case implementation.
7676d0caaeSpatrickIt is a typical Dijkstra semaphore algorithm over atomics, wait and notify
77037e7968Spatrickfunctions. It avoids contention against users' own use of those facilities.
78037e7968Spatrick
79037e7968Spatrick*/
80037e7968Spatrick
81037e7968Spatrickclass __atomic_semaphore_base
82037e7968Spatrick{
83*4bdff4beSrobert    __atomic_base<ptrdiff_t> __a_;
84037e7968Spatrick
85037e7968Spatrickpublic:
86037e7968Spatrick    _LIBCPP_INLINE_VISIBILITY
87*4bdff4beSrobert    constexpr explicit __atomic_semaphore_base(ptrdiff_t __count) : __a_(__count)
88037e7968Spatrick    {
89037e7968Spatrick    }
90037e7968Spatrick    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
91037e7968Spatrick    void release(ptrdiff_t __update = 1)
92037e7968Spatrick    {
93*4bdff4beSrobert        if(0 < __a_.fetch_add(__update, memory_order_release))
94037e7968Spatrick            ;
95037e7968Spatrick        else if(__update > 1)
96*4bdff4beSrobert            __a_.notify_all();
97037e7968Spatrick        else
98*4bdff4beSrobert            __a_.notify_one();
99037e7968Spatrick    }
100037e7968Spatrick    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
101037e7968Spatrick    void acquire()
102037e7968Spatrick    {
10376d0caaeSpatrick        auto const __test_fn = [this]() -> bool {
104*4bdff4beSrobert            auto __old = __a_.load(memory_order_relaxed);
105*4bdff4beSrobert            return (__old != 0) && __a_.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed);
106037e7968Spatrick        };
107*4bdff4beSrobert        __cxx_atomic_wait(&__a_.__a_, __test_fn);
108037e7968Spatrick    }
109037e7968Spatrick    template <class Rep, class Period>
110037e7968Spatrick    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
111037e7968Spatrick    bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time)
112037e7968Spatrick    {
113*4bdff4beSrobert        if (__rel_time == chrono::duration<Rep, Period>::zero())
114*4bdff4beSrobert            return try_acquire();
115*4bdff4beSrobert        auto const __test_fn = [this]() { return try_acquire(); };
116*4bdff4beSrobert        return std::__libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy(), __rel_time);
117*4bdff4beSrobert    }
118*4bdff4beSrobert    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
119*4bdff4beSrobert    bool try_acquire()
120*4bdff4beSrobert    {
121*4bdff4beSrobert        auto __old = __a_.load(memory_order_acquire);
122*4bdff4beSrobert        while (true) {
123037e7968Spatrick            if (__old == 0)
124037e7968Spatrick                return false;
125*4bdff4beSrobert            if (__a_.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed))
126037e7968Spatrick                return true;
127037e7968Spatrick        }
128037e7968Spatrick    }
129037e7968Spatrick};
130037e7968Spatrick
131037e7968Spatrick#define _LIBCPP_SEMAPHORE_MAX (numeric_limits<ptrdiff_t>::max())
132037e7968Spatrick
133037e7968Spatricktemplate<ptrdiff_t __least_max_value = _LIBCPP_SEMAPHORE_MAX>
134037e7968Spatrickclass counting_semaphore
135037e7968Spatrick{
136*4bdff4beSrobert    __atomic_semaphore_base __semaphore_;
137037e7968Spatrick
138037e7968Spatrickpublic:
139037e7968Spatrick    static constexpr ptrdiff_t max() noexcept {
140037e7968Spatrick        return __least_max_value;
141037e7968Spatrick    }
142037e7968Spatrick
143037e7968Spatrick    _LIBCPP_INLINE_VISIBILITY
144*4bdff4beSrobert    constexpr explicit counting_semaphore(ptrdiff_t __count) : __semaphore_(__count) { }
145037e7968Spatrick    ~counting_semaphore() = default;
146037e7968Spatrick
147037e7968Spatrick    counting_semaphore(const counting_semaphore&) = delete;
148037e7968Spatrick    counting_semaphore& operator=(const counting_semaphore&) = delete;
149037e7968Spatrick
150037e7968Spatrick    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
151037e7968Spatrick    void release(ptrdiff_t __update = 1)
152037e7968Spatrick    {
153*4bdff4beSrobert        __semaphore_.release(__update);
154037e7968Spatrick    }
155037e7968Spatrick    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
156037e7968Spatrick    void acquire()
157037e7968Spatrick    {
158*4bdff4beSrobert        __semaphore_.acquire();
159037e7968Spatrick    }
160037e7968Spatrick    template<class Rep, class Period>
161037e7968Spatrick    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
162037e7968Spatrick    bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time)
163037e7968Spatrick    {
164*4bdff4beSrobert        return __semaphore_.try_acquire_for(chrono::duration_cast<chrono::nanoseconds>(__rel_time));
165037e7968Spatrick    }
166037e7968Spatrick    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
167037e7968Spatrick    bool try_acquire()
168037e7968Spatrick    {
169*4bdff4beSrobert        return __semaphore_.try_acquire();
170037e7968Spatrick    }
171037e7968Spatrick    template <class Clock, class Duration>
172037e7968Spatrick    _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY
173037e7968Spatrick    bool try_acquire_until(chrono::time_point<Clock, Duration> const& __abs_time)
174037e7968Spatrick    {
175037e7968Spatrick        auto const current = Clock::now();
176037e7968Spatrick        if (current >= __abs_time)
177037e7968Spatrick            return try_acquire();
178037e7968Spatrick        else
179037e7968Spatrick            return try_acquire_for(__abs_time - current);
180037e7968Spatrick    }
181037e7968Spatrick};
182037e7968Spatrick
183037e7968Spatrickusing binary_semaphore = counting_semaphore<1>;
184037e7968Spatrick
185037e7968Spatrick_LIBCPP_END_NAMESPACE_STD
186037e7968Spatrick
187037e7968Spatrick#endif // _LIBCPP_STD_VER >= 14
188037e7968Spatrick
18976d0caaeSpatrick_LIBCPP_POP_MACROS
19076d0caaeSpatrick
191037e7968Spatrick#endif //_LIBCPP_SEMAPHORE
192