From aabb58f9381671aab73379e10359488dbecfe992 Mon Sep 17 00:00:00 2001 From: Thomas Rodgers Date: Mon, 24 Feb 2020 17:17:35 -0800 Subject: [PATCH] Add c++2a binary_semaphore * include/std/semaphore: New file. * include/std/version (__cpp_lib_semaphore): Add feature test macro. * include/Makefile.am (std_headers): add semaphore. * include/Makefile.in: Regenerate. * testsuite/30_threads/semaphore/1.cc: New test. * testsuite/30_threads/semaphore/2.cc: New test. * testsuite/30_threads/semaphore/binary_semaphore.cc: New test. * testsuite/30_threads/semaphore/try_acquire.cc: New test. * testsuite/30_threads/semaphore/try_acquire_for.cc: New test. * testsuite/30_threads/semaphore/try_acquire_until.cc: New test. --- libstdc++-v3/include/Makefile.am | 1 + libstdc++-v3/include/Makefile.in | 1 + libstdc++-v3/include/std/semaphore | 323 ++++++++++++++++++ libstdc++-v3/include/std/version | 2 + .../testsuite/30_threads/semaphore/1.cc | 27 ++ .../testsuite/30_threads/semaphore/2.cc | 27 ++ .../30_threads/semaphore/binary_semaphore.cc | 49 +++ .../30_threads/semaphore/try_acquire.cc | 36 ++ .../30_threads/semaphore/try_acquire_for.cc | 75 ++++ .../30_threads/semaphore/try_acquire_until.cc | 75 ++++ 10 files changed, 616 insertions(+) create mode 100644 libstdc++-v3/include/std/semaphore create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/1.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/2.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/binary_semaphore.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc create mode 100644 libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 89835759069..351941801ce 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -69,6 +69,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index 123d24bb1c6..7d856e4122e 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -414,6 +414,7 @@ std_headers = \ ${std_srcdir}/ratio \ ${std_srcdir}/regex \ ${std_srcdir}/scoped_allocator \ + ${std_srcdir}/semaphore \ ${std_srcdir}/set \ ${std_srcdir}/shared_mutex \ ${std_srcdir}/span \ diff --git a/libstdc++-v3/include/std/semaphore b/libstdc++-v3/include/std/semaphore new file mode 100644 index 00000000000..18b63426bcb --- /dev/null +++ b/libstdc++-v3/include/std/semaphore @@ -0,0 +1,323 @@ +// -*- C++ -*- + +// Copyright (C) 2019-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file include/semaphore + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_SEMAPHORE +#define _GLIBCXX_SEMAPHORE + +#include +#if defined(_GLIBCXX_HAS_GTHREADS) +#include +#endif + +#ifdef _GLIBCXX_USE_NANOSLEEP +# include // errno, EINTR +# include // nanosleep +#else +# ifdef _GLIBCXX_HAVE_SLEEP +# include +# elif defined(_GLIBCXX_HAVE_WIN32_SLEEP) +# include +# else +# error "No sleep function known for this target" +# endif +#endif + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX +#include +#include +#include +#include +#endif + +#include +#include + +#include +#if __cplusplus > 201703L +#define __cpp_lib_semaphore 201907L + +namespace std _GLIBCXX_VISIBILITY(default) +{ + _GLIBCXX_BEGIN_NAMESPACE_VERSION + + struct binary_semaphore + { + explicit binary_semaphore(int __d) : _M_counter(__d > 0) { } + + static constexpr std::ptrdiff_t + max() noexcept { return 1; } + + void + release() + { + _M_counter.fetch_add(1, memory_order::release); +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + _S_futex_notify_all((int*)(void*)&_M_counter); +#endif + } + + void + acquire() + { + while (!_M_try_acquire()) + { + if (_M_try_acquire_sleep()) + break; + } + } + + bool + try_acquire() noexcept + { + return _M_try_acquire_spin(1u, 0u); + } + + template + bool + try_acquire_for(const std::chrono::duration<_Rep, _Period>& __rel_time) + { return try_acquire_until(__clock_t::now() + __rel_time); } + + template + bool + try_acquire_until(const std::chrono::time_point<_Clock, _Duration>& __abs_time) + { + const auto __c_entry = _Clock::now(); + const auto __s_entry = __clock_t::now(); + const auto __delta = __abs_time -__c_entry; + const auto __s_atime = __s_entry + __delta; + bool __ret = _M_try_acquire(); + while (!!__ret && __s_atime < __clock_t::now()); + { +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + const auto __s = std::chrono::time_point_cast(__s_atime); + const auto __ns = std::chrono::duration_cast(__s_atime - __s); + if (_S_futex_wait_until((int*)(void*)&_M_counter, 0, + true, __s.time_since_epoch(), __ns)) + __ret = _M_try_acquire(); +#else + __ret = _M_try_acquire_sleep() +#endif + }; + return __ret; + } + + private: + static void + _S_relax() noexcept + { +#if defined __i386__ || defined __x86_64__ + __builtin_ia32_pause(); +#elif defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + static void + _S_yield() noexcept + { +#if defined _GLIBCXX_USE_SCHED_YIELD + __gthread_yield(); +#endif + } + + static void + _S_sleep_for(std::chrono::microseconds __rtime) + { + if (__rtime <= __rtime.zero()) + return; + auto __s = chrono::duration_cast(__rtime); + auto __ns = chrono::duration_cast(__rtime - __s); +#ifdef _GLIBCXX_USE_NANOSLEEP + __gthread_time_t __ts = + { + static_cast(__s.count()), + static_cast(__ns.count()) + }; + + while (::nanosleep(&__ts, &__ts) == -1 && errno == EINTR) + { } +#elif defined(_GLIBCXX_HAVE_SLEEP) + const auto __target = chrono::steady_clock::now() + __s + __ns; + while (true) + { + unsigned __secs = __s.count(); + if (__ns.count() > 0) + { +# ifdef _GLIBCXX_HAVE_USLEEP + long __us = __ns.count() / 1000; + if (__us == 0) + __us = 1; + ::usleep(__us); +# else + if (__ns.count() > 1000000 || __secs == 0) + ++__secs; // No sub-second sleep function, so round up. +#endif + } + + if (__secs > 0) + { + // Sleep in a loop to handle interruption by signals: + while ((__secs = ::sleep(__secs))) + { } + } + const auto __now = chrono::steady_clock::now(); + if (__now >= __target) + break; + __s = chrono::duration_cast(__target - __now); + __ns = chrono::duration_cast(__target - (__now + __s)); + } +#elif defined(_GLIBCXX_HAVE_WIN32_SLEEP) + unsigned long __ms = __ns.count() / 1000000; + if (__ns.count() > 0 && __ms == 0) + __ms = 1; + ::Sleep(chrono::milliseconds(__s).count() + __ms); +#endif + } + + + bool + _M_try_acquire_spin(unsigned __spin_ct1, unsigned __spin_ct2) + { + int __old = 1; + for (auto __spin = 0; __spin < __spin_ct1; ++__spin) + { + if (_M_counter.compare_exchange_weak(__old, 0, + memory_order::acquire, + memory_order::relaxed)) + return true; + __old = 1; + } + + for (auto __spin = 0; __spin < __spin_ct2; _S_relax(), ++__spin) + { + if (_M_counter.compare_exchange_weak(__old, 0, + memory_order::acquire, + memory_order::relaxed)) + return true; + __old = 1; + } + return false; + } + +#ifndef _GLIBCXX_HAVE_LINUX_FUTEX + struct __backoff + { + int _M_rtime_us = 64; + + void + _M_sleep() + { + _S_sleep_for(std::chrono::microseconds(_M_rtime_us)); + auto __next_rtime_us = _M_rtime_us + (_M_rtime_us >> 2); + _M_rtime_us = __next_rtime_us < 1024 ? __next_rtime_us : 1024; + } + }; +#endif + + bool + _M_try_acquire_sleep() + { + int __old = 1; +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + _S_futex_wait_until((int*)(void*)&_M_counter,__old); +#else + __backoff __b; + for (auto __spin = 0; ; __b._M_sleep(), ++__spin) + { + if (_M_counter.compare_exchange_weak(__old, 0, + memory_order::acquire, + memory_order::relaxed)) + return true; + __old = 1; + } +#endif + return false; + } + + using __clock_t = std::chrono::system_clock; + static constexpr unsigned _S_spin_ct1 = 16u; + static constexpr unsigned _S_spin_ct2 = 16u; + + bool + _M_try_acquire() + { + return _M_try_acquire_spin(_S_spin_ct1, _S_spin_ct2); + } + + atomic _M_counter; + + static const auto _S_futex_wake_op = 1u; + +#ifdef _GLIBCXX_HAVE_LINUX_FUTEX + bool + _S_futex_wait_until(int* __addr, int __val, + bool __has_timeout = false, + std::chrono::seconds __s = std::chrono::seconds::zero(), + std::chrono::nanoseconds __ns = std::chrono::nanoseconds::zero()) + { + if (!__has_timeout) + { + syscall (SYS_futex, __addr, 0, __val, nullptr); + // Can't do anything about an error here except abort, so ignore it. + } + else + { + struct timeval __tv; + gettimeofday(&__tv, NULL); + struct timespec __rt; + __rt.tv_sec = __s.count() - __tv.tv_sec; + __rt.tv_nsec = __ns.count() - __tv.tv_usec * 1000; + if (__rt.tv_nsec < 0) + { + __rt.tv_nsec += 1000000000; + --__rt.tv_sec; + } + if (__rt.tv_sec < 0) + return false; + + if (syscall (SYS_futex, __addr, 0, __val, &__rt) == -1) + { + if (errno == ETIMEDOUT) + return false; + } + } + return true; + } + + void + _S_futex_notify_all(int* __addr) + { + syscall (SYS_futex, __addr, 1, INT_MAX); + } +#endif // _GLIBCXX_HAVE_LINUX_FUTEX + }; + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace +#endif // _GLIBCXX_SEMAPHORE +#endif // __cplusplus > 201703L diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index d8a97767453..6ab9d0924a7 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -188,6 +188,8 @@ #define __cpp_lib_interpolate 201902L #ifdef _GLIBCXX_HAS_GTHREADS # define __cpp_lib_jthread 201907L +// TODO uncomment when binary/counted_semaphore are fully implemented +// # define __cpp_lib_semaphore 201907L #endif #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc new file mode 100644 index 00000000000..1bbca687fc3 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in " +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in " +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc new file mode 100644 index 00000000000..b96b8a59c64 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2019-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a" } +// { dg-do compile { target c++2a } } + +#include + +#ifndef __cpp_lib_semaphore +# error "Feature-test macro for semaphore missing in " +#elif __cpp_lib_semaphore != 201907L +# error "Feature-test macro for semaphore has wrong value in " +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/binary_semaphore.cc b/libstdc++-v3/testsuite/30_threads/semaphore/binary_semaphore.cc new file mode 100644 index 00000000000..661f0b0e32d --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/binary_semaphore.cc @@ -0,0 +1,49 @@ +// Copyright (C) 2019-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include +#include +#include + +#include + +int main() +{ + std::binary_semaphore s(1); + + auto ar = [&]() + { + for(auto i = 0u; i < 512; ++i) + { + s.acquire(); + std::this_thread::sleep_for(std::chrono::microseconds(1)); + s.release(); + } + }; + + std::thread t(ar); + ar(); + + t.join(); + + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc new file mode 100644 index 00000000000..295cfa9bca8 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -0,0 +1,36 @@ +// Copyright (C) 2019-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include + +int main() +{ + std::binary_semaphore s(1); + + s.acquire(); + if (s.try_acquire()) + return -1; + s.release(); + if (!s.try_acquire()) + return -1; + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc new file mode 100644 index 00000000000..9e945094db8 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -0,0 +1,75 @@ +// Copyright (C) 2019-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include +#include +#include +#include +#include + +#include + +int main() +{ + using lock_type = std::unique_lock; + + bool ready = false; + lock_type::mutex_type m; + std::condition_variable cv; + + std::binary_semaphore s(1); + auto ar =[&] + { + s.acquire(); + { + lock_type l(m); + ready = true; + } + cv.notify_one(); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + s.release(); + }; + + std::thread t1(ar); + { + lock_type l(m, std::defer_lock); + cv.wait(l, [&] { return ready; }); + + if (s.try_acquire_for(std::chrono::milliseconds(100))) + return -1; + t1.join(); + if (!s.try_acquire()) + return -1; + s.release(); + ready = false; + } + + std::thread t2(ar); + { + lock_type l(m, std::defer_lock); + cv.wait(l, [&] { return ready; }); + if (!s.try_acquire_for(std::chrono::milliseconds(1009))) + return -1; + t2.join(); + } + return 0; +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc new file mode 100644 index 00000000000..46365ed69a2 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -0,0 +1,75 @@ +// Copyright (C) 2019-2020 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-options "-std=gnu++2a -pthread" } +// { dg-do run { target c++2a } } +// { dg-require-effective-target pthread } +// { dg-require-gthreads "" } + +#include +#include +#include +#include +#include + +int main() +{ + using lock_type = std::unique_lock; + + bool ready = false; + lock_type::mutex_type m; + std::condition_variable cv; + + auto abst = std::chrono::steady_clock::now(); + + std::binary_semaphore s(1); + auto ar =[&] + { + s.acquire(); + { + lock_type l(m); + ready = true; + } + cv.notify_one(); + std::this_thread::sleep_until(abst + std::chrono::milliseconds(500)); + s.release(); + }; + + std::thread t1(ar); + { + lock_type l(m, std::defer_lock); + cv.wait(l, [&] { return ready; }); + + if (s.try_acquire_until(abst + std::chrono::milliseconds(100))) + return -1; + t1.join(); + if (!s.try_acquire()) + return -1; + s.release(); + ready = false; + } + + std::thread t2(ar); + { + lock_type l(m, std::defer_lock); + cv.wait(l, [&] { return ready; }); + if (!s.try_acquire_until(abst + std::chrono::milliseconds(1009))) + return -1; + t2.join(); + } + return 0; +} -- 2.24.1