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> 1954fa9ecdSOlivier Girouxclass counting_semaphore 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 4254fa9ecdSOlivier Girouxusing binary_semaphore = counting_semaphore<1>; 4354fa9ecdSOlivier Giroux 4454fa9ecdSOlivier Giroux} 4554fa9ecdSOlivier Giroux 4654fa9ecdSOlivier Giroux*/ 4754fa9ecdSOlivier Giroux 48385cc25aSLouis Dionne#include <__assert> // all public C++ headers provide the assertion handler 492eadbc86SLouis Dionne#include <__availability> 50489637e6SNikolas Klauser#include <__chrono/time_point.h> 51bfbd73f8SArthur O'Dwyer#include <__config> 52df51be85SLouis Dionne#include <__thread/timed_backoff_policy.h> 5354fa9ecdSOlivier Giroux#include <__threading_support> 5454fa9ecdSOlivier Giroux#include <atomic> 55*643df8faSLouis Dionne#include <limits> 56bd6e6846SMark de Wever#include <version> 5754fa9ecdSOlivier Giroux 5854fa9ecdSOlivier Giroux#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) 5954fa9ecdSOlivier Giroux# pragma GCC system_header 6054fa9ecdSOlivier Giroux#endif 6154fa9ecdSOlivier Giroux 6254fa9ecdSOlivier Giroux#ifdef _LIBCPP_HAS_NO_THREADS 631c4b31c3SLouis Dionne# error "<semaphore> is not supported since libc++ has been configured without support for threads." 6454fa9ecdSOlivier Giroux#endif 6554fa9ecdSOlivier Giroux 66d4303307SArthur O'Dwyer_LIBCPP_PUSH_MACROS 67d4303307SArthur O'Dwyer#include <__undef_macros> 68d4303307SArthur O'Dwyer 69ab41129bSLouis Dionne#if _LIBCPP_STD_VER >= 14 7054fa9ecdSOlivier Giroux 7154fa9ecdSOlivier Giroux_LIBCPP_BEGIN_NAMESPACE_STD 7254fa9ecdSOlivier Giroux 7354fa9ecdSOlivier Giroux/* 7454fa9ecdSOlivier Giroux 75d0eaf753SArthur O'Dwyer__atomic_semaphore_base is the general-case implementation. 76b6f19174SArthur O'DwyerIt is a typical Dijkstra semaphore algorithm over atomics, wait and notify 7754fa9ecdSOlivier Girouxfunctions. It avoids contention against users' own use of those facilities. 7854fa9ecdSOlivier Giroux 7954fa9ecdSOlivier Giroux*/ 8054fa9ecdSOlivier Giroux 8154fa9ecdSOlivier Girouxclass __atomic_semaphore_base 8254fa9ecdSOlivier Giroux{ 8354fa9ecdSOlivier Giroux __atomic_base<ptrdiff_t> __a; 8454fa9ecdSOlivier Giroux 8554fa9ecdSOlivier Girouxpublic: 8654fa9ecdSOlivier Giroux _LIBCPP_INLINE_VISIBILITY 87c9af0e61SArthur O'Dwyer constexpr explicit __atomic_semaphore_base(ptrdiff_t __count) : __a(__count) 8854fa9ecdSOlivier Giroux { 8954fa9ecdSOlivier Giroux } 9046c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 9154fa9ecdSOlivier Giroux void release(ptrdiff_t __update = 1) 9254fa9ecdSOlivier Giroux { 9354fa9ecdSOlivier Giroux if(0 < __a.fetch_add(__update, memory_order_release)) 9454fa9ecdSOlivier Giroux ; 9554fa9ecdSOlivier Giroux else if(__update > 1) 9654fa9ecdSOlivier Giroux __a.notify_all(); 9754fa9ecdSOlivier Giroux else 9854fa9ecdSOlivier Giroux __a.notify_one(); 9954fa9ecdSOlivier Giroux } 10046c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 10154fa9ecdSOlivier Giroux void acquire() 10254fa9ecdSOlivier Giroux { 1032e4755ffSLouis Dionne auto const __test_fn = [this]() -> bool { 10454fa9ecdSOlivier Giroux auto __old = __a.load(memory_order_relaxed); 10554fa9ecdSOlivier Giroux return (__old != 0) && __a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed); 10654fa9ecdSOlivier Giroux }; 10754fa9ecdSOlivier Giroux __cxx_atomic_wait(&__a.__a_, __test_fn); 10854fa9ecdSOlivier Giroux } 10954fa9ecdSOlivier Giroux template <class Rep, class Period> 11046c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 11154fa9ecdSOlivier Giroux bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time) 11254fa9ecdSOlivier Giroux { 113c92a253cSArthur O'Dwyer if (__rel_time == chrono::duration<Rep, Period>::zero()) 114c92a253cSArthur O'Dwyer return try_acquire(); 115c92a253cSArthur O'Dwyer auto const __test_fn = [this]() { return try_acquire(); }; 116c92a253cSArthur O'Dwyer return __libcpp_thread_poll_with_backoff(__test_fn, __libcpp_timed_backoff_policy(), __rel_time); 117c92a253cSArthur O'Dwyer } 118c92a253cSArthur O'Dwyer _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 119c92a253cSArthur O'Dwyer bool try_acquire() 120c92a253cSArthur O'Dwyer { 12154fa9ecdSOlivier Giroux auto __old = __a.load(memory_order_acquire); 122c92a253cSArthur O'Dwyer while (true) { 12354fa9ecdSOlivier Giroux if (__old == 0) 12454fa9ecdSOlivier Giroux return false; 12554fa9ecdSOlivier Giroux if (__a.compare_exchange_strong(__old, __old - 1, memory_order_acquire, memory_order_relaxed)) 12654fa9ecdSOlivier Giroux return true; 12754fa9ecdSOlivier Giroux } 12854fa9ecdSOlivier Giroux } 12954fa9ecdSOlivier Giroux}; 13054fa9ecdSOlivier Giroux 13162138846Sogiroux#define _LIBCPP_SEMAPHORE_MAX (numeric_limits<ptrdiff_t>::max()) 13262138846Sogiroux 13354fa9ecdSOlivier Girouxtemplate<ptrdiff_t __least_max_value = _LIBCPP_SEMAPHORE_MAX> 13454fa9ecdSOlivier Girouxclass counting_semaphore 13554fa9ecdSOlivier Giroux{ 136d0eaf753SArthur O'Dwyer __atomic_semaphore_base __semaphore; 13754fa9ecdSOlivier Giroux 13854fa9ecdSOlivier Girouxpublic: 13954fa9ecdSOlivier Giroux static constexpr ptrdiff_t max() noexcept { 14054fa9ecdSOlivier Giroux return __least_max_value; 14154fa9ecdSOlivier Giroux } 14254fa9ecdSOlivier Giroux 14354fa9ecdSOlivier Giroux _LIBCPP_INLINE_VISIBILITY 144c9af0e61SArthur O'Dwyer constexpr explicit counting_semaphore(ptrdiff_t __count) : __semaphore(__count) { } 14554fa9ecdSOlivier Giroux ~counting_semaphore() = default; 14654fa9ecdSOlivier Giroux 14754fa9ecdSOlivier Giroux counting_semaphore(const counting_semaphore&) = delete; 14854fa9ecdSOlivier Giroux counting_semaphore& operator=(const counting_semaphore&) = delete; 14954fa9ecdSOlivier Giroux 15046c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 15154fa9ecdSOlivier Giroux void release(ptrdiff_t __update = 1) 15254fa9ecdSOlivier Giroux { 15354fa9ecdSOlivier Giroux __semaphore.release(__update); 15454fa9ecdSOlivier Giroux } 15546c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 15654fa9ecdSOlivier Giroux void acquire() 15754fa9ecdSOlivier Giroux { 15854fa9ecdSOlivier Giroux __semaphore.acquire(); 15954fa9ecdSOlivier Giroux } 16054fa9ecdSOlivier Giroux template<class Rep, class Period> 16146c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 16254fa9ecdSOlivier Giroux bool try_acquire_for(chrono::duration<Rep, Period> const& __rel_time) 16354fa9ecdSOlivier Giroux { 16454fa9ecdSOlivier Giroux return __semaphore.try_acquire_for(chrono::duration_cast<chrono::nanoseconds>(__rel_time)); 16554fa9ecdSOlivier Giroux } 16646c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 16754fa9ecdSOlivier Giroux bool try_acquire() 16854fa9ecdSOlivier Giroux { 169c92a253cSArthur O'Dwyer return __semaphore.try_acquire(); 17054fa9ecdSOlivier Giroux } 17154fa9ecdSOlivier Giroux template <class Clock, class Duration> 17246c38761SLouis Dionne _LIBCPP_AVAILABILITY_SYNC _LIBCPP_INLINE_VISIBILITY 17354fa9ecdSOlivier Giroux bool try_acquire_until(chrono::time_point<Clock, Duration> const& __abs_time) 17454fa9ecdSOlivier Giroux { 17554fa9ecdSOlivier Giroux auto const current = Clock::now(); 17654fa9ecdSOlivier Giroux if (current >= __abs_time) 17754fa9ecdSOlivier Giroux return try_acquire(); 17854fa9ecdSOlivier Giroux else 17954fa9ecdSOlivier Giroux return try_acquire_for(__abs_time - current); 18054fa9ecdSOlivier Giroux } 18154fa9ecdSOlivier Giroux}; 18254fa9ecdSOlivier Giroux 18354fa9ecdSOlivier Girouxusing binary_semaphore = counting_semaphore<1>; 18454fa9ecdSOlivier Giroux 18554fa9ecdSOlivier Giroux_LIBCPP_END_NAMESPACE_STD 18654fa9ecdSOlivier Giroux 187ab41129bSLouis Dionne#endif // _LIBCPP_STD_VER >= 14 188ab41129bSLouis Dionne 189d4303307SArthur O'Dwyer_LIBCPP_POP_MACROS 190d4303307SArthur O'Dwyer 19154fa9ecdSOlivier Giroux#endif //_LIBCPP_SEMAPHORE 192