(root)/
gcc-13.2.0/
libstdc++-v3/
include/
bits/
semaphore_base.h
       1  // -*- C++ -*- header.
       2  
       3  // Copyright (C) 2020-2023 Free Software Foundation, Inc.
       4  //
       5  // This file is part of the GNU ISO C++ Library.  This library is free
       6  // software; you can redistribute it and/or modify it under the
       7  // terms of the GNU General Public License as published by the
       8  // Free Software Foundation; either version 3, or (at your option)
       9  // any later version.
      10  
      11  // This library is distributed in the hope that it will be useful,
      12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14  // GNU General Public License for more details.
      15  
      16  // Under Section 7 of GPL version 3, you are granted additional
      17  // permissions described in the GCC Runtime Library Exception, version
      18  // 3.1, as published by the Free Software Foundation.
      19  
      20  // You should have received a copy of the GNU General Public License and
      21  // a copy of the GCC Runtime Library Exception along with this program;
      22  // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      23  // <http://www.gnu.org/licenses/>.
      24  
      25  /** @file bits/semaphore_base.h
      26   *  This is an internal header file, included by other library headers.
      27   *  Do not attempt to use it directly. @headername{semaphore}
      28   */
      29  
      30  #ifndef _GLIBCXX_SEMAPHORE_BASE_H
      31  #define _GLIBCXX_SEMAPHORE_BASE_H 1
      32  
      33  #pragma GCC system_header
      34  
      35  #include <bits/atomic_base.h>
      36  #include <bits/chrono.h>
      37  #if __cpp_lib_atomic_wait
      38  #include <bits/atomic_timed_wait.h>
      39  #include <ext/numeric_traits.h>
      40  #endif // __cpp_lib_atomic_wait
      41  
      42  #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
      43  # include <cerrno>	// errno, EINTR, EAGAIN etc.
      44  # include <limits.h>	// SEM_VALUE_MAX
      45  # include <semaphore.h>	// sem_t, sem_init, sem_wait, sem_post etc.
      46  #endif
      47  
      48  namespace std _GLIBCXX_VISIBILITY(default)
      49  {
      50  _GLIBCXX_BEGIN_NAMESPACE_VERSION
      51  
      52  #ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE
      53    struct __platform_semaphore
      54    {
      55      using __clock_t = chrono::system_clock;
      56  #ifdef SEM_VALUE_MAX
      57      static constexpr ptrdiff_t _S_max = SEM_VALUE_MAX;
      58  #else
      59      static constexpr ptrdiff_t _S_max = _POSIX_SEM_VALUE_MAX;
      60  #endif
      61  
      62      explicit __platform_semaphore(ptrdiff_t __count) noexcept
      63      {
      64        sem_init(&_M_semaphore, 0, __count);
      65      }
      66  
      67      __platform_semaphore(const __platform_semaphore&) = delete;
      68      __platform_semaphore& operator=(const __platform_semaphore&) = delete;
      69  
      70      ~__platform_semaphore()
      71      { sem_destroy(&_M_semaphore); }
      72  
      73      _GLIBCXX_ALWAYS_INLINE void
      74      _M_acquire() noexcept
      75      {
      76        for (;;)
      77  	{
      78  	  auto __err = sem_wait(&_M_semaphore);
      79  	  if (__err && (errno == EINTR))
      80  	    continue;
      81  	  else if (__err)
      82  	    std::__terminate();
      83  	  else
      84  	    break;
      85  	}
      86      }
      87  
      88      _GLIBCXX_ALWAYS_INLINE bool
      89      _M_try_acquire() noexcept
      90      {
      91        for (;;)
      92  	{
      93  	  auto __err = sem_trywait(&_M_semaphore);
      94  	  if (__err && (errno == EINTR))
      95  	    continue;
      96  	  else if (__err && (errno == EAGAIN))
      97  	    return false;
      98  	  else if (__err)
      99  	    std::__terminate();
     100  	  else
     101  	    break;
     102  	}
     103        return true;
     104      }
     105  
     106      _GLIBCXX_ALWAYS_INLINE void
     107      _M_release(std::ptrdiff_t __update) noexcept
     108      {
     109        for(; __update != 0; --__update)
     110  	{
     111  	   auto __err = sem_post(&_M_semaphore);
     112  	   if (__err)
     113  	     std::__terminate();
     114  	}
     115      }
     116  
     117      bool
     118      _M_try_acquire_until_impl(const chrono::time_point<__clock_t>& __atime)
     119        noexcept
     120      {
     121  
     122        auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
     123        auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
     124  
     125        struct timespec __ts =
     126        {
     127  	static_cast<std::time_t>(__s.time_since_epoch().count()),
     128  	static_cast<long>(__ns.count())
     129        };
     130  
     131        for (;;)
     132  	{
     133  	  if (auto __err = sem_timedwait(&_M_semaphore, &__ts))
     134  	    {
     135  	      if (errno == EINTR)
     136  		continue;
     137  	      else if (errno == ETIMEDOUT || errno == EINVAL)
     138  		return false;
     139  	      else
     140  		std::__terminate();
     141  	    }
     142  	  else
     143  	    break;
     144  	}
     145        return true;
     146      }
     147  
     148      template<typename _Clock, typename _Duration>
     149        bool
     150        _M_try_acquire_until(const chrono::time_point<_Clock,
     151  			   _Duration>& __atime) noexcept
     152        {
     153  	if constexpr (std::is_same_v<__clock_t, _Clock>)
     154  	  {
     155  	    return _M_try_acquire_until_impl(__atime);
     156  	  }
     157  	else
     158  	  {
     159  	    const typename _Clock::time_point __c_entry = _Clock::now();
     160  	    const auto __s_entry = __clock_t::now();
     161  	    const auto __delta = __atime - __c_entry;
     162  	    const auto __s_atime = __s_entry + __delta;
     163  	    if (_M_try_acquire_until_impl(__s_atime))
     164  	      return true;
     165  
     166  	    // We got a timeout when measured against __clock_t but
     167  	    // we need to check against the caller-supplied clock
     168  	    // to tell whether we should return a timeout.
     169  	    return (_Clock::now() < __atime);
     170  	  }
     171        }
     172  
     173      template<typename _Rep, typename _Period>
     174        _GLIBCXX_ALWAYS_INLINE bool
     175        _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
     176  	noexcept
     177        { return _M_try_acquire_until(__clock_t::now() + __rtime); }
     178  
     179    private:
     180      sem_t _M_semaphore;
     181    };
     182  #endif // _GLIBCXX_HAVE_POSIX_SEMAPHORE
     183  
     184  #if __cpp_lib_atomic_wait
     185    struct __atomic_semaphore
     186    {
     187      static constexpr ptrdiff_t _S_max = __gnu_cxx::__int_traits<int>::__max;
     188      explicit __atomic_semaphore(__detail::__platform_wait_t __count) noexcept
     189        : _M_counter(__count)
     190      {
     191        __glibcxx_assert(__count >= 0 && __count <= _S_max);
     192      }
     193  
     194      __atomic_semaphore(const __atomic_semaphore&) = delete;
     195      __atomic_semaphore& operator=(const __atomic_semaphore&) = delete;
     196  
     197      static _GLIBCXX_ALWAYS_INLINE bool
     198      _S_do_try_acquire(__detail::__platform_wait_t* __counter) noexcept
     199      {
     200        auto __old = __atomic_impl::load(__counter, memory_order::acquire);
     201        if (__old == 0)
     202  	return false;
     203  
     204        return __atomic_impl::compare_exchange_strong(__counter,
     205  						    __old, __old - 1,
     206  						    memory_order::acquire,
     207  						    memory_order::relaxed);
     208      }
     209  
     210      _GLIBCXX_ALWAYS_INLINE void
     211      _M_acquire() noexcept
     212      {
     213        auto const __pred =
     214  	[this] { return _S_do_try_acquire(&this->_M_counter); };
     215        std::__atomic_wait_address_bare(&_M_counter, __pred);
     216      }
     217  
     218      bool
     219      _M_try_acquire() noexcept
     220      {
     221        auto const __pred =
     222  	[this] { return _S_do_try_acquire(&this->_M_counter); };
     223        return std::__detail::__atomic_spin(__pred);
     224      }
     225  
     226      template<typename _Clock, typename _Duration>
     227        _GLIBCXX_ALWAYS_INLINE bool
     228        _M_try_acquire_until(const chrono::time_point<_Clock,
     229  			   _Duration>& __atime) noexcept
     230        {
     231  	auto const __pred =
     232  	  [this] { return _S_do_try_acquire(&this->_M_counter); };
     233  
     234  	return __atomic_wait_address_until_bare(&_M_counter, __pred, __atime);
     235        }
     236  
     237      template<typename _Rep, typename _Period>
     238        _GLIBCXX_ALWAYS_INLINE bool
     239        _M_try_acquire_for(const chrono::duration<_Rep, _Period>& __rtime)
     240  	noexcept
     241        {
     242  	auto const __pred =
     243  	  [this] { return _S_do_try_acquire(&this->_M_counter); };
     244  
     245  	return __atomic_wait_address_for_bare(&_M_counter, __pred, __rtime);
     246        }
     247  
     248      _GLIBCXX_ALWAYS_INLINE void
     249      _M_release(ptrdiff_t __update) noexcept
     250      {
     251        if (0 < __atomic_impl::fetch_add(&_M_counter, __update, memory_order_release))
     252  	return;
     253        if (__update > 1)
     254  	__atomic_notify_address_bare(&_M_counter, true);
     255        else
     256  	__atomic_notify_address_bare(&_M_counter, true);
     257  // FIXME - Figure out why this does not wake a waiting thread
     258  //	__atomic_notify_address_bare(&_M_counter, false);
     259      }
     260  
     261    private:
     262      alignas(__detail::__platform_wait_alignment)
     263      __detail::__platform_wait_t _M_counter;
     264    };
     265  #endif // __cpp_lib_atomic_wait
     266  
     267  // Note: the _GLIBCXX_USE_POSIX_SEMAPHORE macro can be used to force the
     268  // use of Posix semaphores (sem_t). Doing so however, alters the ABI.
     269  #if defined __cpp_lib_atomic_wait && !_GLIBCXX_USE_POSIX_SEMAPHORE
     270    using __semaphore_impl = __atomic_semaphore;
     271  #elif _GLIBCXX_HAVE_POSIX_SEMAPHORE
     272    using __semaphore_impl = __platform_semaphore;
     273  #endif
     274  
     275  _GLIBCXX_END_NAMESPACE_VERSION
     276  } // namespace std
     277  #endif // _GLIBCXX_SEMAPHORE_BASE_H