public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Add support for C++20 barriers
@ 2020-09-07 23:34 Thomas Rodgers
  2020-11-04 17:29 ` [PATCH] libstdc++: " Thomas Rodgers
  0 siblings, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2020-09-07 23:34 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers, Thomas Rodgers

Re-sending

Adds <barrier>

	* include/Makefile.am (std_headers): Add new header.
	* include/Makefile.in: Regenerate.
        * include/bits/atomic_base.h (__atomic_base<_Itp>::_M_wait): Define.
        (__atomic_base<_Itp>::wait): Delegate to _M_wait.
	* include/std/barrier: New file.
	* testsuite/30_thread/barrier/1.cc: New test.
	* testsuite/30_thread/barrier/2.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
	* testsuite/30_thread/barrier/arrive.cc: Likewise.
	* testsuite/30_thread/barrier/completion.cc: Likewise.
	* testsuite/30_thread/barrier/max.cc: Likewise.
---
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   3 +-
 libstdc++-v3/include/bits/atomic_base.h       |  16 +-
 libstdc++-v3/include/std/barrier              | 230 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   1 +
 .../testsuite/30_threads/barrier/1.cc         |  27 ++
 .../testsuite/30_threads/barrier/2.cc         |  27 ++
 .../testsuite/30_threads/barrier/arrive.cc    |  34 +++
 .../30_threads/barrier/arrive_and_drop.cc     |  32 +++
 .../30_threads/barrier/arrive_and_wait.cc     |  33 +++
 .../30_threads/barrier/completion.cc          |  39 +++
 .../testsuite/30_threads/barrier/max.cc       |  26 ++
 12 files changed, 465 insertions(+), 4 deletions(-)
 create mode 100644 libstdc++-v3/include/std/barrier
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index ef8acd4a389..bae97852348 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -30,6 +30,7 @@ std_headers = \
 	${std_srcdir}/any \
 	${std_srcdir}/array \
 	${std_srcdir}/atomic \
+	${std_srcdir}/barrier \
 	${std_srcdir}/bit \
 	${std_srcdir}/bitset \
 	${std_srcdir}/charconv \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index c121d993fee..98f9941c2d4 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -229,6 +229,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       __atomic_load(&_M_i, &__v, int(__m));
       return __v == __GCC_ATOMIC_TEST_AND_SET_TRUEVAL;
     }
+
 #endif // C++20
 
     _GLIBCXX_ALWAYS_INLINE void
@@ -566,13 +567,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
 #if __cplusplus > 201703L
+      template<typename _Pred>
+	_GLIBCXX_ALWAYS_INLINE void
+	_M_wait(__int_type __old, _Pred&& __pred,
+		memory_order __m) const noexcept
+	{
+	  std::__atomic_wait(&_M_i, __old,
+			     std::forward<_Pred&&>(__pred));
+	}
+
       _GLIBCXX_ALWAYS_INLINE void
       wait(__int_type __old,
 	  memory_order __m = memory_order_seq_cst) const noexcept
       {
-	std::__atomic_wait(&_M_i, __old,
-			   [__m, this, __old]
-			   { return this->load(__m) != __old; });
+	_M_wait(__old, [__m, this, __old]()
+		{ return this->load(__m) != __old; },
+		__m);
       }
 
       // TODO add const volatile overload
diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
new file mode 100644
index 00000000000..870db7db2ac
--- /dev/null
+++ b/libstdc++-v3/include/std/barrier
@@ -0,0 +1,230 @@
+// <barrier> -*- C++ -*-
+// This implementation is based on libcxx/include/barrier
+//===-- barrier.h --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+#ifndef _GLIBCXX_BARRIER
+#define _GLIBCXX_BARRIER 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_barrier 201907L
+
+#include <bits/c++config.h>
+
+#if defined(_GLIBCXX_HAS_GTHREADS)
+#include <bits/atomic_base.h>
+#include <bits/atomic_wait.h>
+#include <bits/functional_hash.h>
+#include <ext/numeric_traits.h>
+
+#include <memory>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  struct __empty_completion
+  {
+    _GLIBCXX_ALWAYS_INLINE void
+    operator()() noexcept
+    { }
+  };
+
+/*
+
+The default implementation of __barrier_base is a classic tree barrier.
+
+It looks different from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+  using __barrier_phase_t = uint8_t;
+
+  template<class _CompletionF>
+    class __barrier_base
+    {
+      struct alignas(64) /* naturally-align the heap state */ __state_t
+      {
+	struct
+	{
+	  __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
+	} __tickets[64];
+      };
+
+      ptrdiff_t _M_expected;
+      unique_ptr<char[]> _M_state_allocation;
+      __state_t* 		_M_state;
+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
+      _CompletionF _M_completion;
+      __atomic_base<__barrier_phase_t> _M_phase;
+
+      static __gthread_t
+      _S_get_tid() noexcept
+      {
+#ifdef __GLIBC__
+	// For the GNU C library pthread_self() is usable without linking to
+	// libpthread.so but returns 0, so we cannot use it in single-threaded
+	// programs, because this_thread::get_id() != thread::id{} must be true.
+	// We know that pthread_t is an integral type in the GNU C library.
+	if (!__gthread_active_p())
+	  return 1;
+#endif
+	return __gthread_self();
+      }
+
+      bool
+      _M_arrive(__barrier_phase_t __old_phase)
+      {
+	__barrier_phase_t const __half_step = __old_phase + 1,
+				__full_step = __old_phase + 2;
+	size_t __current_expected = _M_expected;
+	size_t __current = _Hash_impl::hash(_S_get_tid())
+				% ((_M_expected + 1) >> 1);
+	for (int __round = 0;; ++__round)
+	  {
+	    if (__current_expected <= 1)
+		return true;
+	    size_t const __end_node = ((__current_expected + 1) >> 1),
+			 __last_node = __end_node - 1;
+	    for (;;++__current)
+	      {
+		if (__current == __end_node)
+		  __current = 0;
+		__barrier_phase_t __expect = __old_phase;
+		if (__current == __last_node && (__current_expected & 1))
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;     // I'm 1 in 1, go to next __round
+		  }
+		else if (_M_state[__current].__tickets[__round].__phase
+			 .compare_exchange_strong(__expect, __half_step,
+						  memory_order_acq_rel))
+		  {
+		    return false; // I'm 1 in 2, done with arrival
+		  }
+		else if (__expect == __half_step)
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;    // I'm 2 in 2, go to next __round
+		  }
+	      }
+	    __current_expected = __last_node + 1;
+	    __current >>= 1;
+	  }
+      }
+
+    public:
+      using arrival_token = __barrier_phase_t;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
+
+      __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
+	  : _M_expected(__expected), _M_expected_adjustment(0),
+	    _M_completion(move(__completion)), _M_phase(0)
+      {
+	size_t const __count = (_M_expected + 1) >> 1;
+	size_t const __size = sizeof(__state_t) * __count;
+	size_t __allocation_size = __size + alignof(__state_t);
+	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
+	void* __allocation = _M_state_allocation.get();
+	void* const __state = align(alignof(__state_t), __size, __allocation,
+				      __allocation_size);
+	_M_state = new (__state) __state_t[__count];
+      }
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update)
+      {
+	auto const __old_phase = _M_phase.load(memory_order_relaxed);
+	for(; __update; --__update)
+	  {
+	    if(_M_arrive(__old_phase))
+	      {
+		_M_completion();
+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
+		_M_expected_adjustment.store(0, memory_order_relaxed);
+		_M_phase.store(__old_phase + 2, memory_order_release);
+		_M_phase.notify_all();
+	      }
+	  }
+	return __old_phase;
+      }
+
+      void
+      wait(arrival_token&& __old_phase) const
+      {
+	auto const __test_fn = [=, this]
+	  {
+	    return _M_phase.load(memory_order_acquire) != __old_phase;
+	  };
+	_M_phase._M_wait(__old_phase, __test_fn, memory_order_acquire);
+      }
+
+      void
+      arrive_and_drop()
+      {
+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
+	(void)arrive(1);
+      }
+    };
+
+  template<class _CompletionF = __empty_completion>
+    class barrier
+    {
+      __barrier_base<_CompletionF> _M_b;
+    public:
+      using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __barrier_base<_CompletionF>::max(); }
+
+      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
+	  : _M_b(__count, std::move(__completion))
+      { }
+
+      barrier(barrier const&) = delete;
+      barrier& operator=(barrier const&) = delete;
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update = 1)
+      { return _M_b.arrive(__update); }
+
+      void
+      wait(arrival_token&& __phase) const
+      { _M_b.wait(std::move(__phase)); }
+
+      void
+      arrive_and_wait()
+      { wait(arrive()); }
+
+      void
+      arrive_and_drop()
+      { _M_b.arrive_and_drop(); }
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // _GLIBCXX_HAS_GTHREADS
+#endif // __cplusplus > 201703L
+#endif // _GLIBCXX_BARRIER
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 72d7769ebcf..4ff81732db1 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -195,6 +195,7 @@
 #undef __cpp_lib_constexpr_char_traits
 #define __cpp_lib_array_constexpr 201811L
 #define __cpp_lib_assume_aligned 201811L
+#define __cpp_lib_barrier 201907L
 #define __cpp_lib_bind_front 201907L
 // FIXME: #define __cpp_lib_execution 201902L
 #define __cpp_lib_integer_comparison_functions 202002L
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
new file mode 100644
index 00000000000..0b38160a58b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <barrier>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <barrier>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <barrier>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
new file mode 100644
index 00000000000..1d8d83639e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <version>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
new file mode 100644
index 00000000000..9e0cf0fc00f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -0,0 +1,34 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
new file mode 100644
index 00000000000..6a1bc6afaa3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -0,0 +1,32 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
new file mode 100644
index 00000000000..eee5ae053d6
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -0,0 +1,33 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
new file mode 100644
index 00000000000..d075f55cbe7
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -0,0 +1,39 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+#include <iostream>
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&] { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+        b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  VERIFY( x == 10 );
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
new file mode 100644
index 00000000000..c8efe549873
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
@@ -0,0 +1,26 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+  return 0;
+}
-- 
2.26.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] libstdc++: Add support for C++20 barriers
  2020-09-07 23:34 [PATCH] Add support for C++20 barriers Thomas Rodgers
@ 2020-11-04 17:29 ` Thomas Rodgers
  2020-11-04 18:41   ` Thomas Rodgers
                     ` (2 more replies)
  0 siblings, 3 replies; 23+ messages in thread
From: Thomas Rodgers @ 2020-11-04 17:29 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

Adds <barrier>

libstdc++/ChangeLog:

	* include/Makefile.am (std_headers): Add new header.
	* include/Makefile.in: Regenerate.
	* include/std/barrier: New file.
	* testsuite/30_thread/barrier/1.cc: New test.
	* testsuite/30_thread/barrier/2.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
	* testsuite/30_thread/barrier/arrive.cc: Likewise.
	* testsuite/30_thread/barrier/completion.cc: Likewise.
	* testsuite/30_thread/barrier/max.cc: Likewise.
---
 libstdc++-v3/include/std/barrier              | 248 ++++++++++++++++++
 .../testsuite/30_threads/barrier/1.cc         |  27 ++
 .../testsuite/30_threads/barrier/2.cc         |  27 ++
 .../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
 .../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
 .../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
 .../30_threads/barrier/completion.cc          |  54 ++++
 .../testsuite/30_threads/barrier/max.cc       |  44 ++++
 8 files changed, 551 insertions(+)
 create mode 100644 libstdc++-v3/include/std/barrier
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc

diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
new file mode 100644
index 00000000000..80e6d668cf5
--- /dev/null
+++ b/libstdc++-v3/include/std/barrier
@@ -0,0 +1,248 @@
+// <barrier> -*- C++ -*-
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/include/barrier
+//===-- barrier.h --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+#ifndef _GLIBCXX_BARRIER
+#define _GLIBCXX_BARRIER 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_barrier 201907L
+
+#include <bits/c++config.h>
+
+#if defined(_GLIBCXX_HAS_GTHREADS)
+#include <bits/atomic_base.h>
+#include <bits/atomic_wait.h>
+#include <bits/functional_hash.h>
+#include <ext/numeric_traits.h>
+
+#include <memory>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  struct __empty_completion
+  {
+    _GLIBCXX_ALWAYS_INLINE void
+    operator()() noexcept
+    { }
+  };
+
+/*
+
+The default implementation of __barrier_base is a classic tree barrier.
+
+It looks different from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+  using __barrier_phase_t = uint8_t;
+
+  template<class _CompletionF>
+    class __barrier_base
+    {
+      struct alignas(64) /* naturally-align the heap state */ __state_t
+      {
+	struct
+	{
+	  __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
+	} __tickets[64];
+      };
+
+      ptrdiff_t _M_expected;
+      unique_ptr<char[]> _M_state_allocation;
+      __state_t* 		_M_state;
+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
+      _CompletionF _M_completion;
+      __atomic_base<__barrier_phase_t> _M_phase;
+
+      static __gthread_t
+      _S_get_tid() noexcept
+      {
+#ifdef __GLIBC__
+	// For the GNU C library pthread_self() is usable without linking to
+	// libpthread.so but returns 0, so we cannot use it in single-threaded
+	// programs, because this_thread::get_id() != thread::id{} must be true.
+	// We know that pthread_t is an integral type in the GNU C library.
+	if (!__gthread_active_p())
+	  return 1;
+#endif
+	return __gthread_self();
+      }
+
+      bool
+      _M_arrive(__barrier_phase_t __old_phase)
+      {
+	__barrier_phase_t const __half_step = __old_phase + 1,
+				__full_step = __old_phase + 2;
+	size_t __current_expected = _M_expected;
+	size_t __current = _Hash_impl::hash(_S_get_tid())
+				% ((_M_expected + 1) >> 1);
+	for (int __round = 0;; ++__round)
+	  {
+	    if (__current_expected <= 1)
+		return true;
+	    size_t const __end_node = ((__current_expected + 1) >> 1),
+			 __last_node = __end_node - 1;
+	    for (;;++__current)
+	      {
+		if (__current == __end_node)
+		  __current = 0;
+		__barrier_phase_t __expect = __old_phase;
+		if (__current == __last_node && (__current_expected & 1))
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;     // I'm 1 in 1, go to next __round
+		  }
+		else if (_M_state[__current].__tickets[__round].__phase
+			 .compare_exchange_strong(__expect, __half_step,
+						  memory_order_acq_rel))
+		  {
+		    return false; // I'm 1 in 2, done with arrival
+		  }
+		else if (__expect == __half_step)
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;    // I'm 2 in 2, go to next __round
+		  }
+	      }
+	    __current_expected = __last_node + 1;
+	    __current >>= 1;
+	  }
+      }
+
+    public:
+      using arrival_token = __barrier_phase_t;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
+
+      __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
+	  : _M_expected(__expected), _M_expected_adjustment(0),
+	    _M_completion(move(__completion)), _M_phase(0)
+      {
+	size_t const __count = (_M_expected + 1) >> 1;
+	size_t const __size = sizeof(__state_t) * __count;
+	size_t __allocation_size = __size + alignof(__state_t);
+	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
+	void* __allocation = _M_state_allocation.get();
+	void* const __state = align(alignof(__state_t), __size, __allocation,
+				      __allocation_size);
+	_M_state = new (__state) __state_t[__count];
+      }
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update)
+      {
+	auto const __old_phase = _M_phase.load(memory_order_relaxed);
+	for(; __update; --__update)
+	  {
+	    if(_M_arrive(__old_phase))
+	      {
+		_M_completion();
+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
+		_M_expected_adjustment.store(0, memory_order_relaxed);
+		_M_phase.store(__old_phase + 2, memory_order_release);
+		_M_phase.notify_all();
+	      }
+	  }
+	return __old_phase;
+      }
+
+      void
+      wait(arrival_token&& __old_phase) const
+      {
+	auto const __test_fn = [=, this]
+	  {
+	    return _M_phase.load(memory_order_acquire) != __old_phase;
+	  };
+	_M_phase._M_wait(__old_phase, __test_fn, memory_order_acquire);
+      }
+
+      void
+      arrive_and_drop()
+      {
+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
+	(void)arrive(1);
+      }
+    };
+
+  template<class _CompletionF = __empty_completion>
+    class barrier
+    {
+      __barrier_base<_CompletionF> _M_b;
+    public:
+      using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __barrier_base<_CompletionF>::max(); }
+
+      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
+	  : _M_b(__count, std::move(__completion))
+      { }
+
+      barrier(barrier const&) = delete;
+      barrier& operator=(barrier const&) = delete;
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update = 1)
+      { return _M_b.arrive(__update); }
+
+      void
+      wait(arrival_token&& __phase) const
+      { _M_b.wait(std::move(__phase)); }
+
+      void
+      arrive_and_wait()
+      { wait(arrive()); }
+
+      void
+      arrive_and_drop()
+      { _M_b.arrive_and_drop(); }
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // _GLIBCXX_HAS_GTHREADS
+#endif // __cplusplus > 201703L
+#endif // _GLIBCXX_BARRIER
+
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
new file mode 100644
index 00000000000..0b38160a58b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <barrier>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <barrier>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <barrier>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
new file mode 100644
index 00000000000..1d8d83639e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <version>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
new file mode 100644
index 00000000000..c128d5ea794
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -0,0 +1,51 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
new file mode 100644
index 00000000000..b7f9404bc5d
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -0,0 +1,49 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
new file mode 100644
index 00000000000..b2940ae7c34
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -0,0 +1,51 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
new file mode 100644
index 00000000000..8f37eecd8ed
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -0,0 +1,54 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&] { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+	b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  VERIFY( x == 10 );
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
new file mode 100644
index 00000000000..720037bb2c7
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
@@ -0,0 +1,44 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+  return 0;
+}
-- 
2.26.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-04 17:29 ` [PATCH] libstdc++: " Thomas Rodgers
@ 2020-11-04 18:41   ` Thomas Rodgers
  2020-11-04 18:52     ` Jonathan Wakely
  2020-11-04 18:50   ` Jonathan Wakely
  2020-11-21  0:30   ` Thomas Rodgers
  2 siblings, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2020-11-04 18:41 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

IGNORE the previous version of this patch please.

Adds <barrier>

libstdc++/ChangeLog:

	* include/Makefile.am (std_headers): Add new header.
	* include/Makefile.in: Regenerate.
	* include/std/barrier: New file.
	* testsuite/30_thread/barrier/1.cc: New test.
	* testsuite/30_thread/barrier/2.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
	* testsuite/30_thread/barrier/arrive.cc: Likewise.
	* testsuite/30_thread/barrier/completion.cc: Likewise.
	* testsuite/30_thread/barrier/max.cc: Likewise.
---
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 libstdc++-v3/include/bits/atomic_base.h       |  11 +-
 libstdc++-v3/include/std/barrier              | 248 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   1 +
 .../testsuite/30_threads/barrier/1.cc         |  27 ++
 .../testsuite/30_threads/barrier/2.cc         |  27 ++
 .../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
 .../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
 .../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
 .../30_threads/barrier/completion.cc          |  54 ++++
 .../testsuite/30_threads/barrier/max.cc       |  44 ++++
 12 files changed, 562 insertions(+), 3 deletions(-)
 create mode 100644 libstdc++-v3/include/std/barrier
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 382e94322c1..9e497835ee0 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -30,6 +30,7 @@ std_headers = \
 	${std_srcdir}/any \
 	${std_srcdir}/array \
 	${std_srcdir}/atomic \
+	${std_srcdir}/barrier \
 	${std_srcdir}/bit \
 	${std_srcdir}/bitset \
 	${std_srcdir}/charconv \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index dd4db926592..1ad34719d3e 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -603,13 +603,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
 #if __cplusplus > 201703L
+      template<typename _Func>
+	_GLIBCXX_ALWAYS_INLINE void
+	_M_wait(__int_type __old, const _Func& __fn) const noexcept
+	{ std::__atomic_wait(&_M_i, __old, __fn); }
+
       _GLIBCXX_ALWAYS_INLINE void
       wait(__int_type __old,
 	  memory_order __m = memory_order_seq_cst) const noexcept
       {
-	std::__atomic_wait(&_M_i, __old,
-			   [__m, this, __old]
-			   { return this->load(__m) != __old; });
+	_M_wait(__old,
+		[__m, this, __old]
+		{ return this->load(__m) != __old; });
       }
 
       // TODO add const volatile overload
diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
new file mode 100644
index 00000000000..50654b00a0c
--- /dev/null
+++ b/libstdc++-v3/include/std/barrier
@@ -0,0 +1,248 @@
+// <barrier> -*- C++ -*-
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/include/barrier
+//===-- barrier.h --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+#ifndef _GLIBCXX_BARRIER
+#define _GLIBCXX_BARRIER 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_barrier 201907L
+
+#include <bits/c++config.h>
+
+#if defined(_GLIBCXX_HAS_GTHREADS)
+#include <bits/atomic_base.h>
+#include <bits/atomic_wait.h>
+#include <bits/functional_hash.h>
+#include <ext/numeric_traits.h>
+
+#include <memory>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  struct __empty_completion
+  {
+    _GLIBCXX_ALWAYS_INLINE void
+    operator()() noexcept
+    { }
+  };
+
+/*
+
+The default implementation of __barrier_base is a classic tree barrier.
+
+It looks different from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+  using __barrier_phase_t = uint8_t;
+
+  template<class _CompletionF>
+    class __barrier_base
+    {
+      struct alignas(64) /* naturally-align the heap state */ __state_t
+      {
+	struct
+	{
+	  __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
+	} __tickets[64];
+      };
+
+      ptrdiff_t _M_expected;
+      unique_ptr<char[]> _M_state_allocation;
+      __state_t* 		_M_state;
+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
+      _CompletionF _M_completion;
+      __atomic_base<__barrier_phase_t> _M_phase;
+
+      static __gthread_t
+      _S_get_tid() noexcept
+      {
+#ifdef __GLIBC__
+	// For the GNU C library pthread_self() is usable without linking to
+	// libpthread.so but returns 0, so we cannot use it in single-threaded
+	// programs, because this_thread::get_id() != thread::id{} must be true.
+	// We know that pthread_t is an integral type in the GNU C library.
+	if (!__gthread_active_p())
+	  return 1;
+#endif
+	return __gthread_self();
+      }
+
+      bool
+      _M_arrive(__barrier_phase_t __old_phase)
+      {
+	__barrier_phase_t const __half_step = __old_phase + 1,
+				__full_step = __old_phase + 2;
+	size_t __current_expected = _M_expected;
+	size_t __current = _Hash_impl::hash(_S_get_tid())
+				% ((_M_expected + 1) >> 1);
+	for (int __round = 0;; ++__round)
+	  {
+	    if (__current_expected <= 1)
+		return true;
+	    size_t const __end_node = ((__current_expected + 1) >> 1),
+			 __last_node = __end_node - 1;
+	    for (;;++__current)
+	      {
+		if (__current == __end_node)
+		  __current = 0;
+		__barrier_phase_t __expect = __old_phase;
+		if (__current == __last_node && (__current_expected & 1))
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;     // I'm 1 in 1, go to next __round
+		  }
+		else if (_M_state[__current].__tickets[__round].__phase
+			 .compare_exchange_strong(__expect, __half_step,
+						  memory_order_acq_rel))
+		  {
+		    return false; // I'm 1 in 2, done with arrival
+		  }
+		else if (__expect == __half_step)
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;    // I'm 2 in 2, go to next __round
+		  }
+	      }
+	    __current_expected = __last_node + 1;
+	    __current >>= 1;
+	  }
+      }
+
+    public:
+      using arrival_token = __barrier_phase_t;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
+
+      __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
+	  : _M_expected(__expected), _M_expected_adjustment(0),
+	    _M_completion(move(__completion)), _M_phase(0)
+      {
+	size_t const __count = (_M_expected + 1) >> 1;
+	size_t const __size = sizeof(__state_t) * __count;
+	size_t __allocation_size = __size + alignof(__state_t);
+	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
+	void* __allocation = _M_state_allocation.get();
+	void* const __state = align(alignof(__state_t), __size, __allocation,
+				      __allocation_size);
+	_M_state = new (__state) __state_t[__count];
+      }
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update)
+      {
+	auto const __old_phase = _M_phase.load(memory_order_relaxed);
+	for(; __update; --__update)
+	  {
+	    if(_M_arrive(__old_phase))
+	      {
+		_M_completion();
+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
+		_M_expected_adjustment.store(0, memory_order_relaxed);
+		_M_phase.store(__old_phase + 2, memory_order_release);
+		_M_phase.notify_all();
+	      }
+	  }
+	return __old_phase;
+      }
+
+      void
+      wait(arrival_token&& __old_phase) const
+      {
+	auto const __test_fn = [=, this]
+	  {
+	    return _M_phase.load(memory_order_acquire) != __old_phase;
+	  };
+	_M_phase._M_wait(__old_phase, __test_fn);
+      }
+
+      void
+      arrive_and_drop()
+      {
+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
+	(void)arrive(1);
+      }
+    };
+
+  template<class _CompletionF = __empty_completion>
+    class barrier
+    {
+      __barrier_base<_CompletionF> _M_b;
+    public:
+      using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __barrier_base<_CompletionF>::max(); }
+
+      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
+	  : _M_b(__count, std::move(__completion))
+      { }
+
+      barrier(barrier const&) = delete;
+      barrier& operator=(barrier const&) = delete;
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update = 1)
+      { return _M_b.arrive(__update); }
+
+      void
+      wait(arrival_token&& __phase) const
+      { _M_b.wait(std::move(__phase)); }
+
+      void
+      arrive_and_wait()
+      { wait(arrive()); }
+
+      void
+      arrive_and_drop()
+      { _M_b.arrive_and_drop(); }
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // _GLIBCXX_HAS_GTHREADS
+#endif // __cplusplus > 201703L
+#endif // _GLIBCXX_BARRIER
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index dcfbbb9ee54..da261414f65 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -197,6 +197,7 @@
 #if _GLIBCXX_HOSTED
 #define __cpp_lib_array_constexpr 201811L
 #define __cpp_lib_assume_aligned 201811L
+#define __cpp_lib_barrier 201907L
 #define __cpp_lib_bind_front 201907L
 // FIXME: #define __cpp_lib_execution 201902L
 #define __cpp_lib_integer_comparison_functions 202002L
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
new file mode 100644
index 00000000000..0b38160a58b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <barrier>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <barrier>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <barrier>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
new file mode 100644
index 00000000000..1d8d83639e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <version>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
new file mode 100644
index 00000000000..c128d5ea794
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -0,0 +1,51 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
new file mode 100644
index 00000000000..b7f9404bc5d
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -0,0 +1,49 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
new file mode 100644
index 00000000000..b2940ae7c34
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -0,0 +1,51 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
new file mode 100644
index 00000000000..8f37eecd8ed
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -0,0 +1,54 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&] { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+	b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  VERIFY( x == 10 );
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
new file mode 100644
index 00000000000..720037bb2c7
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
@@ -0,0 +1,44 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+  return 0;
+}
-- 
2.26.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-04 17:29 ` [PATCH] libstdc++: " Thomas Rodgers
  2020-11-04 18:41   ` Thomas Rodgers
@ 2020-11-04 18:50   ` Jonathan Wakely
  2020-11-04 19:43     ` Thomas Rodgers
  2020-11-21  0:30   ` Thomas Rodgers
  2 siblings, 1 reply; 23+ messages in thread
From: Jonathan Wakely @ 2020-11-04 18:50 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 04/11/20 09:29 -0800, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>Adds <barrier>
>
>libstdc++/ChangeLog:
>
>	* include/Makefile.am (std_headers): Add new header.
>	* include/Makefile.in: Regenerate.
>	* include/std/barrier: New file.
>	* testsuite/30_thread/barrier/1.cc: New test.
>	* testsuite/30_thread/barrier/2.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive.cc: Likewise.
>	* testsuite/30_thread/barrier/completion.cc: Likewise.
>	* testsuite/30_thread/barrier/max.cc: Likewise.
>---
> libstdc++-v3/include/std/barrier              | 248 ++++++++++++++++++
> .../testsuite/30_threads/barrier/1.cc         |  27 ++
> .../testsuite/30_threads/barrier/2.cc         |  27 ++
> .../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
> .../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
> .../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
> .../30_threads/barrier/completion.cc          |  54 ++++
> .../testsuite/30_threads/barrier/max.cc       |  44 ++++
> 8 files changed, 551 insertions(+)
> create mode 100644 libstdc++-v3/include/std/barrier
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc
>
>diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
>new file mode 100644
>index 00000000000..80e6d668cf5
>--- /dev/null
>+++ b/libstdc++-v3/include/std/barrier
>@@ -0,0 +1,248 @@
>+// <barrier> -*- C++ -*-
>+
>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// This implementation is based on libcxx/include/barrier
>+//===-- barrier.h --------------------------------------------------===//
>+//
>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>+// See https://llvm.org/LICENSE.txt for license information.
>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>+//
>+//===---------------------------------------------------------------===//
>+
>+#ifndef _GLIBCXX_BARRIER
>+#define _GLIBCXX_BARRIER 1
>+
>+#pragma GCC system_header
>+
>+#if __cplusplus > 201703L
>+#define __cpp_lib_barrier 201907L

This feature test macro will be defined unconditionally, even if
_GLIBCXX_HAS_GTHREADS is not defined. It should be inside the check
for gthreads.

You're also missing an edit to <version> (which should depend on the
same conditions).


>+#include <bits/c++config.h>
>+
>+#if defined(_GLIBCXX_HAS_GTHREADS)
>+#include <bits/atomic_base.h>
>+#include <bits/atomic_wait.h>
>+#include <bits/functional_hash.h>
>+#include <ext/numeric_traits.h>
>+
>+#include <memory>
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+  struct __empty_completion
>+  {
>+    _GLIBCXX_ALWAYS_INLINE void
>+    operator()() noexcept
>+    { }
>+  };
>+
>+/*
>+
>+The default implementation of __barrier_base is a classic tree barrier.
>+
>+It looks different from literature pseudocode for two main reasons:
>+ 1. Threads that call into std::barrier functions do not provide indices,
>+    so a numbering step is added before the actual barrier algorithm,
>+    appearing as an N+1 round to the N rounds of the tree barrier.
>+ 2. A great deal of attention has been paid to avoid cache line thrashing
>+    by flattening the tree structure into cache-line sized arrays, that
>+    are indexed in an efficient way.
>+
>+*/
>+
>+  using __barrier_phase_t = uint8_t;

Please add <stdint.h> or <cstdint> since you're using uint8_t
(it's currently included by <bits/atomic_base.h> but that could
change).

Would it work to use a scoped enumeration type here instead? That
would prevent people accidentally doing arithmetic on it, or passing
it to functions taking an integer (and prevent it promoting to int in
arithmetic).

e.g. define it similar to std::byte:

enum class __barrier_phase_t : unsigned char { };

and then cast to an integer on the way in and the way out, so that the
implementation works with its numeric value, but users have a
non-arithmetic type that they can't do anything with.

>+
>+  template<class _CompletionF>

s/typename/class/

>+    class __barrier_base
>+    {
>+      struct alignas(64) /* naturally-align the heap state */ __state_t
>+      {
>+	struct
>+	{
>+	  __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);

No need to use ATOMIC_VAR_INIT, we're not trying to be compatible with
C, it can be just = {0} or similar.

>+	} __tickets[64];
>+      };
>+
>+      ptrdiff_t _M_expected;
>+      unique_ptr<char[]> _M_state_allocation;
>+      __state_t* 		_M_state;
>+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
>+      _CompletionF _M_completion;
>+      __atomic_base<__barrier_phase_t> _M_phase;

Can these just use atomic instead of __atomic_base?

The order of the members looks suboptimal. If alignof(_CompletionF) is
less than alignof(atomic<__barrier_phase_t>) then you'll get padding
bytes between them (or is that intentional, to avoid false sharing?)

I would put _M_completion last, and add [[no_unique_address]] so it
doesn't need to waste space if it's an empty type.

>+
>+      static __gthread_t
>+      _S_get_tid() noexcept
>+      {
>+#ifdef __GLIBC__
>+	// For the GNU C library pthread_self() is usable without linking to
>+	// libpthread.so but returns 0, so we cannot use it in single-threaded
>+	// programs, because this_thread::get_id() != thread::id{} must be true.
>+	// We know that pthread_t is an integral type in the GNU C library.
>+	if (!__gthread_active_p())
>+	  return 1;
>+#endif
>+	return __gthread_self();

This is the third place we're doing this (and all of them need to be
changed for recent versions of glibc) so we should put it in one
place.

>+      }
>+
>+      bool
>+      _M_arrive(__barrier_phase_t __old_phase)
>+      {
>+	__barrier_phase_t const __half_step = __old_phase + 1,
>+				__full_step = __old_phase + 2;
>+	size_t __current_expected = _M_expected;
>+	size_t __current = _Hash_impl::hash(_S_get_tid())
>+				% ((_M_expected + 1) >> 1);
>+	for (int __round = 0;; ++__round)
>+	  {
>+	    if (__current_expected <= 1)
>+		return true;
>+	    size_t const __end_node = ((__current_expected + 1) >> 1),
>+			 __last_node = __end_node - 1;
>+	    for (;;++__current)

A space after the semi-colons please.

>+	      {
>+		if (__current == __end_node)
>+		  __current = 0;
>+		__barrier_phase_t __expect = __old_phase;
>+		if (__current == __last_node && (__current_expected & 1))
>+		  {
>+		    if (_M_state[__current].__tickets[__round].__phase
>+			.compare_exchange_strong(__expect, __full_step,
>+						 memory_order_acq_rel))
>+		      break;     // I'm 1 in 1, go to next __round
>+		  }
>+		else if (_M_state[__current].__tickets[__round].__phase
>+			 .compare_exchange_strong(__expect, __half_step,
>+						  memory_order_acq_rel))
>+		  {
>+		    return false; // I'm 1 in 2, done with arrival
>+		  }
>+		else if (__expect == __half_step)
>+		  {
>+		    if (_M_state[__current].__tickets[__round].__phase
>+			.compare_exchange_strong(__expect, __full_step,
>+						 memory_order_acq_rel))
>+		      break;    // I'm 2 in 2, go to next __round
>+		  }
>+	      }
>+	    __current_expected = __last_node + 1;
>+	    __current >>= 1;
>+	  }
>+      }
>+
>+    public:
>+      using arrival_token = __barrier_phase_t;
>+
>+      static constexpr ptrdiff_t
>+      max() noexcept
>+      { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }

s/__numeric_traits/__int_traits/

Or just use __PTRDIFF_MAX__ which doesn't require any template
instantiation and is pre-defined by the compiler.

>+
>+      __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())

This line is too long.

As the ctor can be called with a single argument it should be
'explicit', although I don't think it needs to be callable with a
single argument. It's always going to be passed two arguments by the
derived class, so it doesn't need the default argument for
__completion (which solves the line length problem!)


>+	  : _M_expected(__expected), _M_expected_adjustment(0),
>+	    _M_completion(move(__completion)), _M_phase(0)
>+      {
>+	size_t const __count = (_M_expected + 1) >> 1;
>+	size_t const __size = sizeof(__state_t) * __count;
>+	size_t __allocation_size = __size + alignof(__state_t);
>+	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
>+	void* __allocation = _M_state_allocation.get();
>+	void* const __state = align(alignof(__state_t), __size, __allocation,
>+				      __allocation_size);
>+	_M_state = new (__state) __state_t[__count];

This looks like a workaround for not having aligned-new. I think you
can get rid of _M_state_allocation and just do:

#ifndef __cpp_aligned_new
# error NO BARRIER FOR YOU
#endif
         _M_state = std::make_unique<__state_t[]>(__count);

Probably a bit more user-friendly to add a check for __cpp_aligned_new
to the #if at the top of the file though.

Aligned new is enabled by default in C++20. If anybody wants to
compile with -fno-aligned-new then they don't get nice things.


>+      }
>+
>+      [[nodiscard]] arrival_token
>+      arrive(ptrdiff_t __update)
>+      {
>+	auto const __old_phase = _M_phase.load(memory_order_relaxed);
>+	for(; __update; --__update)
>+	  {
>+	    if(_M_arrive(__old_phase))
>+	      {
>+		_M_completion();
>+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
>+		_M_expected_adjustment.store(0, memory_order_relaxed);
>+		_M_phase.store(__old_phase + 2, memory_order_release);
>+		_M_phase.notify_all();
>+	      }
>+	  }
>+	return __old_phase;
>+      }
>+
>+      void
>+      wait(arrival_token&& __old_phase) const
>+      {
>+	auto const __test_fn = [=, this]
>+	  {
>+	    return _M_phase.load(memory_order_acquire) != __old_phase;
>+	  };
>+	_M_phase._M_wait(__old_phase, __test_fn, memory_order_acquire);
>+      }
>+
>+      void
>+      arrive_and_drop()
>+      {
>+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
>+	(void)arrive(1);
>+      }
>+    };
>+
>+  template<class _CompletionF = __empty_completion>

s/class/typename/

>+    class barrier
>+    {
>+      __barrier_base<_CompletionF> _M_b;

New line after this member declaration.

Why is it called _base if it's a member? Why is the impl in a separate
class anyway? It just seems to add a level of indirection for no real
benefit.

>+    public:
>+      using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
>+
>+      static constexpr ptrdiff_t
>+      max() noexcept
>+      { return __barrier_base<_CompletionF>::max(); }
>+
>+      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())

This is missing 'explicit'

>+	  : _M_b(__count, std::move(__completion))
>+      { }
>+
>+      barrier(barrier const&) = delete;
>+      barrier& operator=(barrier const&) = delete;
>+
>+      [[nodiscard]] arrival_token
>+      arrive(ptrdiff_t __update = 1)
>+      { return _M_b.arrive(__update); }
>+
>+      void
>+      wait(arrival_token&& __phase) const
>+      { _M_b.wait(std::move(__phase)); }
>+
>+      void
>+      arrive_and_wait()
>+      { wait(arrive()); }
>+
>+      void
>+      arrive_and_drop()
>+      { _M_b.arrive_and_drop(); }
>+    };
>+
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace
>+#endif // _GLIBCXX_HAS_GTHREADS
>+#endif // __cplusplus > 201703L
>+#endif // _GLIBCXX_BARRIER
>+
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
>new file mode 100644
>index 00000000000..0b38160a58b
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }
>+
>+#include <barrier>
>+
>+#ifndef __cpp_lib_barrier
>+# error "Feature-test macro for barrier missing in <barrier>"
>+#elif __cpp_lib_barrier != 201907L
>+# error "Feature-test macro for barrier has wrong value in <barrier>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
>new file mode 100644
>index 00000000000..1d8d83639e0
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
>@@ -0,0 +1,27 @@
>+// Copyright (C) 2019-2020 Free Software Foundation, Inc.

Just 2020 here

>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }

Once the macro is only defined inside the _GLIBCXX_HAS_GTHREADS guard
this test should { dg-require-gthreads "" }.

Now that I think about it, we should probably have negative tests that
check that the feature test macro *isn't* defined when we don't have a
gthreads target. But we aren't doing the anywhere else either.


>+
>+#include <version>
>+
>+#ifndef __cpp_lib_barrier
>+# error "Feature-test macro for barrier missing in <version>"
>+#elif __cpp_lib_barrier != 201907L
>+# error "Feature-test macro for barrier has wrong value in <version>"
>+#endif

This is going to currently fail, right? The patch doesn't add anything
to <version>.

>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>new file mode 100644
>index 00000000000..c128d5ea794
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>@@ -0,0 +1,51 @@
>+// { dg-options "-std=gnu++2a -pthread" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-effective-target pthread }
>+// { dg-require-gthreads "" }

The directives should be:

// { dg-options "-std=gnu++2a" }
// { dg-do run { target c++2a } }
// { dg-require-gthreads "" }
// { dg-additional-options "-pthread" { target pthread } }

i.e. don't require pthreads, only add -pthread for pthread targets.

This allows it to work for targets that have a non-pthreads
implementation of the gthreads abstraction (e.g. vxworks).

Similarly for the remaining tests.

>+
>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
>+//===----------------------------------------------------------------------===//
>+//
>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>+// See https://llvm.org/LICENSE.txt for license information.
>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>+//
>+//===----------------------------------------------------------------------===//
>+
>+#include <barrier>
>+#include <thread>
>+
>+#include <testsuite_hooks.h>

No need for this header if you don't use VERIFY.

>+int main(int, char**)

int main()

>+{
>+  std::barrier<> b(2);
>+
>+  auto tok = b.arrive();
>+  std::thread t([&](){
>+    (void)b.arrive();
>+  });
>+  b.wait(std::move(tok));
>+  t.join();
>+
>+  auto tok2 = b.arrive(2);
>+  b.wait(std::move(tok2));
>+  return 0;

No need for the return statement.

And similarly for the remaining tests.


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-04 18:41   ` Thomas Rodgers
@ 2020-11-04 18:52     ` Jonathan Wakely
  2020-11-04 18:55       ` Thomas Rodgers
  0 siblings, 1 reply; 23+ messages in thread
From: Jonathan Wakely @ 2020-11-04 18:52 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 04/11/20 10:41 -0800, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>IGNORE the previous version of this patch please.

OK, but all my comments seem to apply to this one too.

>Adds <barrier>
>
>libstdc++/ChangeLog:
>
>	* include/Makefile.am (std_headers): Add new header.
>	* include/Makefile.in: Regenerate.
>	* include/std/barrier: New file.
>	* testsuite/30_thread/barrier/1.cc: New test.
>	* testsuite/30_thread/barrier/2.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive.cc: Likewise.
>	* testsuite/30_thread/barrier/completion.cc: Likewise.
>	* testsuite/30_thread/barrier/max.cc: Likewise.
>---
> libstdc++-v3/include/Makefile.am              |   1 +
> libstdc++-v3/include/Makefile.in              |   1 +
> libstdc++-v3/include/bits/atomic_base.h       |  11 +-
> libstdc++-v3/include/std/barrier              | 248 ++++++++++++++++++
> libstdc++-v3/include/std/version              |   1 +
> .../testsuite/30_threads/barrier/1.cc         |  27 ++
> .../testsuite/30_threads/barrier/2.cc         |  27 ++
> .../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
> .../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
> .../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
> .../30_threads/barrier/completion.cc          |  54 ++++
> .../testsuite/30_threads/barrier/max.cc       |  44 ++++
> 12 files changed, 562 insertions(+), 3 deletions(-)
> create mode 100644 libstdc++-v3/include/std/barrier
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc
>
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index 382e94322c1..9e497835ee0 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -30,6 +30,7 @@ std_headers = \
> 	${std_srcdir}/any \
> 	${std_srcdir}/array \
> 	${std_srcdir}/atomic \
>+	${std_srcdir}/barrier \
> 	${std_srcdir}/bit \
> 	${std_srcdir}/bitset \
> 	${std_srcdir}/charconv \
>diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
>index dd4db926592..1ad34719d3e 100644
>--- a/libstdc++-v3/include/bits/atomic_base.h
>+++ b/libstdc++-v3/include/bits/atomic_base.h
>@@ -603,13 +603,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       }
>
> #if __cplusplus > 201703L
>+      template<typename _Func>
>+	_GLIBCXX_ALWAYS_INLINE void
>+	_M_wait(__int_type __old, const _Func& __fn) const noexcept
>+	{ std::__atomic_wait(&_M_i, __old, __fn); }
>+
>       _GLIBCXX_ALWAYS_INLINE void
>       wait(__int_type __old,
> 	  memory_order __m = memory_order_seq_cst) const noexcept
>       {
>-	std::__atomic_wait(&_M_i, __old,
>-			   [__m, this, __old]
>-			   { return this->load(__m) != __old; });
>+	_M_wait(__old,
>+		[__m, this, __old]
>+		{ return this->load(__m) != __old; });
>       }

This looks like it's not meant to be part of this patch.

It also looks wrong for any patch, because it adds _M_wait as a public
member.

Not sure what this piece is for :-)



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-04 18:52     ` Jonathan Wakely
@ 2020-11-04 18:55       ` Thomas Rodgers
  2020-11-12 17:27         ` Jonathan Wakely
  0 siblings, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2020-11-04 18:55 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-patches List, libstdc++, trodgers



> On Nov 4, 2020, at 10:52 AM, Jonathan Wakely <jwakely@redhat.com> wrote:
> 
> On 04/11/20 10:41 -0800, Thomas Rodgers wrote:
>> From: Thomas Rodgers <trodgers@redhat.com>
>> 
>> IGNORE the previous version of this patch please.
> 
> OK, but all my comments seem to apply to this one too.
> 

Sure :)

>> Adds <barrier>
>> 
>> libstdc++/ChangeLog:
>> 
>> 	* include/Makefile.am (std_headers): Add new header.
>> 	* include/Makefile.in: Regenerate.
>> 	* include/std/barrier: New file.
>> 	* testsuite/30_thread/barrier/1.cc: New test.
>> 	* testsuite/30_thread/barrier/2.cc: Likewise.
>> 	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>> 	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>> 	* testsuite/30_thread/barrier/arrive.cc: Likewise.
>> 	* testsuite/30_thread/barrier/completion.cc: Likewise.
>> 	* testsuite/30_thread/barrier/max.cc: Likewise.
>> ---
>> libstdc++-v3/include/Makefile.am              |   1 +
>> libstdc++-v3/include/Makefile.in              |   1 +
>> libstdc++-v3/include/bits/atomic_base.h       |  11 +-
>> libstdc++-v3/include/std/barrier              | 248 ++++++++++++++++++
>> libstdc++-v3/include/std/version              |   1 +
>> .../testsuite/30_threads/barrier/1.cc         |  27 ++
>> .../testsuite/30_threads/barrier/2.cc         |  27 ++
>> .../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
>> .../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
>> .../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
>> .../30_threads/barrier/completion.cc          |  54 ++++
>> .../testsuite/30_threads/barrier/max.cc       |  44 ++++
>> 12 files changed, 562 insertions(+), 3 deletions(-)
>> create mode 100644 libstdc++-v3/include/std/barrier
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc
>> 
>> diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>> index 382e94322c1..9e497835ee0 100644
>> --- a/libstdc++-v3/include/Makefile.am
>> +++ b/libstdc++-v3/include/Makefile.am
>> @@ -30,6 +30,7 @@ std_headers = \
>> 	${std_srcdir}/any \
>> 	${std_srcdir}/array \
>> 	${std_srcdir}/atomic \
>> +	${std_srcdir}/barrier \
>> 	${std_srcdir}/bit \
>> 	${std_srcdir}/bitset \
>> 	${std_srcdir}/charconv \
>> diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
>> index dd4db926592..1ad34719d3e 100644
>> --- a/libstdc++-v3/include/bits/atomic_base.h
>> +++ b/libstdc++-v3/include/bits/atomic_base.h
>> @@ -603,13 +603,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>      }
>> 
>> #if __cplusplus > 201703L
>> +      template<typename _Func>
>> +	_GLIBCXX_ALWAYS_INLINE void
>> +	_M_wait(__int_type __old, const _Func& __fn) const noexcept
>> +	{ std::__atomic_wait(&_M_i, __old, __fn); }
>> +
>>      _GLIBCXX_ALWAYS_INLINE void
>>      wait(__int_type __old,
>> 	  memory_order __m = memory_order_seq_cst) const noexcept
>>      {
>> -	std::__atomic_wait(&_M_i, __old,
>> -			   [__m, this, __old]
>> -			   { return this->load(__m) != __old; });
>> +	_M_wait(__old,
>> +		[__m, this, __old]
>> +		{ return this->load(__m) != __old; });
>>      }
> 
> This looks like it's not meant to be part of this patch.
> 
> It also looks wrong for any patch, because it adds _M_wait as a public
> member.
> 
> Not sure what this piece is for :-)
> 

It is used at include/std/barrier:197 to keep the implementation as close as possible to the libc++ version upon which it is based.


> 


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-04 18:50   ` Jonathan Wakely
@ 2020-11-04 19:43     ` Thomas Rodgers
  0 siblings, 0 replies; 23+ messages in thread
From: Thomas Rodgers @ 2020-11-04 19:43 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-patches List, libstdc++, trodgers



> On Nov 4, 2020, at 10:50 AM, Jonathan Wakely <jwakely@redhat.com> wrote:
> 
> On 04/11/20 09:29 -0800, Thomas Rodgers wrote:
>> From: Thomas Rodgers <trodgers@redhat.com>
>> 
>> Adds <barrier>
>> 
>> libstdc++/ChangeLog:
>> 
>> 	* include/Makefile.am (std_headers): Add new header.
>> 	* include/Makefile.in: Regenerate.
>> 	* include/std/barrier: New file.
>> 	* testsuite/30_thread/barrier/1.cc: New test.
>> 	* testsuite/30_thread/barrier/2.cc: Likewise.
>> 	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>> 	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>> 	* testsuite/30_thread/barrier/arrive.cc: Likewise.
>> 	* testsuite/30_thread/barrier/completion.cc: Likewise.
>> 	* testsuite/30_thread/barrier/max.cc: Likewise.
>> ---
>> libstdc++-v3/include/std/barrier              | 248 ++++++++++++++++++
>> .../testsuite/30_threads/barrier/1.cc         |  27 ++
>> .../testsuite/30_threads/barrier/2.cc         |  27 ++
>> .../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
>> .../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
>> .../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
>> .../30_threads/barrier/completion.cc          |  54 ++++
>> .../testsuite/30_threads/barrier/max.cc       |  44 ++++
>> 8 files changed, 551 insertions(+)
>> create mode 100644 libstdc++-v3/include/std/barrier
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
>> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc
>> 
>> diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
>> new file mode 100644
>> index 00000000000..80e6d668cf5
>> --- /dev/null
>> +++ b/libstdc++-v3/include/std/barrier
>> @@ -0,0 +1,248 @@
>> +// <barrier> -*- C++ -*-
>> +
>> +// 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
>> +// <http://www.gnu.org/licenses/>.
>> +
>> +// This implementation is based on libcxx/include/barrier
>> +//===-- barrier.h --------------------------------------------------===//
>> +//
>> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>> +//
>> +//===---------------------------------------------------------------===//
>> +
>> +#ifndef _GLIBCXX_BARRIER
>> +#define _GLIBCXX_BARRIER 1
>> +
>> +#pragma GCC system_header
>> +
>> +#if __cplusplus > 201703L
>> +#define __cpp_lib_barrier 201907L
> 
> This feature test macro will be defined unconditionally, even if
> _GLIBCXX_HAS_GTHREADS is not defined. It should be inside the check
> for gthreads.
> 
> You're also missing an edit to <version> (which should depend on the
> same conditions).
> 
> 
>> +#include <bits/c++config.h>
>> +
>> +#if defined(_GLIBCXX_HAS_GTHREADS)
>> +#include <bits/atomic_base.h>
>> +#include <bits/atomic_wait.h>
>> +#include <bits/functional_hash.h>
>> +#include <ext/numeric_traits.h>
>> +
>> +#include <memory>
>> +
>> +namespace std _GLIBCXX_VISIBILITY(default)
>> +{
>> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
>> +
>> +  struct __empty_completion
>> +  {
>> +    _GLIBCXX_ALWAYS_INLINE void
>> +    operator()() noexcept
>> +    { }
>> +  };
>> +
>> +/*
>> +
>> +The default implementation of __barrier_base is a classic tree barrier.
>> +
>> +It looks different from literature pseudocode for two main reasons:
>> + 1. Threads that call into std::barrier functions do not provide indices,
>> +    so a numbering step is added before the actual barrier algorithm,
>> +    appearing as an N+1 round to the N rounds of the tree barrier.
>> + 2. A great deal of attention has been paid to avoid cache line thrashing
>> +    by flattening the tree structure into cache-line sized arrays, that
>> +    are indexed in an efficient way.
>> +
>> +*/
>> +
>> +  using __barrier_phase_t = uint8_t;
> 
> Please add <stdint.h> or <cstdint> since you're using uint8_t
> (it's currently included by <bits/atomic_base.h> but that could
> change).
> 
> Would it work to use a scoped enumeration type here instead? That
> would prevent people accidentally doing arithmetic on it, or passing
> it to functions taking an integer (and prevent it promoting to int in
> arithmetic).
> 
> e.g. define it similar to std::byte:
> 
> enum class __barrier_phase_t : unsigned char { };
> 
> and then cast to an integer on the way in and the way out, so that the
> implementation works with its numeric value, but users have a
> non-arithmetic type that they can't do anything with.
> 
>> +
>> +  template<class _CompletionF>
> 
> s/typename/class/
> 
>> +    class __barrier_base
>> +    {
>> +      struct alignas(64) /* naturally-align the heap state */ __state_t
>> +      {
>> +	struct
>> +	{
>> +	  __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
> 
> No need to use ATOMIC_VAR_INIT, we're not trying to be compatible with
> C, it can be just = {0} or similar.
> 
>> +	} __tickets[64];
>> +      };
>> +
>> +      ptrdiff_t _M_expected;
>> +      unique_ptr<char[]> _M_state_allocation;
>> +      __state_t* 		_M_state;
>> +      __atomic_base<ptrdiff_t> _M_expected_adjustment;
>> +      _CompletionF _M_completion;
>> +      __atomic_base<__barrier_phase_t> _M_phase;
> 
> Can these just use atomic instead of __atomic_base?
> 
> The order of the members looks suboptimal. If alignof(_CompletionF) is
> less than alignof(atomic<__barrier_phase_t>) then you'll get padding
> bytes between them (or is that intentional, to avoid false sharing?)
> 
> I would put _M_completion last, and add [[no_unique_address]] so it
> doesn't need to waste space if it's an empty type.
> 
>> +
>> +      static __gthread_t
>> +      _S_get_tid() noexcept
>> +      {
>> +#ifdef __GLIBC__
>> +	// For the GNU C library pthread_self() is usable without linking to
>> +	// libpthread.so but returns 0, so we cannot use it in single-threaded
>> +	// programs, because this_thread::get_id() != thread::id{} must be true.
>> +	// We know that pthread_t is an integral type in the GNU C library.
>> +	if (!__gthread_active_p())
>> +	  return 1;
>> +#endif
>> +	return __gthread_self();
> 
> This is the third place we're doing this (and all of them need to be
> changed for recent versions of glibc) so we should put it in one
> place.
> 
>> +      }
>> +
>> +      bool
>> +      _M_arrive(__barrier_phase_t __old_phase)
>> +      {
>> +	__barrier_phase_t const __half_step = __old_phase + 1,
>> +				__full_step = __old_phase + 2;
>> +	size_t __current_expected = _M_expected;
>> +	size_t __current = _Hash_impl::hash(_S_get_tid())
>> +				% ((_M_expected + 1) >> 1);
>> +	for (int __round = 0;; ++__round)
>> +	  {
>> +	    if (__current_expected <= 1)
>> +		return true;
>> +	    size_t const __end_node = ((__current_expected + 1) >> 1),
>> +			 __last_node = __end_node - 1;
>> +	    for (;;++__current)
> 
> A space after the semi-colons please.
> 
>> +	      {
>> +		if (__current == __end_node)
>> +		  __current = 0;
>> +		__barrier_phase_t __expect = __old_phase;
>> +		if (__current == __last_node && (__current_expected & 1))
>> +		  {
>> +		    if (_M_state[__current].__tickets[__round].__phase
>> +			.compare_exchange_strong(__expect, __full_step,
>> +						 memory_order_acq_rel))
>> +		      break;     // I'm 1 in 1, go to next __round
>> +		  }
>> +		else if (_M_state[__current].__tickets[__round].__phase
>> +			 .compare_exchange_strong(__expect, __half_step,
>> +						  memory_order_acq_rel))
>> +		  {
>> +		    return false; // I'm 1 in 2, done with arrival
>> +		  }
>> +		else if (__expect == __half_step)
>> +		  {
>> +		    if (_M_state[__current].__tickets[__round].__phase
>> +			.compare_exchange_strong(__expect, __full_step,
>> +						 memory_order_acq_rel))
>> +		      break;    // I'm 2 in 2, go to next __round
>> +		  }
>> +	      }
>> +	    __current_expected = __last_node + 1;
>> +	    __current >>= 1;
>> +	  }
>> +      }
>> +
>> +    public:
>> +      using arrival_token = __barrier_phase_t;
>> +
>> +      static constexpr ptrdiff_t
>> +      max() noexcept
>> +      { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
> 
> s/__numeric_traits/__int_traits/
> 
> Or just use __PTRDIFF_MAX__ which doesn't require any template
> instantiation and is pre-defined by the compiler.
> 
>> +
>> +      __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
> 
> This line is too long.
> 
> As the ctor can be called with a single argument it should be
> 'explicit', although I don't think it needs to be callable with a
> single argument. It's always going to be passed two arguments by the
> derived class, so it doesn't need the default argument for
> __completion (which solves the line length problem!)
> 
> 
>> +	  : _M_expected(__expected), _M_expected_adjustment(0),
>> +	    _M_completion(move(__completion)), _M_phase(0)
>> +      {
>> +	size_t const __count = (_M_expected + 1) >> 1;
>> +	size_t const __size = sizeof(__state_t) * __count;
>> +	size_t __allocation_size = __size + alignof(__state_t);
>> +	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
>> +	void* __allocation = _M_state_allocation.get();
>> +	void* const __state = align(alignof(__state_t), __size, __allocation,
>> +				      __allocation_size);
>> +	_M_state = new (__state) __state_t[__count];
> 
> This looks like a workaround for not having aligned-new. I think you
> can get rid of _M_state_allocation and just do:
> 
> #ifndef __cpp_aligned_new
> # error NO BARRIER FOR YOU
> #endif
>        _M_state = std::make_unique<__state_t[]>(__count);
> 
> Probably a bit more user-friendly to add a check for __cpp_aligned_new
> to the #if at the top of the file though.
> 
> Aligned new is enabled by default in C++20. If anybody wants to
> compile with -fno-aligned-new then they don't get nice things.
> 
> 
>> +      }
>> +
>> +      [[nodiscard]] arrival_token
>> +      arrive(ptrdiff_t __update)
>> +      {
>> +	auto const __old_phase = _M_phase.load(memory_order_relaxed);
>> +	for(; __update; --__update)
>> +	  {
>> +	    if(_M_arrive(__old_phase))
>> +	      {
>> +		_M_completion();
>> +		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
>> +		_M_expected_adjustment.store(0, memory_order_relaxed);
>> +		_M_phase.store(__old_phase + 2, memory_order_release);
>> +		_M_phase.notify_all();
>> +	      }
>> +	  }
>> +	return __old_phase;
>> +      }
>> +
>> +      void
>> +      wait(arrival_token&& __old_phase) const
>> +      {
>> +	auto const __test_fn = [=, this]
>> +	  {
>> +	    return _M_phase.load(memory_order_acquire) != __old_phase;
>> +	  };
>> +	_M_phase._M_wait(__old_phase, __test_fn, memory_order_acquire);
>> +      }
>> +
>> +      void
>> +      arrive_and_drop()
>> +      {
>> +	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
>> +	(void)arrive(1);
>> +      }
>> +    };
>> +
>> +  template<class _CompletionF = __empty_completion>
> 
> s/class/typename/
> 
>> +    class barrier
>> +    {
>> +      __barrier_base<_CompletionF> _M_b;
> 
> New line after this member declaration.
> 
> Why is it called _base if it's a member? Why is the impl in a separate
> class anyway? It just seems to add a level of indirection for no real
> benefit.
> 

In the original implementation there are two discrete barrier algorithm implementations, the tree barrier algorithm here, and a “central barrier” algorithm (which also has two variants based on whether or not the completion function is empty), and what is __barrier_base here is split into two parts __barrier_algorithm_base and __barrier_base, the former existing as the ABI stable interface to the underlying algorithm and the latter being the shared bits which depend on the type of _CompletionF. I left most of the structure intact but took only the tree barrier algorithm, which will scale better for larger core/socket counts, but it’s conceivable we may want to eventually add the central barrier algorithm back which is a better choice for small core-count targets.  

>> +    public:
>> +      using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
>> +
>> +      static constexpr ptrdiff_t
>> +      max() noexcept
>> +      { return __barrier_base<_CompletionF>::max(); }
>> +
>> +      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
> 
> This is missing 'explicit'
> 
>> +	  : _M_b(__count, std::move(__completion))
>> +      { }
>> +
>> +      barrier(barrier const&) = delete;
>> +      barrier& operator=(barrier const&) = delete;
>> +
>> +      [[nodiscard]] arrival_token
>> +      arrive(ptrdiff_t __update = 1)
>> +      { return _M_b.arrive(__update); }
>> +
>> +      void
>> +      wait(arrival_token&& __phase) const
>> +      { _M_b.wait(std::move(__phase)); }
>> +
>> +      void
>> +      arrive_and_wait()
>> +      { wait(arrive()); }
>> +
>> +      void
>> +      arrive_and_drop()
>> +      { _M_b.arrive_and_drop(); }
>> +    };
>> +
>> +_GLIBCXX_END_NAMESPACE_VERSION
>> +} // namespace
>> +#endif // _GLIBCXX_HAS_GTHREADS
>> +#endif // __cplusplus > 201703L
>> +#endif // _GLIBCXX_BARRIER
>> +
>> diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
>> new file mode 100644
>> index 00000000000..0b38160a58b
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/30_threads/barrier/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
>> +// <http://www.gnu.org/licenses/>.
>> +
>> +// { dg-options "-std=gnu++2a" }
>> +// { dg-do compile { target c++2a } }
>> +
>> +#include <barrier>
>> +
>> +#ifndef __cpp_lib_barrier
>> +# error "Feature-test macro for barrier missing in <barrier>"
>> +#elif __cpp_lib_barrier != 201907L
>> +# error "Feature-test macro for barrier has wrong value in <barrier>"
>> +#endif
>> diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
>> new file mode 100644
>> index 00000000000..1d8d83639e0
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
>> @@ -0,0 +1,27 @@
>> +// Copyright (C) 2019-2020 Free Software Foundation, Inc.
> 
> Just 2020 here
> 
>> +// 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
>> +// <http://www.gnu.org/licenses/>.
>> +
>> +// { dg-options "-std=gnu++2a" }
>> +// { dg-do compile { target c++2a } }
> 
> Once the macro is only defined inside the _GLIBCXX_HAS_GTHREADS guard
> this test should { dg-require-gthreads "" }.
> 
> Now that I think about it, we should probably have negative tests that
> check that the feature test macro *isn't* defined when we don't have a
> gthreads target. But we aren't doing the anywhere else either.
> 
> 
>> +
>> +#include <version>
>> +
>> +#ifndef __cpp_lib_barrier
>> +# error "Feature-test macro for barrier missing in <version>"
>> +#elif __cpp_lib_barrier != 201907L
>> +# error "Feature-test macro for barrier has wrong value in <version>"
>> +#endif
> 
> This is going to currently fail, right? The patch doesn't add anything
> to <version>.
> 
>> diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>> new file mode 100644
>> index 00000000000..c128d5ea794
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>> @@ -0,0 +1,51 @@
>> +// { dg-options "-std=gnu++2a -pthread" }
>> +// { dg-do run { target c++2a } }
>> +// { dg-require-effective-target pthread }
>> +// { dg-require-gthreads "" }
> 
> The directives should be:
> 
> // { dg-options "-std=gnu++2a" }
> // { dg-do run { target c++2a } }
> // { dg-require-gthreads "" }
> // { dg-additional-options "-pthread" { target pthread } }
> 
> i.e. don't require pthreads, only add -pthread for pthread targets.
> 
> This allows it to work for targets that have a non-pthreads
> implementation of the gthreads abstraction (e.g. vxworks).
> 
> Similarly for the remaining tests.
> 
>> +
>> +// 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
>> +// <http://www.gnu.org/licenses/>.
>> +
>> +// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
>> +//===----------------------------------------------------------------------===//
>> +//
>> +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>> +// See https://llvm.org/LICENSE.txt for license information.
>> +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>> +//
>> +//===----------------------------------------------------------------------===//
>> +
>> +#include <barrier>
>> +#include <thread>
>> +
>> +#include <testsuite_hooks.h>
> 
> No need for this header if you don't use VERIFY.
> 
>> +int main(int, char**)
> 
> int main()
> 
>> +{
>> +  std::barrier<> b(2);
>> +
>> +  auto tok = b.arrive();
>> +  std::thread t([&](){
>> +    (void)b.arrive();
>> +  });
>> +  b.wait(std::move(tok));
>> +  t.join();
>> +
>> +  auto tok2 = b.arrive(2);
>> +  b.wait(std::move(tok2));
>> +  return 0;
> 
> No need for the return statement.
> 
> And similarly for the remaining tests.


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-04 18:55       ` Thomas Rodgers
@ 2020-11-12 17:27         ` Jonathan Wakely
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Wakely @ 2020-11-12 17:27 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: trodgers, libstdc++, gcc-patches List

On 04/11/20 10:55 -0800, Thomas Rodgers wrote:
>>> --- a/libstdc++-v3/include/bits/atomic_base.h
>>> +++ b/libstdc++-v3/include/bits/atomic_base.h
>>> @@ -603,13 +603,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>      }
>>>
>>> #if __cplusplus > 201703L
>>> +      template<typename _Func>
>>> +	_GLIBCXX_ALWAYS_INLINE void
>>> +	_M_wait(__int_type __old, const _Func& __fn) const noexcept
>>> +	{ std::__atomic_wait(&_M_i, __old, __fn); }
>>> +
>>>      _GLIBCXX_ALWAYS_INLINE void
>>>      wait(__int_type __old,
>>> 	  memory_order __m = memory_order_seq_cst) const noexcept
>>>      {
>>> -	std::__atomic_wait(&_M_i, __old,
>>> -			   [__m, this, __old]
>>> -			   { return this->load(__m) != __old; });
>>> +	_M_wait(__old,
>>> +		[__m, this, __old]
>>> +		{ return this->load(__m) != __old; });
>>>      }
>>
>> This looks like it's not meant to be part of this patch.
>>
>> It also looks wrong for any patch, because it adds _M_wait as a public
>> member.
>>
>> Not sure what this piece is for :-)
>>
>
>It is used at include/std/barrier:197 to keep the implementation as close as possible to the libc++ version upon which it is based.

So the caller in <barrier> can't use __atomic_wait directly because it
can't access the _M_i member of the atomic.

Would it be possible to use atomic_ref instead of atomic, so that the
barrier code has access to the underlying object and can use it
directly with __atomic_wait?



^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-04 17:29 ` [PATCH] libstdc++: " Thomas Rodgers
  2020-11-04 18:41   ` Thomas Rodgers
  2020-11-04 18:50   ` Jonathan Wakely
@ 2020-11-21  0:30   ` Thomas Rodgers
  2020-11-27 17:46     ` Jonathan Wakely
  2 siblings, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2020-11-21  0:30 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

Should include all discussion on and off list to date.

Adds <barrier>

libstdc++/ChangeLog:

	* include/Makefile.am (std_headers): Add new header.
	* include/Makefile.in: Regenerate.
	* include/std/barrier: New file.
	* include/std/version: Add __cpp_lib_barrier feature test macro.
	* testsuite/30_thread/barrier/1.cc: New test.
	* testsuite/30_thread/barrier/2.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
	* testsuite/30_thread/barrier/arrive.cc: Likewise.
	* testsuite/30_thread/barrier/completion.cc: Likewise.
	* testsuite/30_thread/barrier/max.cc: Likewise.
---
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 libstdc++-v3/include/std/barrier              | 258 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   3 +
 .../testsuite/30_threads/barrier/1.cc         |  27 ++
 .../testsuite/30_threads/barrier/2.cc         |  27 ++
 .../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
 .../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
 .../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
 .../30_threads/barrier/completion.cc          |  54 ++++
 .../testsuite/30_threads/barrier/max.cc       |  44 +++
 11 files changed, 566 insertions(+)
 create mode 100644 libstdc++-v3/include/std/barrier
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index ca413b8fdfe..a20dd461fd1 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -30,6 +30,7 @@ std_headers = \
 	${std_srcdir}/any \
 	${std_srcdir}/array \
 	${std_srcdir}/atomic \
+	${std_srcdir}/barrier \
 	${std_srcdir}/bit \
 	${std_srcdir}/bitset \
 	${std_srcdir}/charconv \
diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
new file mode 100644
index 00000000000..a6cc6a058dd
--- /dev/null
+++ b/libstdc++-v3/include/std/barrier
@@ -0,0 +1,258 @@
+// <barrier> -*- C++ -*-
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/include/barrier
+//===-- barrier.h --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+#ifndef _GLIBCXX_BARRIER
+#define _GLIBCXX_BARRIER 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#include <bits/c++config.h>
+
+#if defined(_GLIBCXX_HAS_GTHREADS)
+#define __cpp_lib_barrier 201907L
+#include <bits/atomic_base.h>
+#include <bits/atomic_wait.h>
+#include <bits/functional_hash.h>
+
+#include <memory>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  struct __empty_completion
+  {
+    _GLIBCXX_ALWAYS_INLINE void
+    operator()() noexcept
+    { }
+  };
+
+/*
+
+The default implementation of __tree_barrier is a classic tree barrier.
+
+It looks different from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+  using __barrier_phase_t = uint8_t;
+
+  template<typename _CompletionF>
+    class __tree_barrier
+    {
+      struct alignas(64) /* naturally-align the heap state */ __state_t
+      {
+	struct
+	{
+	  __atomic_base<__barrier_phase_t> __phase = { 0 };
+	} __tickets[64];
+      };
+
+      ptrdiff_t _M_expected;
+      unique_ptr<char[]> _M_state_allocation;
+      __state_t* 		_M_state;
+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
+      _CompletionF _M_completion;
+
+      using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>;
+      using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>;
+      static constexpr size_t __phase_alignment =
+		      __atomic_phase_ref_t::required_alignment;
+      alignas(__phase_alignment) __barrier_phase_t  _M_phase;
+
+      static __gthread_t
+      _S_get_tid() noexcept
+      {
+#ifdef __GLIBC__
+	// For the GNU C library pthread_self() is usable without linking to
+	// libpthread.so but returns 0, so we cannot use it in single-threaded
+	// programs, because this_thread::get_id() != thread::id{} must be true.
+	// We know that pthread_t is an integral type in the GNU C library.
+	if (!__gthread_active_p())
+	  return 1;
+#endif
+	return __gthread_self();
+      }
+
+      bool
+      _M_arrive(__barrier_phase_t __old_phase)
+      {
+	__barrier_phase_t const __half_step = __old_phase + 1,
+				__full_step = __old_phase + 2;
+	size_t __current_expected = _M_expected;
+	size_t __current = _Hash_impl::hash(_S_get_tid())
+				% ((_M_expected + 1) >> 1);
+	for (int __round = 0; ; ++__round)
+	  {
+	    if (__current_expected <= 1)
+		return true;
+	    size_t const __end_node = ((__current_expected + 1) >> 1),
+			 __last_node = __end_node - 1;
+	    for (; ; ++__current)
+	      {
+		if (__current == __end_node)
+		  __current = 0;
+		__barrier_phase_t __expect = __old_phase;
+		if (__current == __last_node && (__current_expected & 1))
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;     // I'm 1 in 1, go to next __round
+		  }
+		else if (_M_state[__current].__tickets[__round].__phase
+			 .compare_exchange_strong(__expect, __half_step,
+						  memory_order_acq_rel))
+		  {
+		    return false; // I'm 1 in 2, done with arrival
+		  }
+		else if (__expect == __half_step)
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;    // I'm 2 in 2, go to next __round
+		  }
+	      }
+	    __current_expected = __last_node + 1;
+	    __current >>= 1;
+	  }
+      }
+
+    public:
+      using arrival_token = __barrier_phase_t;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __PTRDIFF_MAX__; }
+
+      __tree_barrier(ptrdiff_t __expected,
+		     _CompletionF __completion = _CompletionF())
+	  : _M_expected(__expected), _M_expected_adjustment(0),
+	    _M_completion(move(__completion)), _M_phase(0)
+      {
+	size_t const __count = (_M_expected + 1) >> 1;
+	size_t const __size = sizeof(__state_t) * __count;
+	size_t __allocation_size = __size + alignof(__state_t);
+	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
+	void* __allocation = _M_state_allocation.get();
+	void* const __state = align(alignof(__state_t), __size, __allocation,
+				      __allocation_size);
+	_M_state = new (__state) __state_t[__count];
+      }
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update)
+      {
+	__atomic_phase_ref_t __phase(_M_phase);
+	auto const __old_phase = __phase.load(memory_order_relaxed);
+	for(; __update; --__update)
+	  {
+	    if(_M_arrive(__old_phase))
+	      {
+		_M_completion();
+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
+		_M_expected_adjustment.store(0, memory_order_relaxed);
+		__phase.store(__old_phase + 2, memory_order_release);
+		__phase.notify_all();
+	      }
+	  }
+	return __old_phase;
+      }
+
+      void
+      wait(arrival_token&& __old_phase) const
+      {
+	__atomic_phase_const_ref_t __phase(_M_phase);
+	auto const __test_fn = [=, this]
+	  {
+	    return __phase.load(memory_order_acquire) != __old_phase;
+	  };
+	std::__atomic_wait(&_M_phase, __old_phase, __test_fn);
+      }
+
+      void
+      arrive_and_drop()
+      {
+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
+	(void)arrive(1);
+      }
+    };
+
+  template<typename _CompletionF = __empty_completion>
+    class barrier
+    {
+      // Note, we may introduce a "central" barrier algorithm at some point
+      // for more space constrained targets
+      using __algorithm_t = __tree_barrier<_CompletionF>;
+      __algorithm_t _M_b;
+
+    public:
+      using arrival_token = typename __tree_barrier<_CompletionF>::arrival_token;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __algorithm_t::max(); }
+
+      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
+	  : _M_b(__count, std::move(__completion))
+      { }
+
+      barrier(barrier const&) = delete;
+      barrier& operator=(barrier const&) = delete;
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update = 1)
+      { return _M_b.arrive(__update); }
+
+      void
+      wait(arrival_token&& __phase) const
+      { _M_b.wait(std::move(__phase)); }
+
+      void
+      arrive_and_wait()
+      { wait(arrive()); }
+
+      void
+      arrive_and_drop()
+      { _M_b.arrive_and_drop(); }
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // _GLIBCXX_HAS_GTHREADS
+#endif // __cplusplus > 201703L
+#endif // _GLIBCXX_BARRIER
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 42ed7cb74d3..b7458a46909 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -197,6 +197,9 @@
 #if _GLIBCXX_HOSTED
 #define __cpp_lib_array_constexpr 201811L
 #define __cpp_lib_assume_aligned 201811L
+#ifdef _GLIBCXX_HAS_GTHREADS
+#define __cpp_lib_barrier 201907L
+#endif
 #define __cpp_lib_bind_front 201907L
 // FIXME: #define __cpp_lib_execution 201902L
 #define __cpp_lib_integer_comparison_functions 202002L
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
new file mode 100644
index 00000000000..0b38160a58b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <barrier>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <barrier>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <barrier>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
new file mode 100644
index 00000000000..1d8d83639e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <version>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
new file mode 100644
index 00000000000..c128d5ea794
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -0,0 +1,51 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
new file mode 100644
index 00000000000..b7f9404bc5d
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -0,0 +1,49 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
new file mode 100644
index 00000000000..b2940ae7c34
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -0,0 +1,51 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
new file mode 100644
index 00000000000..8f37eecd8ed
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -0,0 +1,54 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&] { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+	b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  VERIFY( x == 10 );
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
new file mode 100644
index 00000000000..720037bb2c7
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
@@ -0,0 +1,44 @@
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+  return 0;
+}
-- 
2.26.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-21  0:30   ` Thomas Rodgers
@ 2020-11-27 17:46     ` Jonathan Wakely
  2020-12-17 14:06       ` Jonathan Wakely
  2020-12-17 19:11       ` Thomas Rodgers
  0 siblings, 2 replies; 23+ messages in thread
From: Jonathan Wakely @ 2020-11-27 17:46 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 20/11/20 16:30 -0800, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>Should include all discussion on and off list to date.

Most of the comments in
https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558090.html
still apply to this version.

>Adds <barrier>
>
>libstdc++/ChangeLog:
>
>	* include/Makefile.am (std_headers): Add new header.
>	* include/Makefile.in: Regenerate.
>	* include/std/barrier: New file.
>	* include/std/version: Add __cpp_lib_barrier feature test macro.
>	* testsuite/30_thread/barrier/1.cc: New test.
>	* testsuite/30_thread/barrier/2.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive.cc: Likewise.
>	* testsuite/30_thread/barrier/completion.cc: Likewise.
>	* testsuite/30_thread/barrier/max.cc: Likewise.
>---
> libstdc++-v3/include/Makefile.am              |   1 +
> libstdc++-v3/include/Makefile.in              |   1 +
> libstdc++-v3/include/std/barrier              | 258 ++++++++++++++++++
> libstdc++-v3/include/std/version              |   3 +
> .../testsuite/30_threads/barrier/1.cc         |  27 ++
> .../testsuite/30_threads/barrier/2.cc         |  27 ++
> .../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
> .../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
> .../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
> .../30_threads/barrier/completion.cc          |  54 ++++
> .../testsuite/30_threads/barrier/max.cc       |  44 +++
> 11 files changed, 566 insertions(+)
> create mode 100644 libstdc++-v3/include/std/barrier
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
> create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc
>
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index ca413b8fdfe..a20dd461fd1 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -30,6 +30,7 @@ std_headers = \
> 	${std_srcdir}/any \
> 	${std_srcdir}/array \
> 	${std_srcdir}/atomic \
>+	${std_srcdir}/barrier \
> 	${std_srcdir}/bit \
> 	${std_srcdir}/bitset \
> 	${std_srcdir}/charconv \

The new header should also be added to include/precompiled/stdc++.h
and doc/doxygen/user.cfg.in

>diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
>new file mode 100644
>index 00000000000..a6cc6a058dd
>--- /dev/null
>+++ b/libstdc++-v3/include/std/barrier
>@@ -0,0 +1,258 @@
>+// <barrier> -*- C++ -*-
>+
>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// This implementation is based on libcxx/include/barrier
>+//===-- barrier.h --------------------------------------------------===//
>+//
>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>+// See https://llvm.org/LICENSE.txt for license information.
>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>+//
>+//===---------------------------------------------------------------===//

This file doesn't have the usual @file doxygen block.

>+
>+#ifndef _GLIBCXX_BARRIER
>+#define _GLIBCXX_BARRIER 1
>+
>+#pragma GCC system_header
>+
>+#if __cplusplus > 201703L
>+#include <bits/c++config.h>
>+
>+#if defined(_GLIBCXX_HAS_GTHREADS)

This doesn't match the condition used in <bits/atomic_wait.h>. I've
added _GLIBCXX_HAVE_ATOMIC_WAIT so it could check that.

>+#define __cpp_lib_barrier 201907L
>+#include <bits/atomic_base.h>
>+#include <bits/atomic_wait.h>

This is already included by <bits/atomic_base.h>

I suggest:

#if __cplusplus > 201703L
#include <bits/atomic_base.h>
#ifdef _GLIBCXX_HAVE_ATOMIC_WAIT
#include <bits/functional_hash.h>
...
#define __cpp_lib_barrier 201907L

We should only define the __cpp_lib macro after including all other
headers, otherwise we advertise to those headers that the feature
exists, but it hasn't been defined yet. Nothing is trying to use
barrier in the rest of the library yet, but that could change.

>+#include <bits/functional_hash.h>
>+
>+#include <memory>
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+  struct __empty_completion
>+  {
>+    _GLIBCXX_ALWAYS_INLINE void
>+    operator()() noexcept
>+    { }
>+  };
>+
>+/*
>+
>+The default implementation of __tree_barrier is a classic tree barrier.
>+
>+It looks different from literature pseudocode for two main reasons:
>+ 1. Threads that call into std::barrier functions do not provide indices,
>+    so a numbering step is added before the actual barrier algorithm,
>+    appearing as an N+1 round to the N rounds of the tree barrier.
>+ 2. A great deal of attention has been paid to avoid cache line thrashing
>+    by flattening the tree structure into cache-line sized arrays, that
>+    are indexed in an efficient way.
>+
>+*/
>+
>+  using __barrier_phase_t = uint8_t;
>+
>+  template<typename _CompletionF>
>+    class __tree_barrier
>+    {
>+      struct alignas(64) /* naturally-align the heap state */ __state_t
>+      {
>+	struct
>+	{
>+	  __atomic_base<__barrier_phase_t> __phase = { 0 };
>+	} __tickets[64];
>+      };
>+
>+      ptrdiff_t _M_expected;
>+      unique_ptr<char[]> _M_state_allocation;
>+      __state_t* 		_M_state;
>+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
>+      _CompletionF _M_completion;
>+
>+      using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>;
>+      using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>;
>+      static constexpr size_t __phase_alignment =
>+		      __atomic_phase_ref_t::required_alignment;
>+      alignas(__phase_alignment) __barrier_phase_t  _M_phase;
>+
>+      static __gthread_t
>+      _S_get_tid() noexcept
>+      {
>+#ifdef __GLIBC__
>+	// For the GNU C library pthread_self() is usable without linking to
>+	// libpthread.so but returns 0, so we cannot use it in single-threaded
>+	// programs, because this_thread::get_id() != thread::id{} must be true.
>+	// We know that pthread_t is an integral type in the GNU C library.
>+	if (!__gthread_active_p())
>+	  return 1;
>+#endif
>+	return __gthread_self();

If you include <bits/std_thread.h> you can use this_thread::get_id()
here. That avoids needing to repeat the glibc-specific hack.

>+      }
>+
>+      bool
>+      _M_arrive(__barrier_phase_t __old_phase)
>+      {
>+	__barrier_phase_t const __half_step = __old_phase + 1,
>+				__full_step = __old_phase + 2;
>+	size_t __current_expected = _M_expected;
>+	size_t __current = _Hash_impl::hash(_S_get_tid())
>+				% ((_M_expected + 1) >> 1);

If we move std::hash<std::thread::id> into <bits/std_thread.h> then
you can just use that.

>+	for (int __round = 0; ; ++__round)
>+	  {
>+	    if (__current_expected <= 1)
>+		return true;
>+	    size_t const __end_node = ((__current_expected + 1) >> 1),
>+			 __last_node = __end_node - 1;
>+	    for (; ; ++__current)
>+	      {
>+		if (__current == __end_node)
>+		  __current = 0;
>+		__barrier_phase_t __expect = __old_phase;
>+		if (__current == __last_node && (__current_expected & 1))
>+		  {
>+		    if (_M_state[__current].__tickets[__round].__phase
>+			.compare_exchange_strong(__expect, __full_step,
>+						 memory_order_acq_rel))
>+		      break;     // I'm 1 in 1, go to next __round
>+		  }
>+		else if (_M_state[__current].__tickets[__round].__phase
>+			 .compare_exchange_strong(__expect, __half_step,
>+						  memory_order_acq_rel))
>+		  {
>+		    return false; // I'm 1 in 2, done with arrival
>+		  }
>+		else if (__expect == __half_step)
>+		  {
>+		    if (_M_state[__current].__tickets[__round].__phase
>+			.compare_exchange_strong(__expect, __full_step,
>+						 memory_order_acq_rel))
>+		      break;    // I'm 2 in 2, go to next __round
>+		  }
>+	      }
>+	    __current_expected = __last_node + 1;
>+	    __current >>= 1;
>+	  }
>+      }
>+
>+    public:
>+      using arrival_token = __barrier_phase_t;
>+
>+      static constexpr ptrdiff_t
>+      max() noexcept
>+      { return __PTRDIFF_MAX__; }
>+
>+      __tree_barrier(ptrdiff_t __expected,
>+		     _CompletionF __completion = _CompletionF())
>+	  : _M_expected(__expected), _M_expected_adjustment(0),
>+	    _M_completion(move(__completion)), _M_phase(0)
>+      {
>+	size_t const __count = (_M_expected + 1) >> 1;
>+	size_t const __size = sizeof(__state_t) * __count;
>+	size_t __allocation_size = __size + alignof(__state_t);
>+	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
>+	void* __allocation = _M_state_allocation.get();
>+	void* const __state = align(alignof(__state_t), __size, __allocation,
>+				      __allocation_size);
>+	_M_state = new (__state) __state_t[__count];

I still want this simplified. The upstream code is gross, so I don't
really want to keep it synced with upstream.

>+      }
>+
>+      [[nodiscard]] arrival_token
>+      arrive(ptrdiff_t __update)
>+      {
>+	__atomic_phase_ref_t __phase(_M_phase);
>+	auto const __old_phase = __phase.load(memory_order_relaxed);
>+	for(; __update; --__update)
>+	  {
>+	    if(_M_arrive(__old_phase))
>+	      {
>+		_M_completion();
>+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
>+		_M_expected_adjustment.store(0, memory_order_relaxed);
>+		__phase.store(__old_phase + 2, memory_order_release);
>+		__phase.notify_all();
>+	      }
>+	  }
>+	return __old_phase;
>+      }
>+
>+      void
>+      wait(arrival_token&& __old_phase) const
>+      {
>+	__atomic_phase_const_ref_t __phase(_M_phase);
>+	auto const __test_fn = [=, this]
>+	  {
>+	    return __phase.load(memory_order_acquire) != __old_phase;
>+	  };
>+	std::__atomic_wait(&_M_phase, __old_phase, __test_fn);
>+      }
>+
>+      void
>+      arrive_and_drop()
>+      {
>+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
>+	(void)arrive(1);
>+      }
>+    };
>+
>+  template<typename _CompletionF = __empty_completion>
>+    class barrier
>+    {
>+      // Note, we may introduce a "central" barrier algorithm at some point
>+      // for more space constrained targets
>+      using __algorithm_t = __tree_barrier<_CompletionF>;
>+      __algorithm_t _M_b;
>+
>+    public:
>+      using arrival_token = typename __tree_barrier<_CompletionF>::arrival_token;
>+
>+      static constexpr ptrdiff_t
>+      max() noexcept
>+      { return __algorithm_t::max(); }
>+
>+      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())

Still missing 'explicit'

See https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558090.html



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-27 17:46     ` Jonathan Wakely
@ 2020-12-17 14:06       ` Jonathan Wakely
  2020-12-17 19:11       ` Thomas Rodgers
  1 sibling, 0 replies; 23+ messages in thread
From: Jonathan Wakely @ 2020-12-17 14:06 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

[-- Attachment #1: Type: text/plain, Size: 8071 bytes --]

On 27/11/20 17:46 +0000, Jonathan Wakely wrote:
>On 20/11/20 16:30 -0800, Thomas Rodgers wrote:
>>From: Thomas Rodgers <trodgers@redhat.com>
>>
>>Should include all discussion on and off list to date.
>
>Most of the comments in
>https://gcc.gnu.org/pipermail/gcc-patches/2020-November/558090.html
>still apply to this version.
>
>>Adds <barrier>
>>
>>libstdc++/ChangeLog:
>>
>>	* include/Makefile.am (std_headers): Add new header.
>>	* include/Makefile.in: Regenerate.
>>	* include/std/barrier: New file.
>>	* include/std/version: Add __cpp_lib_barrier feature test macro.
>>	* testsuite/30_thread/barrier/1.cc: New test.
>>	* testsuite/30_thread/barrier/2.cc: Likewise.
>>	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>>	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>>	* testsuite/30_thread/barrier/arrive.cc: Likewise.
>>	* testsuite/30_thread/barrier/completion.cc: Likewise.
>>	* testsuite/30_thread/barrier/max.cc: Likewise.
>>---
>>libstdc++-v3/include/Makefile.am              |   1 +
>>libstdc++-v3/include/Makefile.in              |   1 +
>>libstdc++-v3/include/std/barrier              | 258 ++++++++++++++++++
>>libstdc++-v3/include/std/version              |   3 +
>>.../testsuite/30_threads/barrier/1.cc         |  27 ++
>>.../testsuite/30_threads/barrier/2.cc         |  27 ++
>>.../testsuite/30_threads/barrier/arrive.cc    |  51 ++++
>>.../30_threads/barrier/arrive_and_drop.cc     |  49 ++++
>>.../30_threads/barrier/arrive_and_wait.cc     |  51 ++++
>>.../30_threads/barrier/completion.cc          |  54 ++++
>>.../testsuite/30_threads/barrier/max.cc       |  44 +++
>>11 files changed, 566 insertions(+)
>>create mode 100644 libstdc++-v3/include/std/barrier
>>create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
>>create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
>>create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>>create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
>>create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
>>create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
>>create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc
>>
>>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>>index ca413b8fdfe..a20dd461fd1 100644
>>--- a/libstdc++-v3/include/Makefile.am
>>+++ b/libstdc++-v3/include/Makefile.am
>>@@ -30,6 +30,7 @@ std_headers = \
>>	${std_srcdir}/any \
>>	${std_srcdir}/array \
>>	${std_srcdir}/atomic \
>>+	${std_srcdir}/barrier \
>>	${std_srcdir}/bit \
>>	${std_srcdir}/bitset \
>>	${std_srcdir}/charconv \
>
>The new header should also be added to include/precompiled/stdc++.h
>and doc/doxygen/user.cfg.in
>
>>diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
>>new file mode 100644
>>index 00000000000..a6cc6a058dd
>>--- /dev/null
>>+++ b/libstdc++-v3/include/std/barrier
>>@@ -0,0 +1,258 @@
>>+// <barrier> -*- C++ -*-
>>+
>>+// 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
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// This implementation is based on libcxx/include/barrier
>>+//===-- barrier.h --------------------------------------------------===//
>>+//
>>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>>+// See https://llvm.org/LICENSE.txt for license information.
>>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>>+//
>>+//===---------------------------------------------------------------===//
>
>This file doesn't have the usual @file doxygen block.
>
>>+
>>+#ifndef _GLIBCXX_BARRIER
>>+#define _GLIBCXX_BARRIER 1
>>+
>>+#pragma GCC system_header
>>+
>>+#if __cplusplus > 201703L
>>+#include <bits/c++config.h>
>>+
>>+#if defined(_GLIBCXX_HAS_GTHREADS)
>
>This doesn't match the condition used in <bits/atomic_wait.h>. I've
>added _GLIBCXX_HAVE_ATOMIC_WAIT so it could check that.
>
>>+#define __cpp_lib_barrier 201907L
>>+#include <bits/atomic_base.h>
>>+#include <bits/atomic_wait.h>
>
>This is already included by <bits/atomic_base.h>
>
>I suggest:
>
>#if __cplusplus > 201703L
>#include <bits/atomic_base.h>
>#ifdef _GLIBCXX_HAVE_ATOMIC_WAIT
>#include <bits/functional_hash.h>
>...
>#define __cpp_lib_barrier 201907L
>
>We should only define the __cpp_lib macro after including all other
>headers, otherwise we advertise to those headers that the feature
>exists, but it hasn't been defined yet. Nothing is trying to use
>barrier in the rest of the library yet, but that could change.
>
>>+#include <bits/functional_hash.h>
>>+
>>+#include <memory>
>>+
>>+namespace std _GLIBCXX_VISIBILITY(default)
>>+{
>>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>+
>>+  struct __empty_completion
>>+  {
>>+    _GLIBCXX_ALWAYS_INLINE void
>>+    operator()() noexcept
>>+    { }
>>+  };
>>+
>>+/*
>>+
>>+The default implementation of __tree_barrier is a classic tree barrier.
>>+
>>+It looks different from literature pseudocode for two main reasons:
>>+ 1. Threads that call into std::barrier functions do not provide indices,
>>+    so a numbering step is added before the actual barrier algorithm,
>>+    appearing as an N+1 round to the N rounds of the tree barrier.
>>+ 2. A great deal of attention has been paid to avoid cache line thrashing
>>+    by flattening the tree structure into cache-line sized arrays, that
>>+    are indexed in an efficient way.
>>+
>>+*/
>>+
>>+  using __barrier_phase_t = uint8_t;
>>+
>>+  template<typename _CompletionF>
>>+    class __tree_barrier
>>+    {
>>+      struct alignas(64) /* naturally-align the heap state */ __state_t
>>+      {
>>+	struct
>>+	{
>>+	  __atomic_base<__barrier_phase_t> __phase = { 0 };
>>+	} __tickets[64];
>>+      };
>>+
>>+      ptrdiff_t _M_expected;
>>+      unique_ptr<char[]> _M_state_allocation;
>>+      __state_t* 		_M_state;
>>+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
>>+      _CompletionF _M_completion;
>>+
>>+      using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>;
>>+      using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>;
>>+      static constexpr size_t __phase_alignment =
>>+		      __atomic_phase_ref_t::required_alignment;
>>+      alignas(__phase_alignment) __barrier_phase_t  _M_phase;
>>+
>>+      static __gthread_t
>>+      _S_get_tid() noexcept
>>+      {
>>+#ifdef __GLIBC__
>>+	// For the GNU C library pthread_self() is usable without linking to
>>+	// libpthread.so but returns 0, so we cannot use it in single-threaded
>>+	// programs, because this_thread::get_id() != thread::id{} must be true.
>>+	// We know that pthread_t is an integral type in the GNU C library.
>>+	if (!__gthread_active_p())
>>+	  return 1;
>>+#endif
>>+	return __gthread_self();
>
>If you include <bits/std_thread.h> you can use this_thread::get_id()
>here. That avoids needing to repeat the glibc-specific hack.
>
>>+      }
>>+
>>+      bool
>>+      _M_arrive(__barrier_phase_t __old_phase)
>>+      {
>>+	__barrier_phase_t const __half_step = __old_phase + 1,
>>+				__full_step = __old_phase + 2;
>>+	size_t __current_expected = _M_expected;
>>+	size_t __current = _Hash_impl::hash(_S_get_tid())
>>+				% ((_M_expected + 1) >> 1);
>
>If we move std::hash<std::thread::id> into <bits/std_thread.h> then
>you can just use that.

Done in r11-6227.

Tested powerpc64le-linux, committed to trunk.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 2532 bytes --]

commit 8cdca5f9c706af118390489efc94336f6214f515
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Dec 17 11:59:07 2020

    libstdc++: Move std::hash<std::thread::id> to <bits/std_thread.h>
    
    This makes the hash function available without including the whole of
    <thread>, which is needed for <barrier>.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/std_thread.h (hash<thread::id>): Move here,
            from ...
            * include/std/thread (hash<thread::id>): ... here.

diff --git a/libstdc++-v3/include/bits/std_thread.h b/libstdc++-v3/include/bits/std_thread.h
index 24bd5fbd44e..4810d355695 100644
--- a/libstdc++-v3/include/bits/std_thread.h
+++ b/libstdc++-v3/include/bits/std_thread.h
@@ -38,6 +38,7 @@
 #include <exception>		// std::terminate
 #include <iosfwd>		// std::basic_ostream
 #include <tuple>		// std::tuple
+#include <bits/functional_hash.h> // std::hash
 #include <bits/invoke.h>	// std::__invoke
 #include <bits/refwrap.h>       // not required, but helpful to users
 #include <bits/unique_ptr.h>	// std::unique_ptr
@@ -288,6 +289,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // N.B. other comparison operators are defined in <thread>
 
+  // DR 889.
+  /// std::hash specialization for thread::id.
+  template<>
+    struct hash<thread::id>
+    : public __hash_base<size_t, thread::id>
+    {
+      size_t
+      operator()(const thread::id& __id) const noexcept
+      { return std::_Hash_impl::hash(__id._M_thread); }
+    };
+
   namespace this_thread
   {
     /// this_thread::get_id
diff --git a/libstdc++-v3/include/std/thread b/libstdc++-v3/include/std/thread
index 8d0ede2b6c2..10fb9e631be 100644
--- a/libstdc++-v3/include/std/thread
+++ b/libstdc++-v3/include/std/thread
@@ -43,7 +43,6 @@
 #endif
 
 #include <bits/std_thread.h> // std::thread, get_id, yield
-#include <bits/functional_hash.h> // std::hash
 
 #ifdef _GLIBCXX_USE_NANOSLEEP
 # include <cerrno>  // errno, EINTR
@@ -94,17 +93,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   { return !(__x < __y); }
 #endif // __cpp_lib_three_way_comparison
 
-  // DR 889.
-  /// std::hash specialization for thread::id.
-  template<>
-    struct hash<thread::id>
-    : public __hash_base<size_t, thread::id>
-    {
-      size_t
-      operator()(const thread::id& __id) const noexcept
-      { return std::_Hash_impl::hash(__id._M_thread); }
-    };
-
   template<class _CharT, class _Traits>
     inline basic_ostream<_CharT, _Traits>&
     operator<<(basic_ostream<_CharT, _Traits>& __out, thread::id __id)

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] libstdc++: Add support for C++20 barriers
  2020-11-27 17:46     ` Jonathan Wakely
  2020-12-17 14:06       ` Jonathan Wakely
@ 2020-12-17 19:11       ` Thomas Rodgers
  2020-12-17 23:37         ` Thomas Rodgers
  1 sibling, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2020-12-17 19:11 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

Let's see if this one sticks...

Adds <barrier>

libstdc++/ChangeLog:

	* doc/doxygen/user.cfg.in: Add new header.
	* include/Makefile.am (std_headers): likewise.
	* include/Makefile.in: Regenerate.
        * include/precompiled/stdc++.h: Add new header.
	* include/std/barrier: New file.
	* include/std/version: Add __cpp_lib_barrier feature test macro.
	* testsuite/30_thread/barrier/1.cc: New test.
	* testsuite/30_thread/barrier/2.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
	* testsuite/30_thread/barrier/arrive.cc: Likewise.
	* testsuite/30_thread/barrier/completion.cc: Likewise.
	* testsuite/30_thread/barrier/max.cc: Likewise.
---
 libstdc++-v3/doc/doxygen/user.cfg.in          |   1 +
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 libstdc++-v3/include/precompiled/stdc++.h     |   1 +
 libstdc++-v3/include/std/barrier              | 267 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   3 +
 .../testsuite/30_threads/barrier/1.cc         |  27 ++
 .../testsuite/30_threads/barrier/2.cc         |  27 ++
 .../testsuite/30_threads/barrier/arrive.cc    |  48 ++++
 .../30_threads/barrier/arrive_and_drop.cc     |  46 +++
 .../30_threads/barrier/arrive_and_wait.cc     |  46 +++
 .../30_threads/barrier/completion.cc          |  53 ++++
 .../testsuite/30_threads/barrier/max.cc       |  40 +++
 13 files changed, 561 insertions(+)
 create mode 100644 libstdc++-v3/include/std/barrier
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc

diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 2261d572efb..fb90db65e55 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -850,6 +850,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
                          include/any \
                          include/array \
                          include/atomic \
+                         include/barrier \
                          include/bit \
                          include/bitset \
                          include/charconv \
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 958dfea5a98..231f7c3ec8f 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -30,6 +30,7 @@ std_headers = \
 	${std_srcdir}/any \
 	${std_srcdir}/array \
 	${std_srcdir}/atomic \
+	${std_srcdir}/barrier \
 	${std_srcdir}/bit \
 	${std_srcdir}/bitset \
 	${std_srcdir}/charconv \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index 692fae76a42..424c0e277d1 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -134,6 +134,7 @@
 #endif
 
 #if __cplusplus > 201703L
+#include <barrier>
 #include <bit>
 #include <compare>
 #include <concepts>
diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
new file mode 100644
index 00000000000..f3c819774bd
--- /dev/null
+++ b/libstdc++-v3/include/std/barrier
@@ -0,0 +1,267 @@
+// <barrier> -*- C++ -*-
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/include/barrier
+//===-- barrier.h --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+/** @file include/atomic
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_BARRIER
+#define _GLIBCXX_BARRIER 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#include <bits/atomic_base.h>
+#if __cpp_lib_atomic_wait  && __cpp_aligned_new
+#include <bits/functional_hash.h>
+#include <bits/std_thread.h>
+
+#include <array>
+#include <memory>
+
+#define __cpp_lib_barrier 201907L
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  struct __empty_completion
+  {
+    _GLIBCXX_ALWAYS_INLINE void
+    operator()() noexcept
+    { }
+  };
+
+/*
+
+The default implementation of __tree_barrier is a classic tree barrier.
+
+It looks different from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+  enum class __barrier_phase_t : unsigned char { };
+
+  template<typename _CompletionF>
+    class __tree_barrier
+    {
+      using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>;
+      using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>;
+      static constexpr auto __phase_alignment =
+		      __atomic_phase_ref_t::required_alignment;
+
+      using __tickets_t = std::array<__barrier_phase_t, 64>;
+      struct alignas(64) /* naturally-align the heap state */ __state_t
+      {
+	alignas(__phase_alignment) __tickets_t __tickets;
+
+	/*
+	struct __ticket_t
+	{
+	  __atomic_base<__barrier_phase_t> __phase
+	      = { static_cast<__barrier_phase_t>(0) };
+	};
+	std::array<__ticket_t, 64> __tickets;
+	*/
+
+      };
+
+      ptrdiff_t _M_expected;
+      // unique_ptr<char[]> _M_state_allocation;
+      // __state_t* 		_M_state;
+      unique_ptr<__state_t[]> _M_state;
+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
+      _CompletionF _M_completion;
+
+      /*
+      using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>;
+      using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>;
+      static constexpr size_t __phase_alignment =
+		      __atomic_phase_ref_t::required_alignment;
+      */
+      alignas(__phase_alignment) __barrier_phase_t  _M_phase;
+
+      bool
+      _M_arrive(__barrier_phase_t __old_phase)
+      {
+	const auto __old_phase_val = static_cast<unsigned char>(__old_phase);
+	const auto __half_step =
+			   static_cast<__barrier_phase_t>(__old_phase_val + 1);
+	const auto __full_step =
+			   static_cast<__barrier_phase_t>(__old_phase_val + 2);
+
+	size_t __current_expected = _M_expected;
+	std::hash<std::thread::id>__hasher;
+	size_t __current = __hasher(std::this_thread::get_id())
+					  % ((_M_expected + 1) >> 1);
+
+	for (int __round = 0; ; ++__round)
+	  {
+	    if (__current_expected <= 1)
+		return true;
+	    size_t const __end_node = ((__current_expected + 1) >> 1),
+			 __last_node = __end_node - 1;
+	    for ( ; ; ++__current)
+	      {
+		if (__current == __end_node)
+		  __current = 0;
+		auto __expect = __old_phase;
+		__atomic_phase_ref_t __phase(_M_state[__current]
+						.__tickets[__round]);
+		if (__current == __last_node && (__current_expected & 1))
+		  {
+		    if (__phase.compare_exchange_strong(__expect, __full_step,
+						        memory_order_acq_rel))
+		      break;     // I'm 1 in 1, go to next __round
+		  }
+		else if (__phase.compare_exchange_strong(__expect, __half_step,
+						         memory_order_acq_rel))
+		  {
+		    return false; // I'm 1 in 2, done with arrival
+		  }
+		else if (__expect == __half_step)
+		  {
+		    if (__phase.compare_exchange_strong(__expect, __full_step,
+						        memory_order_acq_rel))
+		      break;    // I'm 2 in 2, go to next __round
+		  }
+	      }
+	    __current_expected = __last_node + 1;
+	    __current >>= 1;
+	  }
+      }
+
+    public:
+      using arrival_token = __barrier_phase_t;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __PTRDIFF_MAX__; }
+
+      __tree_barrier(ptrdiff_t __expected, _CompletionF __completion)
+	  : _M_expected(__expected), _M_expected_adjustment(0),
+	    _M_completion(move(__completion)),
+	    _M_phase(static_cast<__barrier_phase_t>(0))
+      {
+	size_t const __count = (_M_expected + 1) >> 1;
+
+	_M_state = std::make_unique<__state_t[]>(__count);
+      }
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update)
+      {
+	__atomic_phase_ref_t __phase(_M_phase);
+	const auto __old_phase = __phase.load(memory_order_relaxed);
+	const auto __cur = static_cast<unsigned char>(__old_phase);
+	for(; __update; --__update)
+	  {
+	    if(_M_arrive(__old_phase))
+	      {
+		_M_completion();
+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
+		_M_expected_adjustment.store(0, memory_order_relaxed);
+		auto __new_phase = static_cast<__barrier_phase_t>(__cur + 2);
+		__phase.store(__new_phase, memory_order_release);
+		__phase.notify_all();
+	      }
+	  }
+	return __old_phase;
+      }
+
+      void
+      wait(arrival_token&& __old_phase) const
+      {
+	__atomic_phase_const_ref_t __phase(_M_phase);
+	auto const __test_fn = [=, this]
+	  {
+	    return __phase.load(memory_order_acquire) != __old_phase;
+	  };
+	std::__atomic_wait(&_M_phase, __old_phase, __test_fn);
+      }
+
+      void
+      arrive_and_drop()
+      {
+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
+	(void)arrive(1);
+      }
+    };
+
+  template<typename _CompletionF = __empty_completion>
+    class barrier
+    {
+      // Note, we may introduce a "central" barrier algorithm at some point
+      // for more space constrained targets
+      using __algorithm_t = __tree_barrier<_CompletionF>;
+      __algorithm_t _M_b;
+
+    public:
+      using arrival_token = typename __tree_barrier<_CompletionF>::arrival_token;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __algorithm_t::max(); }
+
+      explicit barrier(ptrdiff_t __count,
+		       _CompletionF __completion = _CompletionF())
+	  : _M_b(__count, std::move(__completion))
+      { }
+
+      barrier(barrier const&) = delete;
+      barrier& operator=(barrier const&) = delete;
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update = 1)
+      { return _M_b.arrive(__update); }
+
+      void
+      wait(arrival_token&& __phase) const
+      { _M_b.wait(std::move(__phase)); }
+
+      void
+      arrive_and_wait()
+      { wait(arrive()); }
+
+      void
+      arrive_and_drop()
+      { _M_b.arrive_and_drop(); }
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // _GLIBCXX_HAS_GTHREADS
+#endif // __cplusplus > 201703L
+#endif // _GLIBCXX_BARRIER
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index e4a8bed52ab..07d17433c5b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -200,6 +200,9 @@
 #if defined _GLIBCXX_HAS_GTHREADS || defined _GLIBCXX_HAVE_LINUX_FUTEX
 # define __cpp_lib_atomic_wait 201907L
 #endif
+#if __cpp_lib_atomic_wait
+#define __cpp_lib_barrier 201907L
+#endif
 #define __cpp_lib_bind_front 201907L
 #if __has_builtin(__builtin_bit_cast)
 # define __cpp_lib_bit_cast 201806L
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
new file mode 100644
index 00000000000..0b38160a58b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <barrier>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <barrier>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <barrier>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
new file mode 100644
index 00000000000..1d8d83639e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <version>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
new file mode 100644
index 00000000000..df70587db5b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -0,0 +1,48 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
new file mode 100644
index 00000000000..746a8f470e3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -0,0 +1,46 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
new file mode 100644
index 00000000000..509986f3b78
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -0,0 +1,46 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
new file mode 100644
index 00000000000..c7997d9d7fb
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -0,0 +1,53 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&] { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+	b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  VERIFY( x == 10 );
+  t.join();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
new file mode 100644
index 00000000000..278bc3fdd10
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
@@ -0,0 +1,40 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+}
-- 
2.26.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] libstdc++: Add support for C++20 barriers
  2020-12-17 19:11       ` Thomas Rodgers
@ 2020-12-17 23:37         ` Thomas Rodgers
  2020-12-18 10:08           ` Jonathan Wakely
  0 siblings, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2020-12-17 23:37 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

Cleans up a few things mentioned on IRC.

Adds <barrier>

libstdc++/ChangeLog:

	* doc/doxygen/user.cfg.in: Add new header.
	* include/Makefile.am (std_headers): likewise.
	* include/Makefile.in: Regenerate.
        * include/precompiled/stdc++.h: Add new header.
	* include/std/barrier: New file.
	* include/std/version: Add __cpp_lib_barrier feature test macro.
	* testsuite/30_thread/barrier/1.cc: New test.
	* testsuite/30_thread/barrier/2.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
	* testsuite/30_thread/barrier/arrive.cc: Likewise.
	* testsuite/30_thread/barrier/completion.cc: Likewise.
	* testsuite/30_thread/barrier/max.cc: Likewise.
---
 libstdc++-v3/doc/doxygen/user.cfg.in          |   1 +
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 libstdc++-v3/include/precompiled/stdc++.h     |   1 +
 libstdc++-v3/include/std/barrier              | 249 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   3 +
 .../testsuite/30_threads/barrier/1.cc         |  27 ++
 .../testsuite/30_threads/barrier/2.cc         |  27 ++
 .../testsuite/30_threads/barrier/arrive.cc    |  48 ++++
 .../30_threads/barrier/arrive_and_drop.cc     |  46 ++++
 .../30_threads/barrier/arrive_and_wait.cc     |  46 ++++
 .../30_threads/barrier/completion.cc          |  53 ++++
 .../testsuite/30_threads/barrier/max.cc       |  40 +++
 13 files changed, 543 insertions(+)
 create mode 100644 libstdc++-v3/include/std/barrier
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc

diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 2261d572efb..fb90db65e55 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -850,6 +850,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
                          include/any \
                          include/array \
                          include/atomic \
+                         include/barrier \
                          include/bit \
                          include/bitset \
                          include/charconv \
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 958dfea5a98..231f7c3ec8f 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -30,6 +30,7 @@ std_headers = \
 	${std_srcdir}/any \
 	${std_srcdir}/array \
 	${std_srcdir}/atomic \
+	${std_srcdir}/barrier \
 	${std_srcdir}/bit \
 	${std_srcdir}/bitset \
 	${std_srcdir}/charconv \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index 692fae76a42..424c0e277d1 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -134,6 +134,7 @@
 #endif
 
 #if __cplusplus > 201703L
+#include <barrier>
 #include <bit>
 #include <compare>
 #include <concepts>
diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
new file mode 100644
index 00000000000..db514540e37
--- /dev/null
+++ b/libstdc++-v3/include/std/barrier
@@ -0,0 +1,249 @@
+// <barrier> -*- C++ -*-
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/include/barrier
+//===-- barrier.h --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+/** @file include/barrier
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_BARRIER
+#define _GLIBCXX_BARRIER 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#include <bits/atomic_base.h>
+#if __cpp_lib_atomic_wait  && __cpp_aligned_new
+#include <bits/functional_hash.h>
+#include <bits/std_thread.h>
+#include <bits/unique_ptr.h>
+
+#include <array>
+
+#define __cpp_lib_barrier 201907L
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  struct __empty_completion
+  {
+    _GLIBCXX_ALWAYS_INLINE void
+    operator()() noexcept
+    { }
+  };
+
+/*
+
+The default implementation of __tree_barrier is a classic tree barrier.
+
+It looks different from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+  enum class __barrier_phase_t : unsigned char { };
+
+  template<typename _CompletionF>
+    class __tree_barrier
+    {
+      using __atomic_phase_ref_t = std::__atomic_ref<__barrier_phase_t>;
+      using __atomic_phase_const_ref_t = std::__atomic_ref<const __barrier_phase_t>;
+      static constexpr auto __phase_alignment =
+		      __atomic_phase_ref_t::required_alignment;
+
+      using __tickets_t = std::array<__barrier_phase_t, 64>;
+      struct alignas(64) /* naturally-align the heap state */ __state_t
+      {
+	alignas(__phase_alignment) __tickets_t __tickets;
+      };
+
+      ptrdiff_t _M_expected;
+      unique_ptr<__state_t[]> _M_state;
+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
+      _CompletionF _M_completion;
+
+      alignas(__phase_alignment) __barrier_phase_t  _M_phase;
+
+      bool
+      _M_arrive(__barrier_phase_t __old_phase)
+      {
+	const auto __old_phase_val = static_cast<unsigned char>(__old_phase);
+	const auto __half_step =
+			   static_cast<__barrier_phase_t>(__old_phase_val + 1);
+	const auto __full_step =
+			   static_cast<__barrier_phase_t>(__old_phase_val + 2);
+
+	size_t __current_expected = _M_expected;
+	std::hash<std::thread::id>__hasher;
+	size_t __current = __hasher(std::this_thread::get_id())
+					  % ((_M_expected + 1) >> 1);
+
+	for (int __round = 0; ; ++__round)
+	  {
+	    if (__current_expected <= 1)
+		return true;
+	    size_t const __end_node = ((__current_expected + 1) >> 1),
+			 __last_node = __end_node - 1;
+	    for ( ; ; ++__current)
+	      {
+		if (__current == __end_node)
+		  __current = 0;
+		auto __expect = __old_phase;
+		__atomic_phase_ref_t __phase(_M_state[__current]
+						.__tickets[__round]);
+		if (__current == __last_node && (__current_expected & 1))
+		  {
+		    if (__phase.compare_exchange_strong(__expect, __full_step,
+						        memory_order_acq_rel))
+		      break;     // I'm 1 in 1, go to next __round
+		  }
+		else if (__phase.compare_exchange_strong(__expect, __half_step,
+						         memory_order_acq_rel))
+		  {
+		    return false; // I'm 1 in 2, done with arrival
+		  }
+		else if (__expect == __half_step)
+		  {
+		    if (__phase.compare_exchange_strong(__expect, __full_step,
+						        memory_order_acq_rel))
+		      break;    // I'm 2 in 2, go to next __round
+		  }
+	      }
+	    __current_expected = __last_node + 1;
+	    __current >>= 1;
+	  }
+      }
+
+    public:
+      using arrival_token = __barrier_phase_t;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __PTRDIFF_MAX__; }
+
+      __tree_barrier(ptrdiff_t __expected, _CompletionF __completion)
+	  : _M_expected(__expected), _M_expected_adjustment(0),
+	    _M_completion(move(__completion)),
+	    _M_phase(static_cast<__barrier_phase_t>(0))
+      {
+	size_t const __count = (_M_expected + 1) >> 1;
+
+	_M_state = std::make_unique<__state_t[]>(__count);
+      }
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update)
+      {
+	__atomic_phase_ref_t __phase(_M_phase);
+	const auto __old_phase = __phase.load(memory_order_relaxed);
+	const auto __cur = static_cast<unsigned char>(__old_phase);
+	for(; __update; --__update)
+	  {
+	    if(_M_arrive(__old_phase))
+	      {
+		_M_completion();
+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
+		_M_expected_adjustment.store(0, memory_order_relaxed);
+		auto __new_phase = static_cast<__barrier_phase_t>(__cur + 2);
+		__phase.store(__new_phase, memory_order_release);
+		__phase.notify_all();
+	      }
+	  }
+	return __old_phase;
+      }
+
+      void
+      wait(arrival_token&& __old_phase) const
+      {
+	__atomic_phase_const_ref_t __phase(_M_phase);
+	auto const __test_fn = [=, this]
+	  {
+	    return __phase.load(memory_order_acquire) != __old_phase;
+	  };
+	std::__atomic_wait(&_M_phase, __old_phase, __test_fn);
+      }
+
+      void
+      arrive_and_drop()
+      {
+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
+	(void)arrive(1);
+      }
+    };
+
+  template<typename _CompletionF = __empty_completion>
+    class barrier
+    {
+      // Note, we may introduce a "central" barrier algorithm at some point
+      // for more space constrained targets
+      using __algorithm_t = __tree_barrier<_CompletionF>;
+      __algorithm_t _M_b;
+
+    public:
+      using arrival_token = typename __tree_barrier<_CompletionF>::arrival_token;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __algorithm_t::max(); }
+
+      explicit barrier(ptrdiff_t __count,
+		       _CompletionF __completion = _CompletionF())
+	  : _M_b(__count, std::move(__completion))
+      { }
+
+      barrier(barrier const&) = delete;
+      barrier& operator=(barrier const&) = delete;
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update = 1)
+      { return _M_b.arrive(__update); }
+
+      void
+      wait(arrival_token&& __phase) const
+      { _M_b.wait(std::move(__phase)); }
+
+      void
+      arrive_and_wait()
+      { wait(arrive()); }
+
+      void
+      arrive_and_drop()
+      { _M_b.arrive_and_drop(); }
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // __cpp_lib_atomic_wait  && __cpp_aligned_new
+#endif // __cplusplus > 201703L
+#endif // _GLIBCXX_BARRIER
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index e4a8bed52ab..07d17433c5b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -200,6 +200,9 @@
 #if defined _GLIBCXX_HAS_GTHREADS || defined _GLIBCXX_HAVE_LINUX_FUTEX
 # define __cpp_lib_atomic_wait 201907L
 #endif
+#if __cpp_lib_atomic_wait
+#define __cpp_lib_barrier 201907L
+#endif
 #define __cpp_lib_bind_front 201907L
 #if __has_builtin(__builtin_bit_cast)
 # define __cpp_lib_bit_cast 201806L
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
new file mode 100644
index 00000000000..0b38160a58b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <barrier>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <barrier>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <barrier>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
new file mode 100644
index 00000000000..1d8d83639e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <version>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
new file mode 100644
index 00000000000..df70587db5b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -0,0 +1,48 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
new file mode 100644
index 00000000000..746a8f470e3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -0,0 +1,46 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
new file mode 100644
index 00000000000..509986f3b78
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -0,0 +1,46 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
new file mode 100644
index 00000000000..c7997d9d7fb
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -0,0 +1,53 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&] { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+	b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  VERIFY( x == 10 );
+  t.join();
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
new file mode 100644
index 00000000000..278bc3fdd10
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
@@ -0,0 +1,40 @@
+// { dg-options "-std=gnu++2a" }
+// { dg-do run { target c++2a } }
+// { dg-require-gthreads "" }
+// { dg-additional-options "-pthread" { target pthread } }
+
+// 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
+// <http://www.gnu.org/licenses/>.
+
+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include <barrier>
+#include <thread>
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+}
-- 
2.26.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-12-17 23:37         ` Thomas Rodgers
@ 2020-12-18 10:08           ` Jonathan Wakely
  2021-01-07 20:56             ` Thomas Rodgers
  0 siblings, 1 reply; 23+ messages in thread
From: Jonathan Wakely @ 2020-12-18 10:08 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 17/12/20 15:37 -0800, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>Cleans up a few things mentioned on IRC.
>
>Adds <barrier>
>
>libstdc++/ChangeLog:
>
>	* doc/doxygen/user.cfg.in: Add new header.
>	* include/Makefile.am (std_headers): likewise.
>	* include/Makefile.in: Regenerate.
>        * include/precompiled/stdc++.h: Add new header.
>	* include/std/barrier: New file.
>	* include/std/version: Add __cpp_lib_barrier feature test macro.
>	* testsuite/30_thread/barrier/1.cc: New test.
>	* testsuite/30_thread/barrier/2.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>	* testsuite/30_thread/barrier/arrive.cc: Likewise.
>	* testsuite/30_thread/barrier/completion.cc: Likewise.
>	* testsuite/30_thread/barrier/max.cc: Likewise.


>+#ifndef _GLIBCXX_BARRIER
>+#define _GLIBCXX_BARRIER 1
>+
>+#pragma GCC system_header
>+
>+#if __cplusplus > 201703L
>+#include <bits/atomic_base.h>
>+#if __cpp_lib_atomic_wait  && __cpp_aligned_new

There's an extra space here before the && operator.

>+#endif // __cpp_lib_atomic_wait  && __cpp_aligned_new

And here.

>+#endif // __cplusplus > 201703L
>+#endif // _GLIBCXX_BARRIER
>+
>diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
>index e4a8bed52ab..07d17433c5b 100644
>--- a/libstdc++-v3/include/std/version
>+++ b/libstdc++-v3/include/std/version
>@@ -200,6 +200,9 @@
> #if defined _GLIBCXX_HAS_GTHREADS || defined _GLIBCXX_HAVE_LINUX_FUTEX
> # define __cpp_lib_atomic_wait 201907L
> #endif
>+#if __cpp_lib_atomic_wait

This needs to match the condition used in <barrier>.

>+#define __cpp_lib_barrier 201907L
>+#endif

You could just put it inside the previous block where
__cpp_lib_atomic_wait is defined:

#if defined _GLIBCXX_HAS_GTHREADS || defined _GLIBCXX_HAVE_LINUX_FUTEX
# define __cpp_lib_atomic_wait 201907L
# if __cpp_aligned_new
#  define __cpp_lib_barrier 201907L
# endif
#endif

> #define __cpp_lib_bind_front 201907L
> #if __has_builtin(__builtin_bit_cast)
> # define __cpp_lib_bit_cast 201806L
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
>new file mode 100644
>index 00000000000..0b38160a58b
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }

This test will fail for non-gthreads targets, because they don't
define the macro. This needs the same condition as the similar
29_atomics/atomic/wait_notify/1.cc test:

// { dg-require-effective-target gthreads }

(which is the new way to say { dg-requires-gthread "" })

>+#include <barrier>
>+
>+#ifndef __cpp_lib_barrier
>+# error "Feature-test macro for barrier missing in <barrier>"
>+#elif __cpp_lib_barrier != 201907L
>+# error "Feature-test macro for barrier has wrong value in <barrier>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
>new file mode 100644
>index 00000000000..1d8d83639e0
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
>+// <http://www.gnu.org/licenses/>.
>+
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do compile { target c++2a } }

Same here.

>+#include <version>
>+
>+#ifndef __cpp_lib_barrier
>+# error "Feature-test macro for barrier missing in <version>"
>+#elif __cpp_lib_barrier != 201907L
>+# error "Feature-test macro for barrier has wrong value in <version>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>new file mode 100644
>index 00000000000..df70587db5b
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>@@ -0,0 +1,48 @@
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-gthreads "" }
>+// { dg-additional-options "-pthread" { target pthread } }
>+
>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp

Please replace "implementation" with "test" or something like that,
since this isn't the implementation. Same for all the other tests
taken from LLVM.

>+//===----------------------------------------------------------------------===//
>+//
>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>+// See https://llvm.org/LICENSE.txt for license information.
>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>+//
>+//===----------------------------------------------------------------------===//
>+
>+#include <barrier>
>+#include <thread>
>+
>+int main(int, char**)
>+{
>+  std::barrier<> b(2);
>+
>+  auto tok = b.arrive();
>+  std::thread t([&](){
>+    (void)b.arrive();
>+  });
>+  b.wait(std::move(tok));
>+  t.join();
>+
>+  auto tok2 = b.arrive(2);
>+  b.wait(std::move(tok2));
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
>new file mode 100644
>index 00000000000..746a8f470e3
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
>@@ -0,0 +1,46 @@
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-gthreads "" }
>+// { dg-additional-options "-pthread" { target pthread } }
>+
>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
>+//===----------------------------------------------------------------------===//
>+//
>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>+// See https://llvm.org/LICENSE.txt for license information.
>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>+//
>+//===----------------------------------------------------------------------===//
>+
>+#include <barrier>
>+#include <thread>
>+
>+int main(int, char**)
>+{
>+  std::barrier<> b(2);
>+
>+  std::thread t([&](){
>+    b.arrive_and_drop();
>+  });
>+
>+  b.arrive_and_wait();
>+  b.arrive_and_wait();
>+  t.join();
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
>new file mode 100644
>index 00000000000..509986f3b78
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
>@@ -0,0 +1,46 @@
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-gthreads "" }
>+// { dg-additional-options "-pthread" { target pthread } }
>+
>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp

Wrong filename?

>+//===----------------------------------------------------------------------===//
>+//
>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>+// See https://llvm.org/LICENSE.txt for license information.
>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>+//
>+//===----------------------------------------------------------------------===//
>+
>+#include <barrier>
>+#include <thread>
>+
>+int main(int, char**)
>+{
>+  std::barrier<> b(2);
>+
>+  std::thread t([&](){
>+    for(int i = 0; i < 10; ++i)
>+      b.arrive_and_wait();
>+  });
>+  for(int i = 0; i < 10; ++i)
>+    b.arrive_and_wait();
>+  t.join();
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
>new file mode 100644
>index 00000000000..c7997d9d7fb
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
>@@ -0,0 +1,53 @@
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do run { target c++2a } }
>+// { dg-require-gthreads "" }
>+// { dg-additional-options "-pthread" { target pthread } }
>+
>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
>+//===----------------------------------------------------------------------===//
>+//
>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>+// See https://llvm.org/LICENSE.txt for license information.
>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>+//
>+//===----------------------------------------------------------------------===//
>+
>+#include <barrier>
>+#include <thread>
>+
>+#include <testsuite_hooks.h>
>+
>+int main(int, char**)
>+{
>+  int x = 0;
>+  auto comp = [&] { x += 1; };
>+  std::barrier<decltype(comp)> b(2, comp);
>+
>+  std::thread t([&](){
>+      for(int i = 0; i < 10; ++i)
>+	b.arrive_and_wait();
>+  });
>+
>+  for(int i = 0; i < 10; ++i)
>+    b.arrive_and_wait();
>+
>+  VERIFY( x == 10 );
>+  t.join();
>+}
>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
>new file mode 100644
>index 00000000000..278bc3fdd10
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
>@@ -0,0 +1,40 @@
>+// { dg-options "-std=gnu++2a" }
>+// { dg-do run { target c++2a } }

This should be 'compile' not 'run'.

I think I'd rather see these static_asserts put into one of the
existing tests, so we don't pay for the overhead of another dejagnu
test (with all its setup and target checking) just for this.

Maybe in barrier/1.cc since that's not doing very much work either
(and don't forget to move the dg-additional-options there too).

>+// { dg-require-gthreads "" }
>+// { dg-additional-options "-pthread" { target pthread } }
>+
>+// 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
>+// <http://www.gnu.org/licenses/>.
>+
>+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
>+//===----------------------------------------------------------------------===//
>+//
>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>+// See https://llvm.org/LICENSE.txt for license information.
>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>+//
>+//===----------------------------------------------------------------------===//
>+
>+#include <barrier>
>+#include <thread>
>+
>+int main(int, char**)
>+{
>+  static_assert(std::barrier<>::max() > 0, "");
>+  auto l = [](){};
>+  static_assert(std::barrier<decltype(l)>::max() > 0, "");

No need for the string literals here, this is C++20.

OK with those changes, thanks.



^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2020-12-18 10:08           ` Jonathan Wakely
@ 2021-01-07 20:56             ` Thomas Rodgers
  2021-01-14 14:26               ` Jonathan Wakely
  0 siblings, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2021-01-07 20:56 UTC (permalink / raw)
  To: Thomas Rodgers, gcc-patches, libstdc++


Tested x86_64-pc-linux-gnu, committed to master.

Jonathan Wakely writes:

> On 17/12/20 15:37 -0800, Thomas Rodgers wrote:
>>From: Thomas Rodgers <trodgers@redhat.com>
>>
>>Cleans up a few things mentioned on IRC.
>>
>>Adds <barrier>
>>
>>libstdc++/ChangeLog:
>>
>>	* doc/doxygen/user.cfg.in: Add new header.
>>	* include/Makefile.am (std_headers): likewise.
>>	* include/Makefile.in: Regenerate.
>>        * include/precompiled/stdc++.h: Add new header.
>>	* include/std/barrier: New file.
>>	* include/std/version: Add __cpp_lib_barrier feature test macro.
>>	* testsuite/30_thread/barrier/1.cc: New test.
>>	* testsuite/30_thread/barrier/2.cc: Likewise.
>>	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>>	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>>	* testsuite/30_thread/barrier/arrive.cc: Likewise.
>>	* testsuite/30_thread/barrier/completion.cc: Likewise.
>>	* testsuite/30_thread/barrier/max.cc: Likewise.
>
>
>>+#ifndef _GLIBCXX_BARRIER
>>+#define _GLIBCXX_BARRIER 1
>>+
>>+#pragma GCC system_header
>>+
>>+#if __cplusplus > 201703L
>>+#include <bits/atomic_base.h>
>>+#if __cpp_lib_atomic_wait  && __cpp_aligned_new
>
> There's an extra space here before the && operator.
>
>>+#endif // __cpp_lib_atomic_wait  && __cpp_aligned_new
>
> And here.
>
>>+#endif // __cplusplus > 201703L
>>+#endif // _GLIBCXX_BARRIER
>>+
>>diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
>>index e4a8bed52ab..07d17433c5b 100644
>>--- a/libstdc++-v3/include/std/version
>>+++ b/libstdc++-v3/include/std/version
>>@@ -200,6 +200,9 @@
>> #if defined _GLIBCXX_HAS_GTHREADS || defined _GLIBCXX_HAVE_LINUX_FUTEX
>> # define __cpp_lib_atomic_wait 201907L
>> #endif
>>+#if __cpp_lib_atomic_wait
>
> This needs to match the condition used in <barrier>.
>
>>+#define __cpp_lib_barrier 201907L
>>+#endif
>
> You could just put it inside the previous block where
> __cpp_lib_atomic_wait is defined:
>
> #if defined _GLIBCXX_HAS_GTHREADS || defined _GLIBCXX_HAVE_LINUX_FUTEX
> # define __cpp_lib_atomic_wait 201907L
> # if __cpp_aligned_new
> #  define __cpp_lib_barrier 201907L
> # endif
> #endif
>
>> #define __cpp_lib_bind_front 201907L
>> #if __has_builtin(__builtin_bit_cast)
>> # define __cpp_lib_bit_cast 201806L
>>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
>>new file mode 100644
>>index 00000000000..0b38160a58b
>>--- /dev/null
>>+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// { dg-options "-std=gnu++2a" }
>>+// { dg-do compile { target c++2a } }
>
> This test will fail for non-gthreads targets, because they don't
> define the macro. This needs the same condition as the similar
> 29_atomics/atomic/wait_notify/1.cc test:
>
> // { dg-require-effective-target gthreads }
>
> (which is the new way to say { dg-requires-gthread "" })
>
>>+#include <barrier>
>>+
>>+#ifndef __cpp_lib_barrier
>>+# error "Feature-test macro for barrier missing in <barrier>"
>>+#elif __cpp_lib_barrier != 201907L
>>+# error "Feature-test macro for barrier has wrong value in <barrier>"
>>+#endif
>>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
>>new file mode 100644
>>index 00000000000..1d8d83639e0
>>--- /dev/null
>>+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// { dg-options "-std=gnu++2a" }
>>+// { dg-do compile { target c++2a } }
>
> Same here.
>
>>+#include <version>
>>+
>>+#ifndef __cpp_lib_barrier
>>+# error "Feature-test macro for barrier missing in <version>"
>>+#elif __cpp_lib_barrier != 201907L
>>+# error "Feature-test macro for barrier has wrong value in <version>"
>>+#endif
>>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>>new file mode 100644
>>index 00000000000..df70587db5b
>>--- /dev/null
>>+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
>>@@ -0,0 +1,48 @@
>>+// { dg-options "-std=gnu++2a" }
>>+// { dg-do run { target c++2a } }
>>+// { dg-require-gthreads "" }
>>+// { dg-additional-options "-pthread" { target pthread } }
>>+
>>+// 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
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
>
> Please replace "implementation" with "test" or something like that,
> since this isn't the implementation. Same for all the other tests
> taken from LLVM.
>
>>+//===----------------------------------------------------------------------===//
>>+//
>>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>>+// See https://llvm.org/LICENSE.txt for license information.
>>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>>+//
>>+//===----------------------------------------------------------------------===//
>>+
>>+#include <barrier>
>>+#include <thread>
>>+
>>+int main(int, char**)
>>+{
>>+  std::barrier<> b(2);
>>+
>>+  auto tok = b.arrive();
>>+  std::thread t([&](){
>>+    (void)b.arrive();
>>+  });
>>+  b.wait(std::move(tok));
>>+  t.join();
>>+
>>+  auto tok2 = b.arrive(2);
>>+  b.wait(std::move(tok2));
>>+}
>>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
>>new file mode 100644
>>index 00000000000..746a8f470e3
>>--- /dev/null
>>+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
>>@@ -0,0 +1,46 @@
>>+// { dg-options "-std=gnu++2a" }
>>+// { dg-do run { target c++2a } }
>>+// { dg-require-gthreads "" }
>>+// { dg-additional-options "-pthread" { target pthread } }
>>+
>>+// 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
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
>>+//===----------------------------------------------------------------------===//
>>+//
>>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>>+// See https://llvm.org/LICENSE.txt for license information.
>>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>>+//
>>+//===----------------------------------------------------------------------===//
>>+
>>+#include <barrier>
>>+#include <thread>
>>+
>>+int main(int, char**)
>>+{
>>+  std::barrier<> b(2);
>>+
>>+  std::thread t([&](){
>>+    b.arrive_and_drop();
>>+  });
>>+
>>+  b.arrive_and_wait();
>>+  b.arrive_and_wait();
>>+  t.join();
>>+}
>>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
>>new file mode 100644
>>index 00000000000..509986f3b78
>>--- /dev/null
>>+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
>>@@ -0,0 +1,46 @@
>>+// { dg-options "-std=gnu++2a" }
>>+// { dg-do run { target c++2a } }
>>+// { dg-require-gthreads "" }
>>+// { dg-additional-options "-pthread" { target pthread } }
>>+
>>+// 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
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
>
> Wrong filename?
>
>>+//===----------------------------------------------------------------------===//
>>+//
>>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>>+// See https://llvm.org/LICENSE.txt for license information.
>>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>>+//
>>+//===----------------------------------------------------------------------===//
>>+
>>+#include <barrier>
>>+#include <thread>
>>+
>>+int main(int, char**)
>>+{
>>+  std::barrier<> b(2);
>>+
>>+  std::thread t([&](){
>>+    for(int i = 0; i < 10; ++i)
>>+      b.arrive_and_wait();
>>+  });
>>+  for(int i = 0; i < 10; ++i)
>>+    b.arrive_and_wait();
>>+  t.join();
>>+}
>>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
>>new file mode 100644
>>index 00000000000..c7997d9d7fb
>>--- /dev/null
>>+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
>>@@ -0,0 +1,53 @@
>>+// { dg-options "-std=gnu++2a" }
>>+// { dg-do run { target c++2a } }
>>+// { dg-require-gthreads "" }
>>+// { dg-additional-options "-pthread" { target pthread } }
>>+
>>+// 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
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
>>+//===----------------------------------------------------------------------===//
>>+//
>>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>>+// See https://llvm.org/LICENSE.txt for license information.
>>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>>+//
>>+//===----------------------------------------------------------------------===//
>>+
>>+#include <barrier>
>>+#include <thread>
>>+
>>+#include <testsuite_hooks.h>
>>+
>>+int main(int, char**)
>>+{
>>+  int x = 0;
>>+  auto comp = [&] { x += 1; };
>>+  std::barrier<decltype(comp)> b(2, comp);
>>+
>>+  std::thread t([&](){
>>+      for(int i = 0; i < 10; ++i)
>>+	b.arrive_and_wait();
>>+  });
>>+
>>+  for(int i = 0; i < 10; ++i)
>>+    b.arrive_and_wait();
>>+
>>+  VERIFY( x == 10 );
>>+  t.join();
>>+}
>>diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
>>new file mode 100644
>>index 00000000000..278bc3fdd10
>>--- /dev/null
>>+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
>>@@ -0,0 +1,40 @@
>>+// { dg-options "-std=gnu++2a" }
>>+// { dg-do run { target c++2a } }
>
> This should be 'compile' not 'run'.
>
> I think I'd rather see these static_asserts put into one of the
> existing tests, so we don't pay for the overhead of another dejagnu
> test (with all its setup and target checking) just for this.
>
> Maybe in barrier/1.cc since that's not doing very much work either
> (and don't forget to move the dg-additional-options there too).
>
>>+// { dg-require-gthreads "" }
>>+// { dg-additional-options "-pthread" { target pthread } }
>>+
>>+// 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
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
>>+//===----------------------------------------------------------------------===//
>>+//
>>+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
>>+// See https://llvm.org/LICENSE.txt for license information.
>>+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
>>+//
>>+//===----------------------------------------------------------------------===//
>>+
>>+#include <barrier>
>>+#include <thread>
>>+
>>+int main(int, char**)
>>+{
>>+  static_assert(std::barrier<>::max() > 0, "");
>>+  auto l = [](){};
>>+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
>
> No need for the string literals here, this is C++20.
>
> OK with those changes, thanks.


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] libstdc++: Add support for C++20 barriers
  2021-01-07 20:56             ` Thomas Rodgers
@ 2021-01-14 14:26               ` Jonathan Wakely
  0 siblings, 0 replies; 23+ messages in thread
From: Jonathan Wakely @ 2021-01-14 14:26 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: Thomas Rodgers, gcc-patches, libstdc++

[-- Attachment #1: Type: text/plain, Size: 169 bytes --]

On 07/01/21 12:56 -0800, Thomas Rodgers via Libstdc++ wrote:
>
>Tested x86_64-pc-linux-gnu, committed to master.

The copyright years need updating. Pushed to master.



[-- Attachment #2: patch.txt --]
[-- Type: text/x-patch, Size: 6016 bytes --]

commit 194a9d67be45568d81bb8c17e9102e31c1309e5f
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Jan 14 14:25:05 2021

    libstdc++: Update copyright dates on new files
    
    The patch adding these files was approved in 2020 but it wasn't
    committed until 2021, so the copyright years were not updated along with
    the years in all the existing files.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/barrier: Update copyright years. Fix whitespace.
            * include/std/version: Fix whitespace.
            * testsuite/30_threads/barrier/1.cc: Update copyright years.
            * testsuite/30_threads/barrier/2.cc: Likewise.
            * testsuite/30_threads/barrier/arrive.cc: Likewise.
            * testsuite/30_threads/barrier/arrive_and_drop.cc: Likewise.
            * testsuite/30_threads/barrier/arrive_and_wait.cc: Likewise.
            * testsuite/30_threads/barrier/completion.cc: Likewise.

diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
index f1143da89b4..e09212dfcb9 100644
--- a/libstdc++-v3/include/std/barrier
+++ b/libstdc++-v3/include/std/barrier
@@ -1,6 +1,6 @@
 // <barrier> -*- C++ -*-
 
-// Copyright (C) 2020 Free Software Foundation, Inc.
+// Copyright (C) 2020-2021 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
@@ -103,7 +103,7 @@ It looks different from literature pseudocode for two main reasons:
 			   static_cast<__barrier_phase_t>(__old_phase_val + 2);
 
 	size_t __current_expected = _M_expected;
-	std::hash<std::thread::id>__hasher;
+	std::hash<std::thread::id> __hasher;
 	size_t __current = __hasher(std::this_thread::get_id())
 					  % ((_M_expected + 1) >> 1);
 
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 9516558d8b4..e3d52b88c21 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -200,8 +200,8 @@
 #if defined _GLIBCXX_HAS_GTHREADS || defined _GLIBCXX_HAVE_LINUX_FUTEX
 # define __cpp_lib_atomic_wait 201907L
 # if __cpp_aligned_new
-# define __cpp_lib_barrier 201907L
-#endif
+#  define __cpp_lib_barrier 201907L
+# endif
 #endif
 #define __cpp_lib_bind_front 201907L
 #if __has_builtin(__builtin_bit_cast)
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
index 4c15deb1398..a21fae32127 100644
--- a/libstdc++-v3/testsuite/30_threads/barrier/1.cc
+++ b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2020 Free Software Foundation, Inc.
+// Copyright (C) 2020-2021 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
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
index 0fac1ef3f3c..94e37d739da 100644
--- a/libstdc++-v3/testsuite/30_threads/barrier/2.cc
+++ b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
@@ -1,4 +1,4 @@
-// Copyright (C) 2019-2020 Free Software Foundation, Inc.
+// Copyright (C) 2020-2021 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
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
index 6e64e378cb0..fb0f56292c0 100644
--- a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -3,7 +3,7 @@
 // { dg-require-gthreads "" }
 // { dg-additional-options "-pthread" { target pthread } }
 
-// Copyright (C) 2020 Free Software Foundation, Inc.
+// Copyright (C) 2020-2021 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
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
index 55f40e17062..22b40200c80 100644
--- a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -3,7 +3,7 @@
 // { dg-require-gthreads "" }
 // { dg-additional-options "-pthread" { target pthread } }
 
-// Copyright (C) 2020 Free Software Foundation, Inc.
+// Copyright (C) 2020-2021 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
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
index 2a3a69ae3c0..f9b4fa29063 100644
--- a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -3,7 +3,7 @@
 // { dg-require-gthreads "" }
 // { dg-additional-options "-pthread" { target pthread } }
 
-// Copyright (C) 2020 Free Software Foundation, Inc.
+// Copyright (C) 2020-2021 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
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
index ef6d2c3eac6..27be0844a60 100644
--- a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -3,7 +3,7 @@
 // { dg-require-gthreads "" }
 // { dg-additional-options "-pthread" { target pthread } }
 
-// Copyright (C) 2020 Free Software Foundation, Inc.
+// Copyright (C) 2020-2021 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

^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] Add support for C++20 barriers
@ 2020-06-06 21:25 Thomas Rodgers
  0 siblings, 0 replies; 23+ messages in thread
From: Thomas Rodgers @ 2020-06-06 21:25 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers, Thomas Rodgers

	* include/Makefile.am (std_headers): Add new header.
	* include/Makefile.in: Regenerate.
	* include/std/barrier: New file.
	* testsuite/30_thread/barrier/1.cc: New test.
	* testsuite/30_thread/barrier/2.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
	* testsuite/30_thread/barrier/arrive.cc: Likewise.
	* testsuite/30_thread/barrier/completion.cc: Likewise.
	* testsuite/30_thread/barrier/max.cc: Likewise.
---
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 libstdc++-v3/include/bits/atomic_base.h       |  15 +-
 libstdc++-v3/include/std/barrier              | 230 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   5 +-
 .../testsuite/30_threads/barrier/1.cc         |  27 ++
 .../testsuite/30_threads/barrier/2.cc         |  27 ++
 .../testsuite/30_threads/barrier/arrive.cc    |  34 +++
 .../30_threads/barrier/arrive_and_drop.cc     |  32 +++
 .../30_threads/barrier/arrive_and_wait.cc     |  33 +++
 .../30_threads/barrier/completion.cc          |  39 +++
 .../testsuite/30_threads/barrier/max.cc       |  26 ++
 12 files changed, 465 insertions(+), 5 deletions(-)
 create mode 100644 libstdc++-v3/include/std/barrier
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index b3ac1a3365f..e2cded53779 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -30,6 +30,7 @@ std_headers = \
 	${std_srcdir}/any \
 	${std_srcdir}/array \
 	${std_srcdir}/atomic \
+	${std_srcdir}/barrier \
 	${std_srcdir}/bit \
 	${std_srcdir}/bitset \
 	${std_srcdir}/charconv \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index e73ff8b3e64..8e1163e8e18 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -375,6 +375,7 @@ std_headers = \
 	${std_srcdir}/any \
 	${std_srcdir}/array \
 	${std_srcdir}/atomic \
+	${std_srcdir}/barrier \
 	${std_srcdir}/bit \
 	${std_srcdir}/bitset \
 	${std_srcdir}/charconv \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 68d9e7e3756..271656c6b37 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -566,13 +566,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
 #if __cplusplus > 201703L
+      template<typename _Pred>
+	_GLIBCXX_ALWAYS_INLINE void
+	_M_wait(__int_type __old, _Pred&& __pred,
+		memory_order __m) const noexcept
+	{
+	  std::__atomic_wait(&_M_i, __old,
+			     std::forward<_Pred&&>(__pred));
+	}
+
       _GLIBCXX_ALWAYS_INLINE void
       wait(__int_type __old,
 	  memory_order __m = memory_order_seq_cst) const noexcept
       {
-	std::__atomic_wait(&_M_i, __old,
-			   [__m, this, __old]()
-			   { return this->load(__m) != __old; });
+	_M_wait(__old, [__m, this, __old]()
+		{ return this->load(__m) != __old; },
+		__m);
       }
 
       // TODO add const volatile overload
diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
new file mode 100644
index 00000000000..870db7db2ac
--- /dev/null
+++ b/libstdc++-v3/include/std/barrier
@@ -0,0 +1,230 @@
+// <barrier> -*- C++ -*-
+// This implementation is based on libcxx/include/barrier
+//===-- barrier.h --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+#ifndef _GLIBCXX_BARRIER
+#define _GLIBCXX_BARRIER 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_barrier 201907L
+
+#include <bits/c++config.h>
+
+#if defined(_GLIBCXX_HAS_GTHREADS)
+#include <bits/atomic_base.h>
+#include <bits/atomic_wait.h>
+#include <bits/functional_hash.h>
+#include <ext/numeric_traits.h>
+
+#include <memory>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  struct __empty_completion
+  {
+    _GLIBCXX_ALWAYS_INLINE void
+    operator()() noexcept
+    { }
+  };
+
+/*
+
+The default implementation of __barrier_base is a classic tree barrier.
+
+It looks different from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+  using __barrier_phase_t = uint8_t;
+
+  template<class _CompletionF>
+    class __barrier_base
+    {
+      struct alignas(64) /* naturally-align the heap state */ __state_t
+      {
+	struct
+	{
+	  __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
+	} __tickets[64];
+      };
+
+      ptrdiff_t _M_expected;
+      unique_ptr<char[]> _M_state_allocation;
+      __state_t* 		_M_state;
+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
+      _CompletionF _M_completion;
+      __atomic_base<__barrier_phase_t> _M_phase;
+
+      static __gthread_t
+      _S_get_tid() noexcept
+      {
+#ifdef __GLIBC__
+	// For the GNU C library pthread_self() is usable without linking to
+	// libpthread.so but returns 0, so we cannot use it in single-threaded
+	// programs, because this_thread::get_id() != thread::id{} must be true.
+	// We know that pthread_t is an integral type in the GNU C library.
+	if (!__gthread_active_p())
+	  return 1;
+#endif
+	return __gthread_self();
+      }
+
+      bool
+      _M_arrive(__barrier_phase_t __old_phase)
+      {
+	__barrier_phase_t const __half_step = __old_phase + 1,
+				__full_step = __old_phase + 2;
+	size_t __current_expected = _M_expected;
+	size_t __current = _Hash_impl::hash(_S_get_tid())
+				% ((_M_expected + 1) >> 1);
+	for (int __round = 0;; ++__round)
+	  {
+	    if (__current_expected <= 1)
+		return true;
+	    size_t const __end_node = ((__current_expected + 1) >> 1),
+			 __last_node = __end_node - 1;
+	    for (;;++__current)
+	      {
+		if (__current == __end_node)
+		  __current = 0;
+		__barrier_phase_t __expect = __old_phase;
+		if (__current == __last_node && (__current_expected & 1))
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;     // I'm 1 in 1, go to next __round
+		  }
+		else if (_M_state[__current].__tickets[__round].__phase
+			 .compare_exchange_strong(__expect, __half_step,
+						  memory_order_acq_rel))
+		  {
+		    return false; // I'm 1 in 2, done with arrival
+		  }
+		else if (__expect == __half_step)
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;    // I'm 2 in 2, go to next __round
+		  }
+	      }
+	    __current_expected = __last_node + 1;
+	    __current >>= 1;
+	  }
+      }
+
+    public:
+      using arrival_token = __barrier_phase_t;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
+
+      __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
+	  : _M_expected(__expected), _M_expected_adjustment(0),
+	    _M_completion(move(__completion)), _M_phase(0)
+      {
+	size_t const __count = (_M_expected + 1) >> 1;
+	size_t const __size = sizeof(__state_t) * __count;
+	size_t __allocation_size = __size + alignof(__state_t);
+	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
+	void* __allocation = _M_state_allocation.get();
+	void* const __state = align(alignof(__state_t), __size, __allocation,
+				      __allocation_size);
+	_M_state = new (__state) __state_t[__count];
+      }
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update)
+      {
+	auto const __old_phase = _M_phase.load(memory_order_relaxed);
+	for(; __update; --__update)
+	  {
+	    if(_M_arrive(__old_phase))
+	      {
+		_M_completion();
+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
+		_M_expected_adjustment.store(0, memory_order_relaxed);
+		_M_phase.store(__old_phase + 2, memory_order_release);
+		_M_phase.notify_all();
+	      }
+	  }
+	return __old_phase;
+      }
+
+      void
+      wait(arrival_token&& __old_phase) const
+      {
+	auto const __test_fn = [=, this]
+	  {
+	    return _M_phase.load(memory_order_acquire) != __old_phase;
+	  };
+	_M_phase._M_wait(__old_phase, __test_fn, memory_order_acquire);
+      }
+
+      void
+      arrive_and_drop()
+      {
+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
+	(void)arrive(1);
+      }
+    };
+
+  template<class _CompletionF = __empty_completion>
+    class barrier
+    {
+      __barrier_base<_CompletionF> _M_b;
+    public:
+      using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __barrier_base<_CompletionF>::max(); }
+
+      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
+	  : _M_b(__count, std::move(__completion))
+      { }
+
+      barrier(barrier const&) = delete;
+      barrier& operator=(barrier const&) = delete;
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update = 1)
+      { return _M_b.arrive(__update); }
+
+      void
+      wait(arrival_token&& __phase) const
+      { _M_b.wait(std::move(__phase)); }
+
+      void
+      arrive_and_wait()
+      { wait(arrive()); }
+
+      void
+      arrive_and_drop()
+      { _M_b.arrive_and_drop(); }
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // _GLIBCXX_HAS_GTHREADS
+#endif // __cplusplus > 201703L
+#endif // _GLIBCXX_BARRIER
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index f09da3344f7..9e1ecb9e010 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -189,8 +189,6 @@
 #endif
 #define __cpp_lib_type_identity 201806L
 #define __cpp_lib_unwrap_ref 201811L
-#define __cpp_lib_semaphore 201907L
-#define __cpp_lib_latch 201907L
 
 #if _GLIBCXX_HOSTED
 #undef __cpp_lib_array_constexpr
@@ -213,6 +211,9 @@
 #define __cpp_lib_interpolate 201902L
 #ifdef _GLIBCXX_HAS_GTHREADS
 # define __cpp_lib_jthread 201911L
+#define __cpp_lib_semaphore 201907L
+#define __cpp_lib_latch 201907L
+#define __cpp_lib_barrier 201907L
 #endif
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
new file mode 100644
index 00000000000..0b38160a58b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <barrier>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <barrier>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <barrier>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
new file mode 100644
index 00000000000..1d8d83639e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <version>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
new file mode 100644
index 00000000000..9e0cf0fc00f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -0,0 +1,34 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
new file mode 100644
index 00000000000..6a1bc6afaa3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -0,0 +1,32 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
new file mode 100644
index 00000000000..eee5ae053d6
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -0,0 +1,33 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
new file mode 100644
index 00000000000..d075f55cbe7
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -0,0 +1,39 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+#include <iostream>
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&] { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+        b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  VERIFY( x == 10 );
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
new file mode 100644
index 00000000000..c8efe549873
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
@@ -0,0 +1,26 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+  return 0;
+}
-- 
2.26.2


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] Add support for C++20 barriers
  2020-05-24 18:11     ` Jonathan Wakely
  2020-05-24 18:15       ` Florian Weimer
@ 2020-05-24 21:37       ` Thomas Rodgers
  1 sibling, 0 replies; 23+ messages in thread
From: Thomas Rodgers @ 2020-05-24 21:37 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Florian Weimer, Thomas Rodgers, libstdc++, gcc-patches



> On May 24, 2020, at 11:11 AM, Jonathan Wakely <jwakely.gcc@gmail.com> wrote:
> 
> On Sun, 24 May 2020 at 18:55, Florian Weimer wrote:
>> 
>> * Thomas Rodgers:
>> 
>>> +      static __gthread_t
>>> +      _S_get_tid() noexcept
>>> +      {
>>> +#ifdef __GLIBC__
>>> +     // For the GNU C library pthread_self() is usable without linking to
>>> +     // libpthread.so but returns 0, so we cannot use it in single-threaded
>>> +     // programs, because this_thread::get_id() != thread::id{} must be true.
>>> +     // We know that pthread_t is an integral type in the GNU C library.
>>> +     if (!__gthread_active_p())
>>> +       return 1;
>>> +#endif
>>> +     return __gthread_self();
>>> +      }
>> 
>> This comment seems outdated or incomplete.  pthread_self returns a
>> proper pointer since glibc 2.27, I believe.
> 
> The comment is copied from the <thread> header, and dates from 2015.

Yes, this comes from <thread> to avoid pulling in all of <thread> to just get a hash from the current thread identity. I’m now using it in two places, is this worth splitting out somewhere?

> 
>> I'm also not sure how the difference is observable for the libstdc++
>> implementation.  Late loading of libpthread isn't quite supported.
> 
> It's nothing to do with late loading. A single threaded program that
> doesn't create any threads and doesn't link to libpthread can still
> expect std::this_thread::get_id() != std::thread::id() to be true in
> the main (and only) thread. If pthread_self() returns 0, and
> thread::id() default constructs with a value of 0, then we can't
> distinguish "the main thread" from "not a thread".
> 
> But I do see a non-zero value from glibc now, which is great. I'll add
> it to my TODO list to remove that workaround from <thread>.


^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] Add support for C++20 barriers
  2020-05-24 18:11     ` Jonathan Wakely
@ 2020-05-24 18:15       ` Florian Weimer
  2020-05-24 21:37       ` Thomas Rodgers
  1 sibling, 0 replies; 23+ messages in thread
From: Florian Weimer @ 2020-05-24 18:15 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Thomas Rodgers, Thomas Rodgers, libstdc++, gcc-patches

* Jonathan Wakely:

> On Sun, 24 May 2020 at 18:55, Florian Weimer wrote:
>>
>> * Thomas Rodgers:
>>
>> > +      static __gthread_t
>> > +      _S_get_tid() noexcept
>> > +      {
>> > +#ifdef __GLIBC__
>> > +     // For the GNU C library pthread_self() is usable without linking to
>> > +     // libpthread.so but returns 0, so we cannot use it in single-threaded
>> > +     // programs, because this_thread::get_id() != thread::id{} must be true.
>> > +     // We know that pthread_t is an integral type in the GNU C library.
>> > +     if (!__gthread_active_p())
>> > +       return 1;
>> > +#endif
>> > +     return __gthread_self();
>> > +      }
>>
>> This comment seems outdated or incomplete.  pthread_self returns a
>> proper pointer since glibc 2.27, I believe.
>
> The comment is copied from the <thread> header, and dates from 2015.
>
>> I'm also not sure how the difference is observable for the libstdc++
>> implementation.  Late loading of libpthread isn't quite supported.
>
> It's nothing to do with late loading. A single threaded program that
> doesn't create any threads and doesn't link to libpthread can still
> expect std::this_thread::get_id() != std::thread::id() to be true in
> the main (and only) thread. If pthread_self() returns 0, and
> thread::id() default constructs with a value of 0, then we can't
> distinguish "the main thread" from "not a thread".

Ahh.  Yes, the POSIX interface does not have any “not a thread” value
for pthread_t, so I can see how it's difficult to implement something
on top of it htat meets the C++ requirements.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] Add support for C++20 barriers
  2020-05-24 17:55   ` Florian Weimer
@ 2020-05-24 18:11     ` Jonathan Wakely
  2020-05-24 18:15       ` Florian Weimer
  2020-05-24 21:37       ` Thomas Rodgers
  0 siblings, 2 replies; 23+ messages in thread
From: Jonathan Wakely @ 2020-05-24 18:11 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Thomas Rodgers, Thomas Rodgers, libstdc++, gcc-patches

On Sun, 24 May 2020 at 18:55, Florian Weimer wrote:
>
> * Thomas Rodgers:
>
> > +      static __gthread_t
> > +      _S_get_tid() noexcept
> > +      {
> > +#ifdef __GLIBC__
> > +     // For the GNU C library pthread_self() is usable without linking to
> > +     // libpthread.so but returns 0, so we cannot use it in single-threaded
> > +     // programs, because this_thread::get_id() != thread::id{} must be true.
> > +     // We know that pthread_t is an integral type in the GNU C library.
> > +     if (!__gthread_active_p())
> > +       return 1;
> > +#endif
> > +     return __gthread_self();
> > +      }
>
> This comment seems outdated or incomplete.  pthread_self returns a
> proper pointer since glibc 2.27, I believe.

The comment is copied from the <thread> header, and dates from 2015.

> I'm also not sure how the difference is observable for the libstdc++
> implementation.  Late loading of libpthread isn't quite supported.

It's nothing to do with late loading. A single threaded program that
doesn't create any threads and doesn't link to libpthread can still
expect std::this_thread::get_id() != std::thread::id() to be true in
the main (and only) thread. If pthread_self() returns 0, and
thread::id() default constructs with a value of 0, then we can't
distinguish "the main thread" from "not a thread".

But I do see a non-zero value from glibc now, which is great. I'll add
it to my TODO list to remove that workaround from <thread>.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] Add support for C++20 barriers
  2020-05-24 17:42 ` Thomas Rodgers
@ 2020-05-24 17:55   ` Florian Weimer
  2020-05-24 18:11     ` Jonathan Wakely
  0 siblings, 1 reply; 23+ messages in thread
From: Florian Weimer @ 2020-05-24 17:55 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: libstdc++, gcc-patches, Thomas Rodgers

* Thomas Rodgers:

> +      static __gthread_t
> +      _S_get_tid() noexcept
> +      {
> +#ifdef __GLIBC__
> +	// For the GNU C library pthread_self() is usable without linking to
> +	// libpthread.so but returns 0, so we cannot use it in single-threaded
> +	// programs, because this_thread::get_id() != thread::id{} must be true.
> +	// We know that pthread_t is an integral type in the GNU C library.
> +	if (!__gthread_active_p())
> +	  return 1;
> +#endif
> +	return __gthread_self();
> +      }

This comment seems outdated or incomplete.  pthread_self returns a
proper pointer since glibc 2.27, I believe.

I'm also not sure how the difference is observable for the libstdc++
implementation.  Late loading of libpthread isn't quite supported.

^ permalink raw reply	[flat|nested] 23+ messages in thread

* Re: [PATCH] Add support for C++20 barriers
  2020-05-23 22:58 Thomas Rodgers
@ 2020-05-24 17:42 ` Thomas Rodgers
  2020-05-24 17:55   ` Florian Weimer
  0 siblings, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2020-05-24 17:42 UTC (permalink / raw)
  To: libstdc++, gcc-patches; +Cc: Thomas Rodgers

[-- Attachment #1: Type: text/plain, Size: 37 bytes --]

This time with 100% more patch…


[-- Attachment #2: 0001-Add-support-for-C-20-barriers_f.patch --]
[-- Type: application/octet-stream, Size: 20463 bytes --]

From 073bca6493b4800de7b9b5ed9203655b4ffdca69 Mon Sep 17 00:00:00 2001
From: Thomas Rodgers <rodgert@appliantology.com>
Date: Sat, 23 May 2020 15:36:33 -0700
Subject: [PATCH] Add support for C++20 barriers

	* include/Makefile.am (std_headers): Add new header.
	* include/Makefile.in: Regenerate.
	* include/std/barrier: New file.
	* testsuite/30_thread/barrier/1.cc: New test.
	* testsuite/30_thread/barrier/2.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
	* testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
	* testsuite/30_thread/barrier/arrive.cc: Likewise.
	* testsuite/30_thread/barrier/completion.cc: Likewise.
	* testsuite/30_thread/barrier/max.cc: Likewise.
---
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 libstdc++-v3/include/bits/atomic_base.h       |  15 +-
 libstdc++-v3/include/std/barrier              | 230 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   5 +-
 .../testsuite/30_threads/barrier/1.cc         |  27 ++
 .../testsuite/30_threads/barrier/2.cc         |  27 ++
 .../testsuite/30_threads/barrier/arrive.cc    |  34 +++
 .../30_threads/barrier/arrive_and_drop.cc     |  32 +++
 .../30_threads/barrier/arrive_and_wait.cc     |  33 +++
 .../30_threads/barrier/completion.cc          |  39 +++
 .../testsuite/30_threads/barrier/max.cc       |  26 ++
 12 files changed, 465 insertions(+), 5 deletions(-)
 create mode 100644 libstdc++-v3/include/std/barrier
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/1.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/2.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/completion.cc
 create mode 100644 libstdc++-v3/testsuite/30_threads/barrier/max.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index b3ac1a3365f..e2cded53779 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -30,6 +30,7 @@ std_headers = \
 	${std_srcdir}/any \
 	${std_srcdir}/array \
 	${std_srcdir}/atomic \
+	${std_srcdir}/barrier \
 	${std_srcdir}/bit \
 	${std_srcdir}/bitset \
 	${std_srcdir}/charconv \
diff --git a/libstdc++-v3/include/bits/atomic_base.h b/libstdc++-v3/include/bits/atomic_base.h
index 68d9e7e3756..271656c6b37 100644
--- a/libstdc++-v3/include/bits/atomic_base.h
+++ b/libstdc++-v3/include/bits/atomic_base.h
@@ -566,13 +566,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
 #if __cplusplus > 201703L
+      template<typename _Pred>
+	_GLIBCXX_ALWAYS_INLINE void
+	_M_wait(__int_type __old, _Pred&& __pred,
+		memory_order __m) const noexcept
+	{
+	  std::__atomic_wait(&_M_i, __old,
+			     std::forward<_Pred&&>(__pred));
+	}
+
       _GLIBCXX_ALWAYS_INLINE void
       wait(__int_type __old,
 	  memory_order __m = memory_order_seq_cst) const noexcept
       {
-	std::__atomic_wait(&_M_i, __old,
-			   [__m, this, __old]()
-			   { return this->load(__m) != __old; });
+	_M_wait(__old, [__m, this, __old]()
+		{ return this->load(__m) != __old; },
+		__m);
       }
 
       // TODO add const volatile overload
diff --git a/libstdc++-v3/include/std/barrier b/libstdc++-v3/include/std/barrier
new file mode 100644
index 00000000000..870db7db2ac
--- /dev/null
+++ b/libstdc++-v3/include/std/barrier
@@ -0,0 +1,230 @@
+// <barrier> -*- C++ -*-
+// This implementation is based on libcxx/include/barrier
+//===-- barrier.h --------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===---------------------------------------------------------------===//
+
+#ifndef _GLIBCXX_BARRIER
+#define _GLIBCXX_BARRIER 1
+
+#pragma GCC system_header
+
+#if __cplusplus > 201703L
+#define __cpp_lib_barrier 201907L
+
+#include <bits/c++config.h>
+
+#if defined(_GLIBCXX_HAS_GTHREADS)
+#include <bits/atomic_base.h>
+#include <bits/atomic_wait.h>
+#include <bits/functional_hash.h>
+#include <ext/numeric_traits.h>
+
+#include <memory>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  struct __empty_completion
+  {
+    _GLIBCXX_ALWAYS_INLINE void
+    operator()() noexcept
+    { }
+  };
+
+/*
+
+The default implementation of __barrier_base is a classic tree barrier.
+
+It looks different from literature pseudocode for two main reasons:
+ 1. Threads that call into std::barrier functions do not provide indices,
+    so a numbering step is added before the actual barrier algorithm,
+    appearing as an N+1 round to the N rounds of the tree barrier.
+ 2. A great deal of attention has been paid to avoid cache line thrashing
+    by flattening the tree structure into cache-line sized arrays, that
+    are indexed in an efficient way.
+
+*/
+
+  using __barrier_phase_t = uint8_t;
+
+  template<class _CompletionF>
+    class __barrier_base
+    {
+      struct alignas(64) /* naturally-align the heap state */ __state_t
+      {
+	struct
+	{
+	  __atomic_base<__barrier_phase_t> __phase = ATOMIC_VAR_INIT(0);
+	} __tickets[64];
+      };
+
+      ptrdiff_t _M_expected;
+      unique_ptr<char[]> _M_state_allocation;
+      __state_t* 		_M_state;
+      __atomic_base<ptrdiff_t> _M_expected_adjustment;
+      _CompletionF _M_completion;
+      __atomic_base<__barrier_phase_t> _M_phase;
+
+      static __gthread_t
+      _S_get_tid() noexcept
+      {
+#ifdef __GLIBC__
+	// For the GNU C library pthread_self() is usable without linking to
+	// libpthread.so but returns 0, so we cannot use it in single-threaded
+	// programs, because this_thread::get_id() != thread::id{} must be true.
+	// We know that pthread_t is an integral type in the GNU C library.
+	if (!__gthread_active_p())
+	  return 1;
+#endif
+	return __gthread_self();
+      }
+
+      bool
+      _M_arrive(__barrier_phase_t __old_phase)
+      {
+	__barrier_phase_t const __half_step = __old_phase + 1,
+				__full_step = __old_phase + 2;
+	size_t __current_expected = _M_expected;
+	size_t __current = _Hash_impl::hash(_S_get_tid())
+				% ((_M_expected + 1) >> 1);
+	for (int __round = 0;; ++__round)
+	  {
+	    if (__current_expected <= 1)
+		return true;
+	    size_t const __end_node = ((__current_expected + 1) >> 1),
+			 __last_node = __end_node - 1;
+	    for (;;++__current)
+	      {
+		if (__current == __end_node)
+		  __current = 0;
+		__barrier_phase_t __expect = __old_phase;
+		if (__current == __last_node && (__current_expected & 1))
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;     // I'm 1 in 1, go to next __round
+		  }
+		else if (_M_state[__current].__tickets[__round].__phase
+			 .compare_exchange_strong(__expect, __half_step,
+						  memory_order_acq_rel))
+		  {
+		    return false; // I'm 1 in 2, done with arrival
+		  }
+		else if (__expect == __half_step)
+		  {
+		    if (_M_state[__current].__tickets[__round].__phase
+			.compare_exchange_strong(__expect, __full_step,
+						 memory_order_acq_rel))
+		      break;    // I'm 2 in 2, go to next __round
+		  }
+	      }
+	    __current_expected = __last_node + 1;
+	    __current >>= 1;
+	  }
+      }
+
+    public:
+      using arrival_token = __barrier_phase_t;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __gnu_cxx::__numeric_traits<ptrdiff_t>::__max; }
+
+      __barrier_base(ptrdiff_t __expected, _CompletionF __completion = _CompletionF())
+	  : _M_expected(__expected), _M_expected_adjustment(0),
+	    _M_completion(move(__completion)), _M_phase(0)
+      {
+	size_t const __count = (_M_expected + 1) >> 1;
+	size_t const __size = sizeof(__state_t) * __count;
+	size_t __allocation_size = __size + alignof(__state_t);
+	_M_state_allocation = unique_ptr<char[]>(new char[__allocation_size]);
+	void* __allocation = _M_state_allocation.get();
+	void* const __state = align(alignof(__state_t), __size, __allocation,
+				      __allocation_size);
+	_M_state = new (__state) __state_t[__count];
+      }
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update)
+      {
+	auto const __old_phase = _M_phase.load(memory_order_relaxed);
+	for(; __update; --__update)
+	  {
+	    if(_M_arrive(__old_phase))
+	      {
+		_M_completion();
+		_M_expected += _M_expected_adjustment.load(memory_order_relaxed);
+		_M_expected_adjustment.store(0, memory_order_relaxed);
+		_M_phase.store(__old_phase + 2, memory_order_release);
+		_M_phase.notify_all();
+	      }
+	  }
+	return __old_phase;
+      }
+
+      void
+      wait(arrival_token&& __old_phase) const
+      {
+	auto const __test_fn = [=, this]
+	  {
+	    return _M_phase.load(memory_order_acquire) != __old_phase;
+	  };
+	_M_phase._M_wait(__old_phase, __test_fn, memory_order_acquire);
+      }
+
+      void
+      arrive_and_drop()
+      {
+	_M_expected_adjustment.fetch_sub(1, memory_order_relaxed);
+	(void)arrive(1);
+      }
+    };
+
+  template<class _CompletionF = __empty_completion>
+    class barrier
+    {
+      __barrier_base<_CompletionF> _M_b;
+    public:
+      using arrival_token = typename __barrier_base<_CompletionF>::arrival_token;
+
+      static constexpr ptrdiff_t
+      max() noexcept
+      { return __barrier_base<_CompletionF>::max(); }
+
+      barrier(ptrdiff_t __count, _CompletionF __completion = _CompletionF())
+	  : _M_b(__count, std::move(__completion))
+      { }
+
+      barrier(barrier const&) = delete;
+      barrier& operator=(barrier const&) = delete;
+
+      [[nodiscard]] arrival_token
+      arrive(ptrdiff_t __update = 1)
+      { return _M_b.arrive(__update); }
+
+      void
+      wait(arrival_token&& __phase) const
+      { _M_b.wait(std::move(__phase)); }
+
+      void
+      arrive_and_wait()
+      { wait(arrive()); }
+
+      void
+      arrive_and_drop()
+      { _M_b.arrive_and_drop(); }
+    };
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+#endif // _GLIBCXX_HAS_GTHREADS
+#endif // __cplusplus > 201703L
+#endif // _GLIBCXX_BARRIER
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index f09da3344f7..9e1ecb9e010 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -189,8 +189,6 @@
 #endif
 #define __cpp_lib_type_identity 201806L
 #define __cpp_lib_unwrap_ref 201811L
-#define __cpp_lib_semaphore 201907L
-#define __cpp_lib_latch 201907L
 
 #if _GLIBCXX_HOSTED
 #undef __cpp_lib_array_constexpr
@@ -213,6 +211,9 @@
 #define __cpp_lib_interpolate 201902L
 #ifdef _GLIBCXX_HAS_GTHREADS
 # define __cpp_lib_jthread 201911L
+#define __cpp_lib_semaphore 201907L
+#define __cpp_lib_latch 201907L
+#define __cpp_lib_barrier 201907L
 #endif
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc
new file mode 100644
index 00000000000..0b38160a58b
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <barrier>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <barrier>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <barrier>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc
new file mode 100644
index 00000000000..1d8d83639e0
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/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
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <version>
+
+#ifndef __cpp_lib_barrier
+# error "Feature-test macro for barrier missing in <version>"
+#elif __cpp_lib_barrier != 201907L
+# error "Feature-test macro for barrier has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
new file mode 100644
index 00000000000..9e0cf0fc00f
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive.cc
@@ -0,0 +1,34 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  auto tok = b.arrive();
+  std::thread t([&](){
+    (void)b.arrive();
+  });
+  b.wait(std::move(tok));
+  t.join();
+
+  auto tok2 = b.arrive(2);
+  b.wait(std::move(tok2));
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
new file mode 100644
index 00000000000..6a1bc6afaa3
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_drop.cc
@@ -0,0 +1,32 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    b.arrive_and_drop();
+  });
+
+  b.arrive_and_wait();
+  b.arrive_and_wait();
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
new file mode 100644
index 00000000000..eee5ae053d6
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/arrive_and_wait.cc
@@ -0,0 +1,33 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/arrive_and_drop.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  std::barrier<> b(2);
+
+  std::thread t([&](){
+    for(int i = 0; i < 10; ++i)
+      b.arrive_and_wait();
+  });
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+  t.join();
+
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/completion.cc b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
new file mode 100644
index 00000000000..d075f55cbe7
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/completion.cc
@@ -0,0 +1,39 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/completion.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+#include <iostream>
+
+int main(int, char**)
+{
+  int x = 0;
+  auto comp = [&] { x += 1; };
+  std::barrier<decltype(comp)> b(2, comp);
+
+  std::thread t([&](){
+      for(int i = 0; i < 10; ++i)
+        b.arrive_and_wait();
+  });
+
+  for(int i = 0; i < 10; ++i)
+    b.arrive_and_wait();
+
+  VERIFY( x == 10 );
+  t.join();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/30_threads/barrier/max.cc b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
new file mode 100644
index 00000000000..c8efe549873
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/barrier/max.cc
@@ -0,0 +1,26 @@
+// This implementation is based on libcxx/test/std/thread/thread.barrier/max.pass.cpp
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// { dg-options "-std=gnu++2a -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-gthreads "" }
+
+#include <barrier>
+#include <thread>
+
+#include <testsuite_hooks.h>
+
+int main(int, char**)
+{
+  static_assert(std::barrier<>::max() > 0, "");
+  auto l = [](){};
+  static_assert(std::barrier<decltype(l)>::max() > 0, "");
+  return 0;
+}
-- 
2.26.2


[-- Attachment #3: Type: text/plain, Size: 963 bytes --]



> On May 23, 2020, at 3:58 PM, Thomas Rodgers <rodgert@appliantology.com> wrote:
> 
> This patch requires the patch for atomic<T>::wait/notify to be applied first.
> 
> This implementation is based on the libc++ implementation, but excludes the alternative “central barrier” implementation for now as there is no standard way to switch between the two.
> 
>        * include/Makefile.am (std_headers): Add new header.
>        * include/Makefile.in: Regenerate.
>        * include/std/barrier: New file.
>        * testsuite/30_thread/barrier/1.cc: New test.
>        * testsuite/30_thread/barrier/2.cc: Likewise.
>        * testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
>        * testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
>        * testsuite/30_thread/barrier/arrive.cc: Likewise.
>        * testsuite/30_thread/barrier/completion.cc: Likewise.
>        * testsuite/30_thread/barrier/max.cc: Likewise.
> 


^ permalink raw reply	[flat|nested] 23+ messages in thread

* [PATCH] Add support for C++20 barriers
@ 2020-05-23 22:58 Thomas Rodgers
  2020-05-24 17:42 ` Thomas Rodgers
  0 siblings, 1 reply; 23+ messages in thread
From: Thomas Rodgers @ 2020-05-23 22:58 UTC (permalink / raw)
  To: libstdc++, gcc-patches; +Cc: Thomas Rodgers

This patch requires the patch for atomic<T>::wait/notify to be applied first.

This implementation is based on the libc++ implementation, but excludes the alternative “central barrier” implementation for now as there is no standard way to switch between the two.

        * include/Makefile.am (std_headers): Add new header.
        * include/Makefile.in: Regenerate.
        * include/std/barrier: New file.
        * testsuite/30_thread/barrier/1.cc: New test.
        * testsuite/30_thread/barrier/2.cc: Likewise.
        * testsuite/30_thread/barrier/arrive_and_drop.cc: Likewise.
        * testsuite/30_thread/barrier/arrive_and_wait.cc: Likewise.
        * testsuite/30_thread/barrier/arrive.cc: Likewise.
        * testsuite/30_thread/barrier/completion.cc: Likewise.
        * testsuite/30_thread/barrier/max.cc: Likewise.


^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2021-01-14 14:26 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-07 23:34 [PATCH] Add support for C++20 barriers Thomas Rodgers
2020-11-04 17:29 ` [PATCH] libstdc++: " Thomas Rodgers
2020-11-04 18:41   ` Thomas Rodgers
2020-11-04 18:52     ` Jonathan Wakely
2020-11-04 18:55       ` Thomas Rodgers
2020-11-12 17:27         ` Jonathan Wakely
2020-11-04 18:50   ` Jonathan Wakely
2020-11-04 19:43     ` Thomas Rodgers
2020-11-21  0:30   ` Thomas Rodgers
2020-11-27 17:46     ` Jonathan Wakely
2020-12-17 14:06       ` Jonathan Wakely
2020-12-17 19:11       ` Thomas Rodgers
2020-12-17 23:37         ` Thomas Rodgers
2020-12-18 10:08           ` Jonathan Wakely
2021-01-07 20:56             ` Thomas Rodgers
2021-01-14 14:26               ` Jonathan Wakely
  -- strict thread matches above, loose matches on Subject: below --
2020-06-06 21:25 [PATCH] " Thomas Rodgers
2020-05-23 22:58 Thomas Rodgers
2020-05-24 17:42 ` Thomas Rodgers
2020-05-24 17:55   ` Florian Weimer
2020-05-24 18:11     ` Jonathan Wakely
2020-05-24 18:15       ` Florian Weimer
2020-05-24 21:37       ` Thomas Rodgers

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).