xref: /llvm-project/libcxx/include/semaphore (revision b9a2658a3e8bd13b0f9e7a8a440832a95b377216)
154fa9ecdSOlivier Giroux// -*- C++ -*-
2eb8650a7SLouis Dionne//===----------------------------------------------------------------------===//
354fa9ecdSOlivier Giroux//
454fa9ecdSOlivier Giroux// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
554fa9ecdSOlivier Giroux// See https://llvm.org/LICENSE.txt for license information.
654fa9ecdSOlivier Giroux// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
754fa9ecdSOlivier Giroux//
854fa9ecdSOlivier Giroux//===----------------------------------------------------------------------===//
954fa9ecdSOlivier Giroux
1054fa9ecdSOlivier Giroux#ifndef _LIBCPP_SEMAPHORE
1154fa9ecdSOlivier Giroux#define _LIBCPP_SEMAPHORE
1254fa9ecdSOlivier Giroux
1354fa9ecdSOlivier Giroux/*
1454fa9ecdSOlivier Giroux    semaphore synopsis
1554fa9ecdSOlivier Giroux
1654fa9ecdSOlivier Girouxnamespace std {
1754fa9ecdSOlivier Giroux
1854fa9ecdSOlivier Girouxtemplate<ptrdiff_t least_max_value = implementation-defined>
19bf1666fbSLouis Dionneclass counting_semaphore                          // since C++20
2054fa9ecdSOlivier Giroux{
2154fa9ecdSOlivier Girouxpublic:
2254fa9ecdSOlivier Girouxstatic constexpr ptrdiff_t max() noexcept;
2354fa9ecdSOlivier Giroux
2454fa9ecdSOlivier Girouxconstexpr explicit counting_semaphore(ptrdiff_t desired);
2554fa9ecdSOlivier Giroux~counting_semaphore();
2654fa9ecdSOlivier Giroux
2754fa9ecdSOlivier Girouxcounting_semaphore(const counting_semaphore&) = delete;
2854fa9ecdSOlivier Girouxcounting_semaphore& operator=(const counting_semaphore&) = delete;
2954fa9ecdSOlivier Giroux
3054fa9ecdSOlivier Girouxvoid release(ptrdiff_t update = 1);
3154fa9ecdSOlivier Girouxvoid acquire();
3254fa9ecdSOlivier Girouxbool try_acquire() noexcept;
3354fa9ecdSOlivier Girouxtemplate<class Rep, class Period>
3454fa9ecdSOlivier Giroux    bool try_acquire_for(const chrono::duration<Rep, Period>& rel_time);
3554fa9ecdSOlivier Girouxtemplate<class Clock, class Duration>
3654fa9ecdSOlivier Giroux    bool try_acquire_until(const chrono::time_point<Clock, Duration>& abs_time);
3754fa9ecdSOlivier Giroux
3854fa9ecdSOlivier Girouxprivate:
3954fa9ecdSOlivier Girouxptrdiff_t counter; // exposition only
4054fa9ecdSOlivier Giroux};
4154fa9ecdSOlivier Giroux
42bf1666fbSLouis Dionneusing binary_semaphore = counting_semaphore<1>; // since C++20
4354fa9ecdSOlivier Giroux
4454fa9ecdSOlivier Giroux}
4554fa9ecdSOlivier Giroux
4654fa9ecdSOlivier Giroux*/
4754fa9ecdSOlivier Giroux
48*b9a2658aSNikolas Klauser#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
49*b9a2658aSNikolas Klauser#  include <__cxx03/semaphore>
50*b9a2658aSNikolas Klauser#else
5134933d18SMark de Wever#  include <__config>
5234933d18SMark de Wever
53c6f3b7bcSNikolas Klauser#  if _LIBCPP_HAS_THREADS
5434933d18SMark de Wever
5537dca605SLouis Dionne#    include <__assert>
563a634076SLouis Dionne#    include <__atomic/atomic.h>
5770617a1aSNikolas Klauser#    include <__atomic/atomic_sync.h>
5870617a1aSNikolas Klauser#    include <__atomic/memory_order.h>
59489637e6SNikolas Klauser#    include <__chrono/time_point.h>
60e99c4906SNikolas Klauser#    include <__cstddef/ptrdiff_t.h>
61e375fd03SLouis Dionne#    include <__thread/poll_with_backoff.h>
627162fd75SLouis Dionne#    include <__thread/support.h>
63df51be85SLouis Dionne#    include <__thread/timed_backoff_policy.h>
64643df8faSLouis Dionne#    include <limits>
65bd6e6846SMark de Wever#    include <version>
6654fa9ecdSOlivier Giroux
6754fa9ecdSOlivier Giroux#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
6854fa9ecdSOlivier Giroux#      pragma GCC system_header
6954fa9ecdSOlivier Giroux#    endif
7054fa9ecdSOlivier Giroux
71d4303307SArthur O'Dwyer_LIBCPP_PUSH_MACROS
72d4303307SArthur O'Dwyer#    include <__undef_macros>
73d4303307SArthur O'Dwyer
74bf1666fbSLouis Dionne#    if _LIBCPP_STD_VER >= 20
7554fa9ecdSOlivier Giroux
7654fa9ecdSOlivier Giroux_LIBCPP_BEGIN_NAMESPACE_STD
7754fa9ecdSOlivier Giroux
7854fa9ecdSOlivier Giroux/*
7954fa9ecdSOlivier Giroux
80d0eaf753SArthur O'Dwyer__atomic_semaphore_base is the general-case implementation.
81b6f19174SArthur O'DwyerIt is a typical Dijkstra semaphore algorithm over atomics, wait and notify
8254fa9ecdSOlivier Girouxfunctions. It avoids contention against users' own use of those facilities.
8354fa9ecdSOlivier Giroux
8454fa9ecdSOlivier Giroux*/
8554fa9ecdSOlivier Giroux
8642a024faSEdoardo Sanguineti#      define _LIBCPP_SEMAPHORE_MAX (numeric_limits<ptrdiff_t>::max())
8742a024faSEdoardo Sanguineti
889783f28cSLouis Dionneclass __atomic_semaphore_base {
893a634076SLouis Dionne  atomic<ptrdiff_t> __a_;
9054fa9ecdSOlivier Giroux
9154fa9ecdSOlivier Girouxpublic:
929783f28cSLouis Dionne  _LIBCPP_HIDE_FROM_ABI constexpr explicit __atomic_semaphore_base(ptrdiff_t __count) : __a_(__count) {}
939783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void release(ptrdiff_t __update = 1) {
9442a024faSEdoardo Sanguineti    auto __old = __a_.fetch_add(__update, memory_order_release);
95dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
969783f28cSLouis Dionne        __update <= _LIBCPP_SEMAPHORE_MAX - __old, "update is greater than the expected value");
9782565885SHui    if (__old == 0) {
9884fc2c3cSNikolas Klauser      __a_.notify_all();
9982565885SHui    }
10054fa9ecdSOlivier Giroux  }
1019783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void acquire() {
102d681e103SLouis Dionne    std::__atomic_wait_unless(__a_, memory_order_relaxed, [this](ptrdiff_t& __old) {
103d681e103SLouis Dionne      return __try_acquire_impl(__old);
104d681e103SLouis Dionne    });
10554fa9ecdSOlivier Giroux  }
1061e24b4d3SNikolas Klauser  template <class _Rep, class _Period>
1079783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool
1089783f28cSLouis Dionne  try_acquire_for(chrono::duration<_Rep, _Period> const& __rel_time) {
1091e24b4d3SNikolas Klauser    if (__rel_time == chrono::duration<_Rep, _Period>::zero())
110c92a253cSArthur O'Dwyer      return try_acquire();
11182565885SHui    auto const __poll_fn = [this]() { return try_acquire(); };
11282565885SHui    return std::__libcpp_thread_poll_with_backoff(__poll_fn, __libcpp_timed_backoff_policy(), __rel_time);
113c92a253cSArthur O'Dwyer  }
1149783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool try_acquire() {
11595ebf2beSJan Kokemüller    auto __old = __a_.load(memory_order_relaxed);
11682565885SHui    return __try_acquire_impl(__old);
11782565885SHui  }
11882565885SHui
11982565885SHuiprivate:
12082565885SHui  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool __try_acquire_impl(ptrdiff_t& __old) {
121c92a253cSArthur O'Dwyer    while (true) {
12254fa9ecdSOlivier Giroux      if (__old == 0)
12354fa9ecdSOlivier Giroux        return false;
12495ebf2beSJan Kokemüller      if (__a_.compare_exchange_weak(__old, __old - 1, memory_order_acquire, memory_order_relaxed))
12554fa9ecdSOlivier Giroux        return true;
12654fa9ecdSOlivier Giroux    }
12754fa9ecdSOlivier Giroux  }
12854fa9ecdSOlivier Giroux};
12954fa9ecdSOlivier Giroux
13054fa9ecdSOlivier Girouxtemplate <ptrdiff_t __least_max_value = _LIBCPP_SEMAPHORE_MAX>
131bf1666fbSLouis Dionneclass counting_semaphore {
13284fc2c3cSNikolas Klauser  __atomic_semaphore_base __semaphore_;
13354fa9ecdSOlivier Giroux
13454fa9ecdSOlivier Girouxpublic:
13542a024faSEdoardo Sanguineti  static_assert(__least_max_value >= 0, "The least maximum value must be a positive number");
13642a024faSEdoardo Sanguineti
1379783f28cSLouis Dionne  static constexpr ptrdiff_t max() noexcept { return __least_max_value; }
13854fa9ecdSOlivier Giroux
1399783f28cSLouis Dionne  _LIBCPP_HIDE_FROM_ABI constexpr explicit counting_semaphore(ptrdiff_t __count) : __semaphore_(__count) {
140dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
14142a024faSEdoardo Sanguineti        __count >= 0,
14242a024faSEdoardo Sanguineti        "counting_semaphore::counting_semaphore(ptrdiff_t): counting_semaphore cannot be "
14342a024faSEdoardo Sanguineti        "initialized with a negative value");
144dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(
14542a024faSEdoardo Sanguineti        __count <= max(),
14642a024faSEdoardo Sanguineti        "counting_semaphore::counting_semaphore(ptrdiff_t): counting_semaphore cannot be "
14742a024faSEdoardo Sanguineti        "initialized with a value greater than max()");
14842a024faSEdoardo Sanguineti  }
14954fa9ecdSOlivier Giroux  ~counting_semaphore() = default;
15054fa9ecdSOlivier Giroux
15154fa9ecdSOlivier Giroux  counting_semaphore(const counting_semaphore&)            = delete;
15254fa9ecdSOlivier Giroux  counting_semaphore& operator=(const counting_semaphore&) = delete;
15354fa9ecdSOlivier Giroux
1549783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void release(ptrdiff_t __update = 1) {
155dc577520SKonstantin Varlamov    _LIBCPP_ASSERT_ARGUMENT_WITHIN_DOMAIN(__update >= 0, "counting_semaphore:release called with a negative value");
15684fc2c3cSNikolas Klauser    __semaphore_.release(__update);
15754fa9ecdSOlivier Giroux  }
1589783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void acquire() { __semaphore_.acquire(); }
1591e24b4d3SNikolas Klauser  template <class _Rep, class _Period>
1609783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool
1619783f28cSLouis Dionne  try_acquire_for(chrono::duration<_Rep, _Period> const& __rel_time) {
16284fc2c3cSNikolas Klauser    return __semaphore_.try_acquire_for(chrono::duration_cast<chrono::nanoseconds>(__rel_time));
16354fa9ecdSOlivier Giroux  }
1649783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool try_acquire() { return __semaphore_.try_acquire(); }
1651e24b4d3SNikolas Klauser  template <class _Clock, class _Duration>
1669783f28cSLouis Dionne  _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool
1679783f28cSLouis Dionne  try_acquire_until(chrono::time_point<_Clock, _Duration> const& __abs_time) {
1681e24b4d3SNikolas Klauser    auto const __current = _Clock::now();
169d05f8895SNikolas Klauser    if (__current >= __abs_time)
17054fa9ecdSOlivier Giroux      return try_acquire();
17154fa9ecdSOlivier Giroux    else
172d05f8895SNikolas Klauser      return try_acquire_for(__abs_time - __current);
17354fa9ecdSOlivier Giroux  }
17454fa9ecdSOlivier Giroux};
17554fa9ecdSOlivier Giroux
176bf1666fbSLouis Dionneusing binary_semaphore = counting_semaphore<1>;
17754fa9ecdSOlivier Giroux
17854fa9ecdSOlivier Giroux_LIBCPP_END_NAMESPACE_STD
17954fa9ecdSOlivier Giroux
180bf1666fbSLouis Dionne#    endif // _LIBCPP_STD_VER >= 20
181ab41129bSLouis Dionne
182d4303307SArthur O'Dwyer_LIBCPP_POP_MACROS
183d4303307SArthur O'Dwyer
184c6f3b7bcSNikolas Klauser#  endif // _LIBCPP_HAS_THREADS
185ef51e617SLouis Dionne
18670617a1aSNikolas Klauser#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
18770617a1aSNikolas Klauser#    include <atomic>
188e99c4906SNikolas Klauser#    include <cstddef>
18970617a1aSNikolas Klauser#  endif
190*b9a2658aSNikolas Klauser#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
19170617a1aSNikolas Klauser
19254fa9ecdSOlivier Giroux#endif // _LIBCPP_SEMAPHORE
193