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