public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] libstdc++: Add c++2a <syncstream>
@ 2020-10-15 14:46 Thomas Rodgers
  2020-10-15 18:13 ` Jonathan Wakely
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Thomas Rodgers @ 2020-10-15 14:46 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

* Note: depends on a sufficiently C++20ified basic_stringbuf<>.

libstdc++/Changelog:
	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
	libstdc++-v3/include/Makefile.in: Regenerate.
	libstdc++-v3/include/std/streambuf
        (__detail::__streambuf_core_access): Define.
        (basic_streambuf): Befriend __detail::__streambuf_core_access.
	libstdc++-v3/include/std/syncstream: New header.
	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc:
        Likewise.

---
 libstdc++-v3/include/Makefile.am              |   1 +
 libstdc++-v3/include/Makefile.in              |   1 +
 libstdc++-v3/include/std/streambuf            |  81 +++++
 libstdc++-v3/include/std/syncstream           | 333 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   4 +
 .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
 .../testsuite/27_io/basic_syncbuf/2.cc        |  27 ++
 .../27_io/basic_syncbuf/basic_ops/1.cc        | 138 ++++++++
 .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
 .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
 .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
 .../testsuite/27_io/basic_syncstream/2.cc     |  27 ++
 .../27_io/basic_syncstream/basic_ops/1.cc     | 135 +++++++
 .../basic_syncstream/requirements/types.cc    |  43 +++
 14 files changed, 1018 insertions(+)
 create mode 100644 libstdc++-v3/include/std/syncstream
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc

diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 28d273924ee..61aaff7a2f4 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -73,6 +73,7 @@ std_headers = \
 	${std_srcdir}/shared_mutex \
 	${std_srcdir}/span \
 	${std_srcdir}/sstream \
+	${std_srcdir}/syncstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
 	${std_srcdir}/stop_token \
diff --git a/libstdc++-v3/include/std/streambuf b/libstdc++-v3/include/std/streambuf
index cae35e75bda..d6053e4c1ed 100644
--- a/libstdc++-v3/include/std/streambuf
+++ b/libstdc++-v3/include/std/streambuf
@@ -48,6 +48,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
 #define _IsUnused __attribute__ ((__unused__))
 
+  template<typename _CharT, typename _Traits>
+    class basic_streambuf;
+
+#if __cplusplus > 201703L
+  namespace __detail
+  {
+    struct __streambuf_core_access
+    {
+      template<typename _CharT, typename _Traits>
+	static void
+	_S_imbue(basic_streambuf<_CharT, _Traits>& __b, const locale& __loc)
+	{ __b.imbue(__loc); }
+
+      template<typename _CharT, typename _Traits>
+	static basic_streambuf<_CharT, _Traits>*
+	_S_setbuf(basic_streambuf<_CharT, _Traits>& __b,
+		  _CharT* __c, streamsize __n)
+	{ return __b.setbuf(__c, __n); }
+
+      template<typename _CharT, typename _Traits>
+	static typename _Traits::pos_type
+	_S_seekoff(basic_streambuf<_CharT, _Traits>& __b,
+		   typename _Traits::off_type __off, ios_base::seekdir __dir,
+		   ios_base::openmode __mode)
+	  { return __b.seekoff(__off, __dir, __mode); }
+
+      template<typename _CharT, typename _Traits>
+	static typename _Traits::pos_type
+	_S_seekpos(basic_streambuf<_CharT, _Traits>& __b,
+		   typename _Traits::pos_type __pos, ios_base::openmode __mode)
+	{ return __b.seekpos(__pos, __mode); }
+
+      template<typename _CharT, typename _Traits>
+	static int
+	_S_sync(basic_streambuf<_CharT, _Traits>& __b)
+	{ return __b.sync(); }
+
+      template<typename _CharT, typename _Traits>
+	static streamsize
+	_S_showmanyc(basic_streambuf<_CharT, _Traits>& __b)
+	{ return __b.showmanyc(); }
+
+      template<typename _CharT, typename _Traits>
+	static streamsize
+	_S_xsgetn(basic_streambuf<_CharT, _Traits>& __b, _CharT* __s, streamsize __n)
+	{ return __b.xsgetn(__s, __n); }
+
+      template<typename _CharT, typename _Traits>
+	static typename _Traits::int_type
+	_S_underflow(basic_streambuf<_CharT, _Traits>& __b)
+	{ return __b.underflow(); }
+
+      template<typename _CharT, typename _Traits>
+	static typename _Traits::int_type
+	_S_uflow(basic_streambuf<_CharT, _Traits>& __b)
+	{ return __b.uflow(); }
+
+      template<typename _CharT, typename _Traits>
+	static typename _Traits::int_type
+	_S_pbackfail(basic_streambuf<_CharT, _Traits>& __b,
+		     typename _Traits::int_type __c)
+	{ return __b.pbackfail(__c); }
+
+      template<typename _CharT, typename _Traits>
+	static streamsize
+	_S_xsputn(basic_streambuf<_CharT, _Traits>& __b,
+	       const typename _Traits::char_type* __s, streamsize __n)
+	{ return __b.xsputn(__s, __n); }
+
+      template<typename _CharT, typename _Traits>
+	typename _Traits::int_type
+	_S_overflow(basic_streambuf<_CharT, _Traits>& __b,
+		    typename _Traits::int_type __c)
+	{ return __b.overflow(__c); } 
+    };
+  }
+#endif // C++20
+
   template<typename _CharT, typename _Traits>
     streamsize
     __copy_streambufs_eof(basic_streambuf<_CharT, _Traits>*,
@@ -456,6 +534,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return this->xsputn(__s, __n); }
 
     protected:
+#if __cplusplus > 201703L
+      friend __detail::__streambuf_core_access;
+#endif
       /**
        *  @brief  Base constructor.
        *
diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
new file mode 100644
index 00000000000..0a034ed03f4
--- /dev/null
+++ b/libstdc++-v3/include/std/syncstream
@@ -0,0 +1,333 @@
+// <syncstream> -*- 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received __a copy of the GNU General Public License and
+// __a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/ranges
+ *  This is a Standard C++ Library header.
+ */
+
+ #ifndef _GLIBCXX_SYNCSTREAM
+ #define _GLIBCXX_SYNCSTREAM 1
+
+#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+
+#define __cpp_lib_syncbuf 201803L
+
+#pragma GCC system_header
+
+#include <bits/alloc_traits.h>
+#include <bits/allocator.h>
+#include <bits/functional_hash.h>
+#include <bits/std_mutex.h>
+
+#include <ostream>
+#include <sstream>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
+    {
+    public:
+      using char_type = _CharT;
+      using int_type = typename _Traits::int_type;
+      using pos_type = typename _Traits::pos_type;
+      using off_type = typename _Traits::off_type;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
+
+      basic_syncbuf()
+      : basic_syncbuf(nullptr)
+      { }
+
+      explicit basic_syncbuf(streambuf_type* __obuf)
+	: basic_syncbuf(__obuf, allocator_type{})
+      { }
+
+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
+	: _M_wrapped(__obuf)
+	, _M_impl(__alloc)
+	, _M_mtx(__obuf ? &_S_get_mutex(__obuf) : nullptr)
+      { }
+
+      basic_syncbuf(basic_syncbuf&& __other)
+	: _M_wrapped(__other._M_wrapped)
+	, _M_impl(std::move(__other._M_impl))
+	, _M_mtx(__other._M_mtx)
+	, _M_emit_on_sync(__other._M_emit_on_sync)
+	, _M_needs_sync(__other._M_needs_sync)
+      {
+	__other._M_wrapped = nullptr;
+      }
+
+      ~basic_syncbuf()
+      {
+	try
+	  {
+	    emit();
+	  }
+	catch (...)
+	  { }
+      }
+
+      basic_syncbuf& operator=(basic_syncbuf&& __other)
+      {
+	if (&__other != this)
+	  {
+	    emit();
+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
+	    _M_impl = std::move(__other._M_impl);
+	    _M_mtx = __other._M_mtx;
+	    _M_emit_on_sync = __other._M_emit_on_sync;
+	    _M_needs_sync = __other._M_needs_sync;
+	  }
+	return *this;
+      }
+
+      void
+      swap(basic_syncbuf& __other)
+      {
+	if (&__other != this)
+	  {
+	    std::swap(_M_wrapped, __other._M_wrapped);
+	    std::swap(_M_impl, __other._M_impl);
+	    std::swap(_M_mtx, __other._M_mtx);
+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
+	    std::swap(_M_needs_sync, __other._M_needs_sync);
+	  }
+      }
+
+      bool
+      emit()
+      {
+	if (!_M_wrapped)
+	  return false;
+
+	auto __s = _M_impl.view();
+	if (__s.empty())
+	  return true;
+
+	const lock_guard<mutex> __l(*_M_mtx);
+	auto __xsz = _M_wrapped->sputn(__s.data(), __s.size());
+
+	if (__xsz != __s.size())
+	  return false;
+
+	if (_M_needs_sync)
+	  {
+	    _M_needs_sync = false;
+	    if (_M_wrapped->pubsync() != 0)
+	      return false;
+	  }
+	_M_impl.str("");
+	return true;
+      }
+
+      streambuf_type*
+      get_wrapped() const noexcept
+      { return _M_wrapped; }
+
+      allocator_type get_allocator() const noexcept
+      { return _M_impl.get_allocator(); }
+
+      void
+      set_emit_on_sync(bool __b) noexcept
+      { _M_emit_on_sync = __b; }
+
+    protected:
+      void
+      imbue(const locale& __loc) override
+      { __detail::__streambuf_core_access::_S_imbue(_M_impl, __loc); }
+
+      basic_streambuf<char_type,_Traits>*
+      setbuf(char_type* __c, streamsize __n) override
+      {
+	return __detail::__streambuf_core_access::_S_setbuf(_M_impl, __c, __n);
+      }
+
+      pos_type
+      seekoff(off_type __off, ios_base::seekdir __dir,
+	      ios_base::openmode __mode) override
+      {
+	return __detail::__streambuf_core_access::_S_seekoff(_M_impl, __off,
+							     __dir, __mode);
+      }
+
+      pos_type
+      seekpos(pos_type __pos, ios_base::openmode __mode) override
+      {
+	return __detail::__streambuf_core_access::_S_seekpos(_M_impl,
+							     __pos, __mode);
+      }
+
+      int
+      sync() override
+      {
+	auto __res = __detail::__streambuf_core_access::_S_sync(_M_impl);
+	if (__res == 0)
+	  {
+	    _M_needs_sync = true;
+	    if (_M_emit_on_sync)
+	      return emit() ? 0 : -1;
+	  }
+	return __res;
+      }
+
+      streamsize
+      showmanyc() override
+      { return __detail::__streambuf_core_access::_S_showmanyc(_M_impl); } 
+
+      streamsize
+      xsgetn(char_type* __s, streamsize __n) override
+      {
+	return __detail::__streambuf_core_access::_S_xsgetn(_M_impl, __s, __n);
+      } 
+
+      int_type
+      underflow() override
+      { return __detail::__streambuf_core_access::_S_underflow(_M_impl); }
+
+      int_type
+      uflow() override
+      { return __detail::__streambuf_core_access::_S_uflow(_M_impl); }
+
+      int_type
+      pbackfail(int_type __c) override
+      { return __detail::__streambuf_core_access::_S_pbackfail(_M_impl, __c); }
+
+      streamsize
+      xsputn(const char_type* __s, streamsize __n) override
+      {
+	return __detail::__streambuf_core_access::_S_xsputn(_M_impl, __s, __n);
+      }
+
+    private:
+      streambuf_type* _M_wrapped;
+
+      using __impl_type = basic_stringbuf<char_type, traits_type,
+					  allocator_type>;
+      __impl_type _M_impl;
+      mutex* _M_mtx;
+
+      bool _M_emit_on_sync = false;
+      bool _M_needs_sync = false;
+
+      static constexpr size_t _S_small_size = 128;
+
+      static mutex&
+      _S_get_mutex(void* __t)
+      {
+	const unsigned char __mask = 0xf;
+	static mutex __m[__mask + 1];
+
+	auto __key = _Hash_impl::hash(__t) & __mask;
+	return __m[__key];
+      }
+    };
+
+  template <typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_osyncstream : public basic_ostream<_CharT, _Traits>
+    {
+    public:
+      // Types:
+      using char_type = _CharT;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using int_type = typename traits_type::int_type;
+      using pos_type = typename traits_type::pos_type;
+      using off_type = typename traits_type::off_type;
+      using syncbuf_type = basic_syncbuf<_CharT, _Traits, _Alloc>;
+      using streambuf_type = typename syncbuf_type::streambuf_type;
+
+      using __ostream_type = basic_ostream<_CharT, _Traits>;
+
+    private:
+      syncbuf_type _M_syncbuf;
+
+    public:
+      basic_osyncstream(streambuf_type* __buf, const allocator_type& __a)
+	: _M_syncbuf(__buf, __a)
+      { this->init(&_M_syncbuf); }
+
+      explicit basic_osyncstream(streambuf_type* __buf)
+	: _M_syncbuf(__buf)
+      { this->init(&_M_syncbuf); }
+
+      basic_osyncstream(basic_ostream<char_type, traits_type>& __os,
+		        const allocator_type& __a)
+	: basic_osyncstream(__os.rdbuf(), __a)
+      { this->init(&_M_syncbuf); }
+
+      explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
+	: basic_osyncstream(__os.rdbuf())
+      { this->init(&_M_syncbuf); }
+
+      basic_osyncstream(basic_osyncstream&& __rhs) noexcept
+	: __ostream_type(std::move(__rhs)),
+	_M_syncbuf(std::move(__rhs._M_syncbuf))
+      { __ostream_type::set_rdbuf(&_M_syncbuf); }
+
+      ~basic_osyncstream() = default;
+
+      basic_osyncstream& operator=(basic_osyncstream&& __rhs) noexcept
+      {
+	if (&__rhs != this)
+	  {
+	    __ostream_type::operator=(std::move(__rhs));
+	    _M_syncbuf = std::move(__rhs._M_syncbuf);
+	    __ostream_type::set_rdbuf(&_M_syncbuf);
+	  }
+	return *this;
+      }
+
+      syncbuf_type* rdbuf() const noexcept
+      { return const_cast<syncbuf_type*>(&_M_syncbuf); }
+
+      streambuf_type* get_wrapped() const noexcept
+      { return _M_syncbuf.get_wrapped(); }
+
+      void emit()
+      { _M_syncbuf.emit(); }
+    };
+
+  template <class _CharT, class _Traits, class _Allocator>
+    inline void
+    swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __x,
+	 basic_syncbuf<_CharT, _Traits, _Allocator>& __y)
+    { __x.swap(__y); }
+
+  using syncbuf = basic_syncbuf<char>;
+  using wsyncbuf = basic_syncbuf<wchar_t>;
+
+  using osyncstream = basic_osyncstream<char>;
+  using wosyncstream = basic_osyncstream<wchar_t>;
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // C++2a
+#endif	/* _GLIBCXX_SYNCSTREAM */
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index d5d42ed0a72..8681d70fbfa 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -215,6 +215,10 @@
 #define __cpp_lib_interpolate 201902L
 #ifdef _GLIBCXX_HAS_GTHREADS
 # define __cpp_lib_jthread 201911L
+# ifdef _GLIBCXX_USE_CXX11_ABI
+// Only supported with cx11-abi
+#  define __cpp_lib_syncbuf 201803L
+# endif
 #endif
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
new file mode 100644
index 00000000000..ebea9becdb3
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
new file mode 100644
index 00000000000..43538f89fb8
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.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 <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
new file mode 100644
index 00000000000..98e00dea1f1
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
@@ -0,0 +1,138 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::syncbuf s1;
+    VERIFY( s1.get_wrapped() == nullptr );
+
+    std::stringbuf b;
+    std::syncbuf s2(&b);
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+
+    sbuf_t b;
+
+    alloc_type aa;
+    sbuf_t s1(&b, aa);
+    VERIFY( aa == s1.get_allocator() );
+
+    alloc_type aaa(42);
+    sbuf_t s2(&b, aaa);
+    VERIFY( aaa == s2.get_allocator() );
+
+    VERIFY( s1.get_allocator() != s2.get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2;
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b;
+  std::syncbuf s1(&b);
+
+  std::syncbuf s2;
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == nullptr );
+  VERIFY( s2.get_wrapped() == &b );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( b.str() != txt );
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() != txt );
+
+    VERIFY( s.emit() );
+    VERIFY( b.str() == txt );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+    s.set_emit_on_sync(true);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() == txt );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
new file mode 100644
index 00000000000..49266b4abf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
@@ -0,0 +1,42 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
new file mode 100644
index 00000000000..097da110178
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
@@ -0,0 +1,130 @@
+// 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 -pthread -Wl,--no-demangle" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <syncstream>
+#include <thread>
+#include <vector>
+#include <unordered_map>
+#include <utility>
+
+#include <testsuite_hooks.h>
+
+int
+main()
+{
+  using namespace std::chrono_literals;
+
+  std::stringbuf b;
+  std::atomic<unsigned> running(0);
+
+  auto const cstr = "This is a test";
+
+  constexpr int ct = 1000;
+  auto const body = [&]{
+    ++running;
+    auto tid = std::this_thread::get_id();
+    std::syncbuf s(&b);
+    for (auto i = 0; i < ct; ++i)
+    {
+      std::stringstream stm;
+      stm << tid << ' ' << cstr << ' ' << i << std::endl;
+      auto sv = stm.view();
+      s.sputn(sv.data(), sv.size());
+      VERIFY( s.emit() );
+    }
+  };
+
+  const auto tct = 8;
+  std::vector<std::thread> ts;
+  ts.reserve(tct);
+
+  for (auto i = 0; i < tct; ++i)
+    ts.emplace_back(std::thread(body));
+
+  do
+  {
+    std::this_thread::sleep_for(100ms);
+  }
+  while (running.load() < tct);
+
+  std::unordered_map<std::string, int> tids;
+  for (auto&& t : ts)
+  {
+    std::stringstream stm;
+    stm << t.get_id();
+    tids.emplace(std::make_pair(stm.str(), 0));
+  };
+
+  for (auto&& t : ts)
+     t.join();
+
+  std::vector<std::string_view> lines;
+  const auto lct = ct * ts.size();
+  lines.reserve(lct);
+
+  std::size_t last = 0;
+  auto sv = b.view();
+  auto p = sv.find('\n');
+  while (p != std::string_view::npos)
+  {
+    lines.emplace_back(sv.substr(last, p - last));
+    last = p+1;
+    p = sv.find('\n', last);
+  }
+  VERIFY( lines.size() == lct );
+
+  auto sep = "";
+  auto i = 0;
+  sv = std::string_view(cstr);
+
+  for (auto&& l : lines)
+  {
+    auto p = l.find(' ');
+    VERIFY( p != std::string_view::npos );
+    std::string tid(l.substr(0, p));
+    ++p;
+
+    VERIFY( l.substr(p, sv.size()) == sv );
+    std::string s(l.substr(++p + sv.size()));
+    std::stringstream stm(s);
+    int n;
+    stm >> n;
+    VERIFY( stm.eof() );
+    VERIFY( n >= 0 && n < ct );
+    auto it = tids.find(tid);
+    VERIFY( it != std::end(tids) );
+    ++(it->second);
+  }
+
+  for (auto const& t : tids)
+  {
+    VERIFY( t.second == ct );
+  }
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
new file mode 100644
index 00000000000..ebea9becdb3
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
new file mode 100644
index 00000000000..43538f89fb8
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.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 <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
new file mode 100644
index 00000000000..e2cb6404cbe
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
@@ -0,0 +1,135 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+    VERIFY( s.rdbuf() != nullptr );
+    VERIFY( s.get_wrapped() == &b );
+  }
+
+  {
+    std::ostringstream stm;
+    std::osyncstream s(stm);
+    VERIFY( s.get_wrapped() == stm.rdbuf() );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+    using stream_t = std::basic_osyncstream<char, std::char_traits<char>,
+					    alloc_type>;
+    using str_t = std::basic_ostringstream<char, std::char_traits<char>,
+                                           alloc_type>;
+    sbuf_t b;
+
+    alloc_type aa;
+    stream_t s1(&b, aa);
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+
+    alloc_type aaa(42);
+    stream_t s2(&b, aaa);
+    VERIFY( aaa == s2.rdbuf()->get_allocator() );
+
+    VERIFY( s1.rdbuf()->get_allocator() != s2.rdbuf()->get_allocator() );
+
+    str_t stm;
+    stream_t s3(stm, aa);
+    VERIFY( s3.get_wrapped() == stm.rdbuf() );
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s1(&b);
+
+    std::osyncstream s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b1;
+    std::osyncstream s1(&b1);
+
+    std::stringbuf b2;
+    std::osyncstream s2(&b2);
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b1 );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b1;
+  std::osyncstream s1(&b1);
+  
+  std::stringbuf b2;
+  std::osyncstream s2(&b2);
+
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == &b2 );
+  VERIFY( s2.get_wrapped() == &b1 );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+
+    const std::string_view txt("This is a test");
+    s << txt;
+
+    s.emit();
+    VERIFY( b.str() == txt );
+  }
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
new file mode 100644
index 00000000000..dfc0b72efcd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
@@ -0,0 +1,43 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+    using syncbuf_type = test_type::syncbuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
-- 
2.26.2


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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-15 14:46 [PATCH] libstdc++: Add c++2a <syncstream> Thomas Rodgers
@ 2020-10-15 18:13 ` Jonathan Wakely
  2020-10-15 18:18 ` Jonathan Wakely
  2020-10-15 19:29 ` Jonathan Wakely
  2 siblings, 0 replies; 16+ messages in thread
From: Jonathan Wakely @ 2020-10-15 18:13 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 15/10/20 07:46 -0700, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>* Note: depends on a sufficiently C++20ified basic_stringbuf<>.
>
>libstdc++/Changelog:
>	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
>	libstdc++-v3/include/Makefile.in: Regenerate.
>	libstdc++-v3/include/std/streambuf
>        (__detail::__streambuf_core_access): Define.
>        (basic_streambuf): Befriend __detail::__streambuf_core_access.
>	libstdc++-v3/include/std/syncstream: New header.
>	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc:
>        Likewise.
>
>---
> libstdc++-v3/include/Makefile.am              |   1 +
> libstdc++-v3/include/Makefile.in              |   1 +
> libstdc++-v3/include/std/streambuf            |  81 +++++
> libstdc++-v3/include/std/syncstream           | 333 ++++++++++++++++++
> libstdc++-v3/include/std/version              |   4 +
> .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
> .../testsuite/27_io/basic_syncbuf/2.cc        |  27 ++
> .../27_io/basic_syncbuf/basic_ops/1.cc        | 138 ++++++++
> .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
> .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
> .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
> .../testsuite/27_io/basic_syncstream/2.cc     |  27 ++
> .../27_io/basic_syncstream/basic_ops/1.cc     | 135 +++++++
> .../basic_syncstream/requirements/types.cc    |  43 +++
> 14 files changed, 1018 insertions(+)
> create mode 100644 libstdc++-v3/include/std/syncstream
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
>
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index 28d273924ee..61aaff7a2f4 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -73,6 +73,7 @@ std_headers = \
> 	${std_srcdir}/shared_mutex \
> 	${std_srcdir}/span \
> 	${std_srcdir}/sstream \
>+	${std_srcdir}/syncstream \

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

> 	${std_srcdir}/stack \
> 	${std_srcdir}/stdexcept \
> 	${std_srcdir}/stop_token \
>diff --git a/libstdc++-v3/include/std/streambuf b/libstdc++-v3/include/std/streambuf
>index cae35e75bda..d6053e4c1ed 100644
>--- a/libstdc++-v3/include/std/streambuf
>+++ b/libstdc++-v3/include/std/streambuf
>@@ -48,6 +48,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> #define _IsUnused __attribute__ ((__unused__))
>
>+  template<typename _CharT, typename _Traits>
>+    class basic_streambuf;
>+
>+#if __cplusplus > 201703L
>+  namespace __detail
>+  {
>+    struct __streambuf_core_access
>+    {
>+      template<typename _CharT, typename _Traits>
>+	static void
>+	_S_imbue(basic_streambuf<_CharT, _Traits>& __b, const locale& __loc)
>+	{ __b.imbue(__loc); }

As discussed on IRC, this doesn't work. But as discussed below, I
don't think it's needed at all.

>+#endif // C++20
>+
>   template<typename _CharT, typename _Traits>
>     streamsize
>     __copy_streambufs_eof(basic_streambuf<_CharT, _Traits>*,
>@@ -456,6 +534,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       { return this->xsputn(__s, __n); }
>
>     protected:
>+#if __cplusplus > 201703L
>+      friend __detail::__streambuf_core_access;
>+#endif
>       /**
>        *  @brief  Base constructor.
>        *
>diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
>new file mode 100644
>index 00000000000..0a034ed03f4
>--- /dev/null
>+++ b/libstdc++-v3/include/std/syncstream
>@@ -0,0 +1,333 @@
>+// <syncstream> -*- 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.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received __a copy of the GNU General Public License and
>+// __a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file include/ranges

s/ranges/syncstream/

>+ *  This is a Standard C++ Library header.
>+ */
>+
>+ #ifndef _GLIBCXX_SYNCSTREAM
>+ #define _GLIBCXX_SYNCSTREAM 1
>+
>+#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
>+
>+#define __cpp_lib_syncbuf 201803L
>+
>+#pragma GCC system_header
>+
>+#include <bits/alloc_traits.h>
>+#include <bits/allocator.h>
>+#include <bits/functional_hash.h>
>+#include <bits/std_mutex.h>
>+
>+#include <ostream>
>+#include <sstream>

The <ostream> include is redundant because <sstream> includes it
anyway.

Please put the standard headers before the <bits/xxx> ones. I don't
think you need <bits/alloc_traits.h> here, and <bits/allocator.h>
should already be included by <sstream>.


>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
>+	    typename _Alloc = allocator<_CharT>>
>+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
>+    {
>+    public:
>+      using char_type = _CharT;
>+      using int_type = typename _Traits::int_type;
>+      using pos_type = typename _Traits::pos_type;
>+      using off_type = typename _Traits::off_type;
>+      using traits_type = _Traits;
>+      using allocator_type = _Alloc;
>+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
>+
>+      basic_syncbuf()
>+      : basic_syncbuf(nullptr)

Please use basic_syncbuf(nullptr, allocator_type{}) here, so we skip
one level of delegation (and overload resolution for two args can
ignore the one-arg ctors).

>+      { }
>+
>+      explicit basic_syncbuf(streambuf_type* __obuf)

Newline after 'explicit' so that all the constructors have the class
name in the same column.


>+	: basic_syncbuf(__obuf, allocator_type{})
>+      { }
>+
>+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
>+	: _M_wrapped(__obuf)
>+	, _M_impl(__alloc)
>+	, _M_mtx(__obuf ? &_S_get_mutex(__obuf) : nullptr)
>+      { }
>+
>+      basic_syncbuf(basic_syncbuf&& __other)
>+	: _M_wrapped(__other._M_wrapped)
>+	, _M_impl(std::move(__other._M_impl))
>+	, _M_mtx(__other._M_mtx)
>+	, _M_emit_on_sync(__other._M_emit_on_sync)
>+	, _M_needs_sync(__other._M_needs_sync)
>+      {
>+	__other._M_wrapped = nullptr;
>+      }
>+
>+      ~basic_syncbuf()
>+      {
>+	try

Include <bits/functexcept.h> and use __try and __catch so it compiles
with -fno-exceptions.

>+	  {
>+	    emit();
>+	  }
>+	catch (...)
>+	  { }
>+      }
>+
>+      basic_syncbuf& operator=(basic_syncbuf&& __other)
>+      {
>+	if (&__other != this)

This needs to be std::__addressof(__other) to stop ADL finding a
greedy operator& in an associated namespace of _CharT, _Traits, or
_Alloc.

>+	  {
>+	    emit();
>+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;

Newline between statements.

>+	    _M_impl = std::move(__other._M_impl);

I think _M_impl needs to be moved first, because that can throw. We
need to do the part that can throw first, then if that succeeds it's
safe to modify the other members.

Although ... the standard says this function is noexcept ... except it
doesn't say that in the synopsis. It's noexcept in one place and not
another. Same for the member swap and non-member swap.

Sigh.

>+	    _M_mtx = __other._M_mtx;
>+	    _M_emit_on_sync = __other._M_emit_on_sync;
>+	    _M_needs_sync = __other._M_needs_sync;
>+	  }
>+	return *this;
>+      }
>+
>+      void
>+      swap(basic_syncbuf& __other)
>+      {
>+	if (&__other != this)

std::__addressof(__other) again.

>+	  {
>+	    std::swap(_M_wrapped, __other._M_wrapped);
>+	    std::swap(_M_impl, __other._M_impl);

Same issue with ordering the throwing operation first.

>+	    std::swap(_M_mtx, __other._M_mtx);
>+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
>+	    std::swap(_M_needs_sync, __other._M_needs_sync);
>+	  }
>+      }
>+
>+      bool
>+      emit()
>+      {
>+	if (!_M_wrapped)
>+	  return false;
>+
>+	auto __s = _M_impl.view();
>+	if (__s.empty())
>+	  return true;
>+
>+	const lock_guard<mutex> __l(*_M_mtx);
>+	auto __xsz = _M_wrapped->sputn(__s.data(), __s.size());
>+
>+	if (__xsz != __s.size())
>+	  return false;
>+
>+	if (_M_needs_sync)
>+	  {
>+	    _M_needs_sync = false;
>+	    if (_M_wrapped->pubsync() != 0)
>+	      return false;
>+	  }
>+	_M_impl.str("");

Hmm, I don't know what the standard specifies here. It says "On
success, the associated output is empty." But that doesn't tell us
anything about the state if we return false.

>+	return true;
>+      }
>+
>+      streambuf_type*
>+      get_wrapped() const noexcept
>+      { return _M_wrapped; }
>+
>+      allocator_type get_allocator() const noexcept
>+      { return _M_impl.get_allocator(); }
>+
>+      void
>+      set_emit_on_sync(bool __b) noexcept
>+      { _M_emit_on_sync = __b; }
>+
>+    protected:
>+      void
>+      imbue(const locale& __loc) override
>+      { __detail::__streambuf_core_access::_S_imbue(_M_impl, __loc); }

pubimbue, but why is it here anyway? I don't see anything in the
standard requiring it.

>+
>+      basic_streambuf<char_type,_Traits>*
>+      setbuf(char_type* __c, streamsize __n) override
>+      {
>+	return __detail::__streambuf_core_access::_S_setbuf(_M_impl, __c, __n);
>+      }

Not required by the standard.

>+
>+      pos_type
>+      seekoff(off_type __off, ios_base::seekdir __dir,
>+	      ios_base::openmode __mode) override
>+      {
>+	return __detail::__streambuf_core_access::_S_seekoff(_M_impl, __off,
>+							     __dir, __mode);

pubseekoff, but not required.

>+      }
>+
>+      pos_type
>+      seekpos(pos_type __pos, ios_base::openmode __mode) override
>+      {
>+	return __detail::__streambuf_core_access::_S_seekpos(_M_impl,
>+							     __pos, __mode);

pubseekpos, but not required.

>+      }
>+
>+      int
>+      sync() override
>+      {
>+	auto __res = __detail::__streambuf_core_access::_S_sync(_M_impl);
>+	if (__res == 0)
>+	  {
>+	    _M_needs_sync = true;
>+	    if (_M_emit_on_sync)
>+	      return emit() ? 0 : -1;
>+	  }
>+	return __res;
>+      }
>+
>+      streamsize
>+      showmanyc() override
>+      { return __detail::__streambuf_core_access::_S_showmanyc(_M_impl); }

I think this could use _M_impl.in_avail(), but it's not required.

>+      streamsize
>+      xsgetn(char_type* __s, streamsize __n) override
>+      {
>+	return __detail::__streambuf_core_access::_S_xsgetn(_M_impl, __s, __n);

Not needed, you can't read from a syncbuf.

>+      }
>+
>+      int_type
>+      underflow() override
>+      { return __detail::__streambuf_core_access::_S_underflow(_M_impl); }

Ditto.

>+
>+      int_type
>+      uflow() override
>+      { return __detail::__streambuf_core_access::_S_uflow(_M_impl); }

Ditto.

>+      int_type
>+      pbackfail(int_type __c) override
>+      { return __detail::__streambuf_core_access::_S_pbackfail(_M_impl, __c); }

You can't read, so you can't put characters back into the get area.

>+      streamsize
>+      xsputn(const char_type* __s, streamsize __n) override
>+      {
>+	return __detail::__streambuf_core_access::_S_xsputn(_M_impl, __s, __n);

This could be _M_impl.sputn(__s, __n)

I don't see any requirement to override it, although doing so might
improve performance.


>+      }
>+
>+    private:
>+      streambuf_type* _M_wrapped;
>+
>+      using __impl_type = basic_stringbuf<char_type, traits_type,
>+					  allocator_type>;
>+      __impl_type _M_impl;
>+      mutex* _M_mtx;
>+
>+      bool _M_emit_on_sync = false;
>+      bool _M_needs_sync = false;
>+
>+      static constexpr size_t _S_small_size = 128;

This seems to be unused.

>+
>+      static mutex&
>+      _S_get_mutex(void* __t)
>+      {
>+	const unsigned char __mask = 0xf;
>+	static mutex __m[__mask + 1];
>+
>+	auto __key = _Hash_impl::hash(__t) & __mask;
>+	return __m[__key];

This is OK for now but please add a FIXME: comment reminding us to put
it in the library

Keeping those mutexes in the library allows us to ensure that our
table of mutexes has a longer lifetime than anything that might use it
(which we don't currently do). Otherwise a global syncbuf can run its
destructor after the mutexes have been destroyed, leading to UB in the
emit() call in the destructor. Better to only fix that in one place.

We should also consider sharing a single table of mutexes for the
various things that need them, so we don't waste space in the library
with several different tables. Especially if we increase their
alignment so there's only one per cacheline.

>+      }
>+    };
>+
>+  template <typename _CharT, typename _Traits = char_traits<_CharT>,
>+	    typename _Alloc = allocator<_CharT>>
>+    class basic_osyncstream : public basic_ostream<_CharT, _Traits>
>+    {
>+    public:
>+      // Types:
>+      using char_type = _CharT;
>+      using traits_type = _Traits;
>+      using allocator_type = _Alloc;
>+      using int_type = typename traits_type::int_type;
>+      using pos_type = typename traits_type::pos_type;
>+      using off_type = typename traits_type::off_type;
>+      using syncbuf_type = basic_syncbuf<_CharT, _Traits, _Alloc>;
>+      using streambuf_type = typename syncbuf_type::streambuf_type;
>+
>+      using __ostream_type = basic_ostream<_CharT, _Traits>;

This should be private.

>+
>+    private:
>+      syncbuf_type _M_syncbuf;
>+
>+    public:
>+      basic_osyncstream(streambuf_type* __buf, const allocator_type& __a)
>+	: _M_syncbuf(__buf, __a)
>+      { this->init(&_M_syncbuf); }

std::__addressof here and the constructors below.

>+
>+      explicit basic_osyncstream(streambuf_type* __buf)
>+	: _M_syncbuf(__buf)
>+      { this->init(&_M_syncbuf); }
>+
>+      basic_osyncstream(basic_ostream<char_type, traits_type>& __os,
>+		        const allocator_type& __a)
>+	: basic_osyncstream(__os.rdbuf(), __a)
>+      { this->init(&_M_syncbuf); }
>+
>+      explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
>+	: basic_osyncstream(__os.rdbuf())
>+      { this->init(&_M_syncbuf); }
>+
>+      basic_osyncstream(basic_osyncstream&& __rhs) noexcept
>+	: __ostream_type(std::move(__rhs)),
>+	_M_syncbuf(std::move(__rhs._M_syncbuf))
>+      { __ostream_type::set_rdbuf(&_M_syncbuf); }
>+
>+      ~basic_osyncstream() = default;
>+
>+      basic_osyncstream& operator=(basic_osyncstream&& __rhs) noexcept
>+      {
>+	if (&__rhs != this)
>+	  {
>+	    __ostream_type::operator=(std::move(__rhs));
>+	    _M_syncbuf = std::move(__rhs._M_syncbuf);
>+	    __ostream_type::set_rdbuf(&_M_syncbuf);
>+	  }
>+	return *this;
>+      }
>+
>+      syncbuf_type* rdbuf() const noexcept
>+      { return const_cast<syncbuf_type*>(&_M_syncbuf); }
>+
>+      streambuf_type* get_wrapped() const noexcept
>+      { return _M_syncbuf.get_wrapped(); }
>+
>+      void emit()
>+      { _M_syncbuf.emit(); }
>+    };
>+
>+  template <class _CharT, class _Traits, class _Allocator>
>+    inline void
>+    swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __x,

The standard says this has to be noexcept.

>+	 basic_syncbuf<_CharT, _Traits, _Allocator>& __y)
>+    { __x.swap(__y); }
>+
>+  using syncbuf = basic_syncbuf<char>;
>+  using wsyncbuf = basic_syncbuf<wchar_t>;
>+
>+  using osyncstream = basic_osyncstream<char>;
>+  using wosyncstream = basic_osyncstream<wchar_t>;
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace std
>+#endif // C++2a
>+#endif	/* _GLIBCXX_SYNCSTREAM */
>+
>diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
>index d5d42ed0a72..8681d70fbfa 100644
>--- a/libstdc++-v3/include/std/version
>+++ b/libstdc++-v3/include/std/version
>@@ -215,6 +215,10 @@
> #define __cpp_lib_interpolate 201902L
> #ifdef _GLIBCXX_HAS_GTHREADS
> # define __cpp_lib_jthread 201911L
>+# ifdef _GLIBCXX_USE_CXX11_ABI
>+// Only supported with cx11-abi
>+#  define __cpp_lib_syncbuf 201803L

Please keep the macros in alphabetical order. It should be further
down, not here.

Put it in order and guard it with
#if defined _GLIBCXX_USE_CXX11_ABI && _GLIBCXX_USE_CXX11_ABI


>+# endif
> #endif
> #define __cpp_lib_list_remove_return_type 201806L
> #define __cpp_lib_math_constants 201907L
>diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
>new file mode 100644
>index 00000000000..ebea9becdb3
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
>@@ -0,0 +1,28 @@
>+// 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 } }
>+// { dg-require-effective-target cxx11-abi }
>+
>+#include <syncstream>
>+
>+#ifndef __cpp_lib_syncbuf
>+# error "Feature-test macro for syncbuf missing in <syncstream>"
>+#elif __cpp_lib_syncbuf!= 201803L

Add a space before the operator.

>+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
>new file mode 100644
>index 00000000000..43538f89fb8
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.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 <version>
>+
>+#ifndef __cpp_lib_syncbuf
>+# error "Feature-test macro for syncbuf missing in <version>"
>+#elif __cpp_lib_syncbuf!= 201803L

Space before the operator.


>+int main()
>+{
>+  test01();
>+  test02();
>+  test03();
>+  test04();
>+  return 0;

The return statement is unnecessary (we might still have something in
the manual saying it's needed, but it's wrong).

>diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
>new file mode 100644
>index 00000000000..ebea9becdb3
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
>@@ -0,0 +1,28 @@
>+// 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 } }
>+// { dg-require-effective-target cxx11-abi }
>+
>+#include <syncstream>
>+
>+#ifndef __cpp_lib_syncbuf
>+# error "Feature-test macro for syncbuf missing in <syncstream>"
>+#elif __cpp_lib_syncbuf!= 201803L

I don't think we need to test it again, we already did it in the
basic_syncbuf tests. This file and the next one can be removed.

>+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
>+#endif
>diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
>new file mode 100644
>index 00000000000..43538f89fb8
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.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 <version>
>+
>+#ifndef __cpp_lib_syncbuf
>+# error "Feature-test macro for syncbuf missing in <version>"
>+#elif __cpp_lib_syncbuf!= 201803L
>+# error "Feature-test macro for syncbuf has wrong value in <version>"
>+#endif


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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-15 14:46 [PATCH] libstdc++: Add c++2a <syncstream> Thomas Rodgers
  2020-10-15 18:13 ` Jonathan Wakely
@ 2020-10-15 18:18 ` Jonathan Wakely
  2020-10-15 19:29 ` Jonathan Wakely
  2 siblings, 0 replies; 16+ messages in thread
From: Jonathan Wakely @ 2020-10-15 18:18 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 15/10/20 07:46 -0700, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>* Note: depends on a sufficiently C++20ified basic_stringbuf<>.
>
>libstdc++/Changelog:
>	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
>	libstdc++-v3/include/Makefile.in: Regenerate.
>	libstdc++-v3/include/std/streambuf
>        (__detail::__streambuf_core_access): Define.
>        (basic_streambuf): Befriend __detail::__streambuf_core_access.
>	libstdc++-v3/include/std/syncstream: New header.
>	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc:
>        Likewise.
>
>---
> libstdc++-v3/include/Makefile.am              |   1 +
> libstdc++-v3/include/Makefile.in              |   1 +
> libstdc++-v3/include/std/streambuf            |  81 +++++
> libstdc++-v3/include/std/syncstream           | 333 ++++++++++++++++++
> libstdc++-v3/include/std/version              |   4 +
> .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
> .../testsuite/27_io/basic_syncbuf/2.cc        |  27 ++
> .../27_io/basic_syncbuf/basic_ops/1.cc        | 138 ++++++++
> .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
> .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
> .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
> .../testsuite/27_io/basic_syncstream/2.cc     |  27 ++
> .../27_io/basic_syncstream/basic_ops/1.cc     | 135 +++++++
> .../basic_syncstream/requirements/types.cc    |  43 +++
> 14 files changed, 1018 insertions(+)
> create mode 100644 libstdc++-v3/include/std/syncstream
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
>
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index 28d273924ee..61aaff7a2f4 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -73,6 +73,7 @@ std_headers = \
> 	${std_srcdir}/shared_mutex \
> 	${std_srcdir}/span \
> 	${std_srcdir}/sstream \
>+	${std_srcdir}/syncstream \
> 	${std_srcdir}/stack \
> 	${std_srcdir}/stdexcept \
> 	${std_srcdir}/stop_token \
>diff --git a/libstdc++-v3/include/std/streambuf b/libstdc++-v3/include/std/streambuf
>index cae35e75bda..d6053e4c1ed 100644
>--- a/libstdc++-v3/include/std/streambuf
>+++ b/libstdc++-v3/include/std/streambuf
>@@ -48,6 +48,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> #define _IsUnused __attribute__ ((__unused__))
>
>+  template<typename _CharT, typename _Traits>
>+    class basic_streambuf;
>+
>+#if __cplusplus > 201703L
>+  namespace __detail
>+  {
>+    struct __streambuf_core_access
>+    {
>+      template<typename _CharT, typename _Traits>
>+	static void
>+	_S_imbue(basic_streambuf<_CharT, _Traits>& __b, const locale& __loc)
>+	{ __b.imbue(__loc); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static basic_streambuf<_CharT, _Traits>*
>+	_S_setbuf(basic_streambuf<_CharT, _Traits>& __b,
>+		  _CharT* __c, streamsize __n)
>+	{ return __b.setbuf(__c, __n); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static typename _Traits::pos_type
>+	_S_seekoff(basic_streambuf<_CharT, _Traits>& __b,
>+		   typename _Traits::off_type __off, ios_base::seekdir __dir,
>+		   ios_base::openmode __mode)
>+	  { return __b.seekoff(__off, __dir, __mode); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static typename _Traits::pos_type
>+	_S_seekpos(basic_streambuf<_CharT, _Traits>& __b,
>+		   typename _Traits::pos_type __pos, ios_base::openmode __mode)
>+	{ return __b.seekpos(__pos, __mode); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static int
>+	_S_sync(basic_streambuf<_CharT, _Traits>& __b)
>+	{ return __b.sync(); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static streamsize
>+	_S_showmanyc(basic_streambuf<_CharT, _Traits>& __b)
>+	{ return __b.showmanyc(); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static streamsize
>+	_S_xsgetn(basic_streambuf<_CharT, _Traits>& __b, _CharT* __s, streamsize __n)
>+	{ return __b.xsgetn(__s, __n); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static typename _Traits::int_type
>+	_S_underflow(basic_streambuf<_CharT, _Traits>& __b)
>+	{ return __b.underflow(); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static typename _Traits::int_type
>+	_S_uflow(basic_streambuf<_CharT, _Traits>& __b)
>+	{ return __b.uflow(); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static typename _Traits::int_type
>+	_S_pbackfail(basic_streambuf<_CharT, _Traits>& __b,
>+		     typename _Traits::int_type __c)
>+	{ return __b.pbackfail(__c); }
>+
>+      template<typename _CharT, typename _Traits>
>+	static streamsize
>+	_S_xsputn(basic_streambuf<_CharT, _Traits>& __b,
>+	       const typename _Traits::char_type* __s, streamsize __n)
>+	{ return __b.xsputn(__s, __n); }
>+
>+      template<typename _CharT, typename _Traits>
>+	typename _Traits::int_type
>+	_S_overflow(basic_streambuf<_CharT, _Traits>& __b,
>+		    typename _Traits::int_type __c)
>+	{ return __b.overflow(__c); }
>+    };
>+  }
>+#endif // C++20
>+
>   template<typename _CharT, typename _Traits>
>     streamsize
>     __copy_streambufs_eof(basic_streambuf<_CharT, _Traits>*,
>@@ -456,6 +534,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       { return this->xsputn(__s, __n); }
>
>     protected:
>+#if __cplusplus > 201703L
>+      friend __detail::__streambuf_core_access;
>+#endif
>       /**
>        *  @brief  Base constructor.
>        *
>diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
>new file mode 100644
>index 00000000000..0a034ed03f4
>--- /dev/null
>+++ b/libstdc++-v3/include/std/syncstream
>@@ -0,0 +1,333 @@
>+// <syncstream> -*- 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.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received __a copy of the GNU General Public License and
>+// __a copy of the GCC Runtime Library Exception along with this program;

Two cases of "__a" above that should be just "a". That seems to have
come from <concepts> which I then copied to <ranges>. Oops. I'll take
care of fixing those.


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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-15 14:46 [PATCH] libstdc++: Add c++2a <syncstream> Thomas Rodgers
  2020-10-15 18:13 ` Jonathan Wakely
  2020-10-15 18:18 ` Jonathan Wakely
@ 2020-10-15 19:29 ` Jonathan Wakely
  2020-10-16  0:24   ` Thomas Rodgers
  2020-10-21 16:53   ` Thomas Rodgers
  2 siblings, 2 replies; 16+ messages in thread
From: Jonathan Wakely @ 2020-10-15 19:29 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 15/10/20 07:46 -0700, Thomas Rodgers wrote:
>+  template <typename _CharT, typename _Traits = char_traits<_CharT>,
>+	    typename _Alloc = allocator<_CharT>>
>+    class basic_osyncstream : public basic_ostream<_CharT, _Traits>
>+    {
>+    public:
>+      // Types:
>+      using char_type = _CharT;
>+      using traits_type = _Traits;
>+      using allocator_type = _Alloc;
>+      using int_type = typename traits_type::int_type;
>+      using pos_type = typename traits_type::pos_type;
>+      using off_type = typename traits_type::off_type;
>+      using syncbuf_type = basic_syncbuf<_CharT, _Traits, _Alloc>;
>+      using streambuf_type = typename syncbuf_type::streambuf_type;
>+
>+      using __ostream_type = basic_ostream<_CharT, _Traits>;
>+
>+    private:
>+      syncbuf_type _M_syncbuf;
>+
>+    public:
>+      basic_osyncstream(streambuf_type* __buf, const allocator_type& __a)
>+	: _M_syncbuf(__buf, __a)
>+      { this->init(&_M_syncbuf); }
>+
>+      explicit basic_osyncstream(streambuf_type* __buf)
>+	: _M_syncbuf(__buf)
>+      { this->init(&_M_syncbuf); }
>+
>+      basic_osyncstream(basic_ostream<char_type, traits_type>& __os,
>+		        const allocator_type& __a)
>+	: basic_osyncstream(__os.rdbuf(), __a)
>+      { this->init(&_M_syncbuf); }
>+
>+      explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
>+	: basic_osyncstream(__os.rdbuf())
>+      { this->init(&_M_syncbuf); }
>+
>+      basic_osyncstream(basic_osyncstream&& __rhs) noexcept
>+	: __ostream_type(std::move(__rhs)),
>+	_M_syncbuf(std::move(__rhs._M_syncbuf))
>+      { __ostream_type::set_rdbuf(&_M_syncbuf); }
>+
>+      ~basic_osyncstream() = default;
>+
>+      basic_osyncstream& operator=(basic_osyncstream&& __rhs) noexcept
>+      {
>+	if (&__rhs != this)

Rather than adding std::__addressof here, I'm not sure we need the
check for self-assignment at all. The ostream base's move assignment
is safe on self-assignment, and the syncbuf is too (because it checks
for it).

>+	  {
>+	    __ostream_type::operator=(std::move(__rhs));
>+	    _M_syncbuf = std::move(__rhs._M_syncbuf);
>+	    __ostream_type::set_rdbuf(&_M_syncbuf);

I think this set_rdbuf is not needed.

I think the move assignment could be defaulted.

>+	  }
>+	return *this;
>+      }
>+
>+      syncbuf_type* rdbuf() const noexcept
>+      { return const_cast<syncbuf_type*>(&_M_syncbuf); }
>+
>+      streambuf_type* get_wrapped() const noexcept
>+      { return _M_syncbuf.get_wrapped(); }
>+
>+      void emit()
>+      { _M_syncbuf.emit(); }

This needs to check the result of _M_syncbuf.emit() and possibly set
failbit.



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

* [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-15 19:29 ` Jonathan Wakely
@ 2020-10-16  0:24   ` Thomas Rodgers
  2020-10-21 16:53   ` Thomas Rodgers
  1 sibling, 0 replies; 16+ messages in thread
From: Thomas Rodgers @ 2020-10-16  0:24 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

This should address the cumulative comments (modulo the discussion going
on on the reflector about specification issues/questions).

libstdc++/Changelog:
	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
	libstdc++-v3/include/Makefile.in: Regenerate.
	libstdc++-v3/include/precompiled/stdc++.h: Includ new header.
	libstdc++-v3/include/std/streambuf
        (__detail::__streambuf_core_access): Define.
        (basic_streambuf): Befriend __detail::__streambuf_core_access.
	libstdc++-v3/include/std/syncstream: New header.
	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
 libstdc++-v3/include/std/sstream              |   2 +-
 libstdc++-v3/include/std/syncstream           | 276 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   4 +
 .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
 .../testsuite/27_io/basic_syncbuf/2.cc        |  27 ++
 .../27_io/basic_syncbuf/basic_ops/1.cc        | 138 +++++++++
 .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
 .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++++
 .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
 .../testsuite/27_io/basic_syncstream/2.cc     |  27 ++
 .../27_io/basic_syncstream/basic_ops/1.cc     | 135 +++++++++
 .../basic_syncstream/requirements/types.cc    |  43 +++
 16 files changed, 883 insertions(+), 2 deletions(-)
 create mode 100644 libstdc++-v3/include/std/syncstream
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc

diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 9b49a15d31b..320f6dea688 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
                          include/streambuf \
                          include/string \
                          include/string_view \
+                         include/syncstream \
                          include/system_error \
                          include/thread \
                          include/tuple \
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 28d273924ee..61aaff7a2f4 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -73,6 +73,7 @@ std_headers = \
 	${std_srcdir}/shared_mutex \
 	${std_srcdir}/span \
 	${std_srcdir}/sstream \
+	${std_srcdir}/syncstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
 	${std_srcdir}/stop_token \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index 7518a98c25a..8899c323a28 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -141,6 +141,6 @@
 #include <ranges>
 #include <span>
 #include <stop_token>
-// #include <syncstream>
+#include <syncstream>
 #include <version>
 #endif
diff --git a/libstdc++-v3/include/std/sstream b/libstdc++-v3/include/std/sstream
index b0d1decfe63..5379f025eef 100644
--- a/libstdc++-v3/include/std/sstream
+++ b/libstdc++-v3/include/std/sstream
@@ -137,7 +137,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       // 27.8.2.2 Assign and swap:
 
       basic_stringbuf&
-      operator=(const basic_stringbuf&) = delete;
+     operator=(const basic_stringbuf&) = delete;
 
       basic_stringbuf&
       operator=(basic_stringbuf&& __rhs)
diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
new file mode 100644
index 00000000000..96bc67a9713
--- /dev/null
+++ b/libstdc++-v3/include/std/syncstream
@@ -0,0 +1,276 @@
+// <syncstream> -*- 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/syncstream
+ *  This is a Standard C++ Library header.
+ */
+
+ #ifndef _GLIBCXX_SYNCSTREAM
+ #define _GLIBCXX_SYNCSTREAM 1
+
+#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+
+#define __cpp_lib_syncbuf 201803L
+
+#pragma GCC system_header
+
+#include <sstream>
+
+#include <bits/alloc_traits.h>
+#include <bits/allocator.h>
+#include <bits/functexcept.h>
+#include <bits/functional_hash.h>
+#include <bits/std_mutex.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
+    {
+    public:
+      using char_type = _CharT;
+      using int_type = typename _Traits::int_type;
+      using pos_type = typename _Traits::pos_type;
+      using off_type = typename _Traits::off_type;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
+
+      basic_syncbuf()
+      : basic_syncbuf(nullptr, allocator_type{})
+      { }
+
+      explicit
+      basic_syncbuf(streambuf_type* __obuf)
+	: basic_syncbuf(__obuf, allocator_type{})
+      { }
+
+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
+	: _M_wrapped(__obuf)
+	, _M_impl(__alloc)
+	, _M_mtx(__obuf ? &_S_get_mutex(__obuf) : nullptr)
+      { }
+
+      basic_syncbuf(basic_syncbuf&& __other)
+	: _M_wrapped(__other._M_wrapped)
+	, _M_impl(std::move(__other._M_impl))
+	, _M_mtx(__other._M_mtx)
+	, _M_emit_on_sync(__other._M_emit_on_sync)
+	, _M_needs_sync(__other._M_needs_sync)
+      {
+	__other._M_wrapped = nullptr;
+      }
+
+      ~basic_syncbuf()
+      {
+	__try
+	  {
+	    emit();
+	  }
+	__catch (...)
+	  { }
+      }
+
+      basic_syncbuf& operator=(basic_syncbuf&& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    emit();
+
+	    _M_impl = std::move(__other._M_impl);
+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
+	    _M_mtx = __other._M_mtx;
+	    _M_emit_on_sync = __other._M_emit_on_sync;
+	    _M_needs_sync = __other._M_needs_sync;
+	  }
+	return *this;
+      }
+
+      void
+      swap(basic_syncbuf& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    std::swap(_M_impl, __other._M_impl);
+	    std::swap(_M_wrapped, __other._M_wrapped);
+	    std::swap(_M_mtx, __other._M_mtx);
+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
+	    std::swap(_M_needs_sync, __other._M_needs_sync);
+	  }
+      }
+
+      bool
+      emit()
+      {
+	if (!_M_wrapped)
+	  return false;
+
+	auto __s = _M_impl.view();
+	if (__s.empty())
+	  return true;
+
+	const lock_guard<mutex> __l(*_M_mtx);
+	auto __xsz = _M_wrapped->sputn(__s.data(), __s.size());
+
+	if (__xsz != __s.size())
+	  return false;
+
+	if (_M_needs_sync)
+	  {
+	    _M_needs_sync = false;
+	    if (_M_wrapped->pubsync() != 0)
+	      return false;
+	  }
+	_M_impl.str("");
+	return true;
+      }
+
+      streambuf_type*
+      get_wrapped() const noexcept
+      { return _M_wrapped; }
+
+      allocator_type get_allocator() const noexcept
+      { return _M_impl.get_allocator(); }
+
+      void
+      set_emit_on_sync(bool __b) noexcept
+      { _M_emit_on_sync = __b; }
+
+    protected:
+      int
+      sync() override
+      {
+	auto __res = _M_impl.pubsync();
+	if (__res == 0)
+	  {
+	    _M_needs_sync = true;
+	    if (_M_emit_on_sync)
+	      return emit() ? 0 : -1;
+	  }
+	return __res;
+      }
+
+      streamsize
+      xsputn(const char_type* __s, streamsize __n) override
+      { return _M_impl.sputn(__s, __n); }
+
+    private:
+      streambuf_type* _M_wrapped;
+
+      using __impl_type = basic_stringbuf<char_type, traits_type,
+					  allocator_type>;
+      __impl_type _M_impl;
+      mutex* _M_mtx;
+
+      bool _M_emit_on_sync = false;
+      bool _M_needs_sync = false;
+
+      // FIXME: This should be put in the .so
+      static mutex&
+      _S_get_mutex(void* __t)
+      {
+	const unsigned char __mask = 0xf;
+	static mutex __m[__mask + 1];
+
+	auto __key = _Hash_impl::hash(__t) & __mask;
+	return __m[__key];
+      }
+    };
+
+  template <typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_osyncstream : public basic_ostream<_CharT, _Traits>
+    {
+      using __ostream_type = basic_ostream<_CharT, _Traits>;
+
+    public:
+      // Types:
+      using char_type = _CharT;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using int_type = typename traits_type::int_type;
+      using pos_type = typename traits_type::pos_type;
+      using off_type = typename traits_type::off_type;
+      using syncbuf_type = basic_syncbuf<_CharT, _Traits, _Alloc>;
+      using streambuf_type = typename syncbuf_type::streambuf_type;
+
+    private:
+      syncbuf_type _M_syncbuf;
+
+    public:
+      basic_osyncstream(streambuf_type* __buf, const allocator_type& __a)
+	: _M_syncbuf(__buf, __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(streambuf_type* __buf)
+	: _M_syncbuf(__buf)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_ostream<char_type, traits_type>& __os,
+		        const allocator_type& __a)
+	: basic_osyncstream(__os.rdbuf(), __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
+	: basic_osyncstream(__os.rdbuf())
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_osyncstream&& __rhs) noexcept
+	: __ostream_type(std::move(__rhs)),
+	_M_syncbuf(std::move(__rhs._M_syncbuf))
+      { __ostream_type::set_rdbuf(std::__addressof(_M_syncbuf)); }
+
+      ~basic_osyncstream() = default;
+
+      basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default;
+
+      syncbuf_type* rdbuf() const noexcept
+      { return const_cast<syncbuf_type*>(&_M_syncbuf); }
+
+      streambuf_type* get_wrapped() const noexcept
+      { return _M_syncbuf.get_wrapped(); }
+
+      void emit()
+      { _M_syncbuf.emit(); }
+    };
+
+  template <class _CharT, class _Traits, class _Allocator>
+    inline void
+    swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __x,
+	 basic_syncbuf<_CharT, _Traits, _Allocator>& __y) noexcept
+    { __x.swap(__y); }
+
+  using syncbuf = basic_syncbuf<char>;
+  using wsyncbuf = basic_syncbuf<wchar_t>;
+
+  using osyncstream = basic_osyncstream<char>;
+  using wosyncstream = basic_osyncstream<wchar_t>;
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // C++2a
+#endif	/* _GLIBCXX_SYNCSTREAM */
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index d5d42ed0a72..8681d70fbfa 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -215,6 +215,10 @@
 #define __cpp_lib_interpolate 201902L
 #ifdef _GLIBCXX_HAS_GTHREADS
 # define __cpp_lib_jthread 201911L
+# ifdef _GLIBCXX_USE_CXX11_ABI
+// Only supported with cx11-abi
+#  define __cpp_lib_syncbuf 201803L
+# endif
 #endif
 #define __cpp_lib_list_remove_return_type 201806L
 #define __cpp_lib_math_constants 201907L
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
new file mode 100644
index 00000000000..ebea9becdb3
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
new file mode 100644
index 00000000000..43538f89fb8
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.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 <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
new file mode 100644
index 00000000000..98e00dea1f1
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
@@ -0,0 +1,138 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::syncbuf s1;
+    VERIFY( s1.get_wrapped() == nullptr );
+
+    std::stringbuf b;
+    std::syncbuf s2(&b);
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+
+    sbuf_t b;
+
+    alloc_type aa;
+    sbuf_t s1(&b, aa);
+    VERIFY( aa == s1.get_allocator() );
+
+    alloc_type aaa(42);
+    sbuf_t s2(&b, aaa);
+    VERIFY( aaa == s2.get_allocator() );
+
+    VERIFY( s1.get_allocator() != s2.get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2;
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b;
+  std::syncbuf s1(&b);
+
+  std::syncbuf s2;
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == nullptr );
+  VERIFY( s2.get_wrapped() == &b );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( b.str() != txt );
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() != txt );
+
+    VERIFY( s.emit() );
+    VERIFY( b.str() == txt );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+    s.set_emit_on_sync(true);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() == txt );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
new file mode 100644
index 00000000000..49266b4abf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
@@ -0,0 +1,42 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
new file mode 100644
index 00000000000..097da110178
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
@@ -0,0 +1,130 @@
+// 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 -pthread -Wl,--no-demangle" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <syncstream>
+#include <thread>
+#include <vector>
+#include <unordered_map>
+#include <utility>
+
+#include <testsuite_hooks.h>
+
+int
+main()
+{
+  using namespace std::chrono_literals;
+
+  std::stringbuf b;
+  std::atomic<unsigned> running(0);
+
+  auto const cstr = "This is a test";
+
+  constexpr int ct = 1000;
+  auto const body = [&]{
+    ++running;
+    auto tid = std::this_thread::get_id();
+    std::syncbuf s(&b);
+    for (auto i = 0; i < ct; ++i)
+    {
+      std::stringstream stm;
+      stm << tid << ' ' << cstr << ' ' << i << std::endl;
+      auto sv = stm.view();
+      s.sputn(sv.data(), sv.size());
+      VERIFY( s.emit() );
+    }
+  };
+
+  const auto tct = 8;
+  std::vector<std::thread> ts;
+  ts.reserve(tct);
+
+  for (auto i = 0; i < tct; ++i)
+    ts.emplace_back(std::thread(body));
+
+  do
+  {
+    std::this_thread::sleep_for(100ms);
+  }
+  while (running.load() < tct);
+
+  std::unordered_map<std::string, int> tids;
+  for (auto&& t : ts)
+  {
+    std::stringstream stm;
+    stm << t.get_id();
+    tids.emplace(std::make_pair(stm.str(), 0));
+  };
+
+  for (auto&& t : ts)
+     t.join();
+
+  std::vector<std::string_view> lines;
+  const auto lct = ct * ts.size();
+  lines.reserve(lct);
+
+  std::size_t last = 0;
+  auto sv = b.view();
+  auto p = sv.find('\n');
+  while (p != std::string_view::npos)
+  {
+    lines.emplace_back(sv.substr(last, p - last));
+    last = p+1;
+    p = sv.find('\n', last);
+  }
+  VERIFY( lines.size() == lct );
+
+  auto sep = "";
+  auto i = 0;
+  sv = std::string_view(cstr);
+
+  for (auto&& l : lines)
+  {
+    auto p = l.find(' ');
+    VERIFY( p != std::string_view::npos );
+    std::string tid(l.substr(0, p));
+    ++p;
+
+    VERIFY( l.substr(p, sv.size()) == sv );
+    std::string s(l.substr(++p + sv.size()));
+    std::stringstream stm(s);
+    int n;
+    stm >> n;
+    VERIFY( stm.eof() );
+    VERIFY( n >= 0 && n < ct );
+    auto it = tids.find(tid);
+    VERIFY( it != std::end(tids) );
+    ++(it->second);
+  }
+
+  for (auto const& t : tids)
+  {
+    VERIFY( t.second == ct );
+  }
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
new file mode 100644
index 00000000000..ebea9becdb3
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
new file mode 100644
index 00000000000..43538f89fb8
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.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 <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
new file mode 100644
index 00000000000..e2cb6404cbe
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
@@ -0,0 +1,135 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+    VERIFY( s.rdbuf() != nullptr );
+    VERIFY( s.get_wrapped() == &b );
+  }
+
+  {
+    std::ostringstream stm;
+    std::osyncstream s(stm);
+    VERIFY( s.get_wrapped() == stm.rdbuf() );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+    using stream_t = std::basic_osyncstream<char, std::char_traits<char>,
+					    alloc_type>;
+    using str_t = std::basic_ostringstream<char, std::char_traits<char>,
+                                           alloc_type>;
+    sbuf_t b;
+
+    alloc_type aa;
+    stream_t s1(&b, aa);
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+
+    alloc_type aaa(42);
+    stream_t s2(&b, aaa);
+    VERIFY( aaa == s2.rdbuf()->get_allocator() );
+
+    VERIFY( s1.rdbuf()->get_allocator() != s2.rdbuf()->get_allocator() );
+
+    str_t stm;
+    stream_t s3(stm, aa);
+    VERIFY( s3.get_wrapped() == stm.rdbuf() );
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s1(&b);
+
+    std::osyncstream s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b1;
+    std::osyncstream s1(&b1);
+
+    std::stringbuf b2;
+    std::osyncstream s2(&b2);
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b1 );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b1;
+  std::osyncstream s1(&b1);
+  
+  std::stringbuf b2;
+  std::osyncstream s2(&b2);
+
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == &b2 );
+  VERIFY( s2.get_wrapped() == &b1 );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+
+    const std::string_view txt("This is a test");
+    s << txt;
+
+    s.emit();
+    VERIFY( b.str() == txt );
+  }
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
new file mode 100644
index 00000000000..dfc0b72efcd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
@@ -0,0 +1,43 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+    using syncbuf_type = test_type::syncbuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
-- 
2.26.2


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

* [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-15 19:29 ` Jonathan Wakely
  2020-10-16  0:24   ` Thomas Rodgers
@ 2020-10-21 16:53   ` Thomas Rodgers
  2020-10-21 17:34     ` Jonathan Wakely
  2020-10-29 16:18     ` Jonathan Wakely
  1 sibling, 2 replies; 16+ messages in thread
From: Thomas Rodgers @ 2020-10-21 16:53 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

libstdc++/Changelog:
	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
	libstdc++-v3/include/Makefile.in: Regenerate.
	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
	libstdc++-v3/include/std/streambuf
        (__detail::__streambuf_core_access): Define.
        (basic_streambuf): Befriend __detail::__streambuf_core_access.
	libstdc++-v3/include/std/syncstream: New header.
	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
        Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
 libstdc++-v3/include/std/syncstream           | 279 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   4 +
 .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
 .../testsuite/27_io/basic_syncbuf/2.cc        |  27 ++
 .../27_io/basic_syncbuf/basic_ops/1.cc        | 138 +++++++++
 .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
 .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 ++++++++
 .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
 .../testsuite/27_io/basic_syncstream/2.cc     |  27 ++
 .../27_io/basic_syncstream/basic_ops/1.cc     | 135 +++++++++
 .../basic_syncstream/requirements/types.cc    |  43 +++
 15 files changed, 885 insertions(+), 1 deletion(-)
 create mode 100644 libstdc++-v3/include/std/syncstream
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc

diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 9b49a15d31b..320f6dea688 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
                          include/streambuf \
                          include/string \
                          include/string_view \
+                         include/syncstream \
                          include/system_error \
                          include/thread \
                          include/tuple \
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 28d273924ee..61aaff7a2f4 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -73,6 +73,7 @@ std_headers = \
 	${std_srcdir}/shared_mutex \
 	${std_srcdir}/span \
 	${std_srcdir}/sstream \
+	${std_srcdir}/syncstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
 	${std_srcdir}/stop_token \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index 7518a98c25a..8899c323a28 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -141,6 +141,6 @@
 #include <ranges>
 #include <span>
 #include <stop_token>
-// #include <syncstream>
+#include <syncstream>
 #include <version>
 #endif
diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
new file mode 100644
index 00000000000..3f78cef1d8d
--- /dev/null
+++ b/libstdc++-v3/include/std/syncstream
@@ -0,0 +1,279 @@
+// <syncstream> -*- 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/syncstream
+ *  This is a Standard C++ Library header.
+ */
+
+ #ifndef _GLIBCXX_SYNCSTREAM
+ #define _GLIBCXX_SYNCSTREAM 1
+
+#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI
+
+#define __cpp_lib_syncbuf 201803L
+
+#pragma GCC system_header
+
+#include <sstream>
+
+#include <bits/alloc_traits.h>
+#include <bits/allocator.h>
+#include <bits/functexcept.h>
+#include <bits/functional_hash.h>
+#include <bits/std_mutex.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
+    {
+    public:
+      using char_type = _CharT;
+      using int_type = typename _Traits::int_type;
+      using pos_type = typename _Traits::pos_type;
+      using off_type = typename _Traits::off_type;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
+
+      basic_syncbuf()
+      : basic_syncbuf(nullptr, allocator_type{})
+      { }
+
+      explicit
+      basic_syncbuf(streambuf_type* __obuf)
+	: basic_syncbuf(__obuf, allocator_type{})
+      { }
+
+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
+	: _M_wrapped(__obuf)
+	, _M_impl(__alloc)
+	, _M_mtx(__obuf ? &_S_get_mutex(__obuf) : nullptr)
+      { }
+
+      basic_syncbuf(basic_syncbuf&& __other)
+	: _M_wrapped(__other._M_wrapped)
+	, _M_impl(std::move(__other._M_impl))
+	, _M_mtx(__other._M_mtx)
+	, _M_emit_on_sync(__other._M_emit_on_sync)
+	, _M_needs_sync(__other._M_needs_sync)
+      {
+	__other._M_wrapped = nullptr;
+      }
+
+      ~basic_syncbuf()
+      {
+	__try
+	  {
+	    emit();
+	  }
+	__catch (...)
+	  { }
+      }
+
+      basic_syncbuf& operator=(basic_syncbuf&& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    emit();
+
+	    _M_impl = std::move(__other._M_impl);
+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
+	    _M_mtx = __other._M_mtx;
+	    _M_emit_on_sync = __other._M_emit_on_sync;
+	    _M_needs_sync = __other._M_needs_sync;
+	  }
+	return *this;
+      }
+
+      void
+      swap(basic_syncbuf& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    std::swap(_M_impl, __other._M_impl);
+	    std::swap(_M_wrapped, __other._M_wrapped);
+	    std::swap(_M_mtx, __other._M_mtx);
+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
+	    std::swap(_M_needs_sync, __other._M_needs_sync);
+	  }
+      }
+
+      bool
+      emit()
+      {
+	if (!_M_wrapped)
+	  return false;
+
+	auto __s = _M_impl.view();
+	if (__s.empty())
+	  return true;
+
+	const lock_guard<mutex> __l(*_M_mtx);
+	auto __xsz = _M_wrapped->sputn(__s.data(), __s.size());
+
+	if (__xsz != __s.size())
+	  return false;
+
+	if (_M_needs_sync)
+	  {
+	    _M_needs_sync = false;
+	    if (_M_wrapped->pubsync() != 0)
+	      return false;
+	  }
+	_M_impl.str("");
+	return true;
+      }
+
+      streambuf_type*
+      get_wrapped() const noexcept
+      { return _M_wrapped; }
+
+      allocator_type get_allocator() const noexcept
+      { return _M_impl.get_allocator(); }
+
+      void
+      set_emit_on_sync(bool __b) noexcept
+      { _M_emit_on_sync = __b; }
+
+    protected:
+      int
+      sync() override
+      {
+	auto __res = _M_impl.pubsync();
+	if (__res == 0)
+	  {
+	    _M_needs_sync = true;
+	    if (_M_emit_on_sync)
+	      return emit() ? 0 : -1;
+	  }
+	return __res;
+      }
+
+      streamsize
+      xsputn(const char_type* __s, streamsize __n) override
+      { return _M_impl.sputn(__s, __n); }
+
+    private:
+      streambuf_type* _M_wrapped;
+
+      using __impl_type = basic_stringbuf<char_type, traits_type,
+					  allocator_type>;
+      __impl_type _M_impl;
+      mutex* _M_mtx;
+
+      bool _M_emit_on_sync = false;
+      bool _M_needs_sync = false;
+
+      // FIXME: This should be put in the .so
+      static mutex&
+      _S_get_mutex(void* __t)
+      {
+	const unsigned char __mask = 0xf;
+	static mutex __m[__mask + 1];
+
+	auto __key = _Hash_impl::hash(__t) & __mask;
+	return __m[__key];
+      }
+    };
+
+  template <typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_osyncstream : public basic_ostream<_CharT, _Traits>
+    {
+      using __ostream_type = basic_ostream<_CharT, _Traits>;
+
+    public:
+      // Types:
+      using char_type = _CharT;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using int_type = typename traits_type::int_type;
+      using pos_type = typename traits_type::pos_type;
+      using off_type = typename traits_type::off_type;
+      using syncbuf_type = basic_syncbuf<_CharT, _Traits, _Alloc>;
+      using streambuf_type = typename syncbuf_type::streambuf_type;
+
+    private:
+      syncbuf_type _M_syncbuf;
+
+    public:
+      basic_osyncstream(streambuf_type* __buf, const allocator_type& __a)
+	: _M_syncbuf(__buf, __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(streambuf_type* __buf)
+	: _M_syncbuf(__buf)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_ostream<char_type, traits_type>& __os,
+		        const allocator_type& __a)
+	: basic_osyncstream(__os.rdbuf(), __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
+	: basic_osyncstream(__os.rdbuf())
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_osyncstream&& __rhs) noexcept
+	: __ostream_type(std::move(__rhs)),
+	_M_syncbuf(std::move(__rhs._M_syncbuf))
+      { __ostream_type::set_rdbuf(std::__addressof(_M_syncbuf)); }
+
+      ~basic_osyncstream() = default;
+
+      basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default;
+
+      syncbuf_type* rdbuf() const noexcept
+      { return const_cast<syncbuf_type*>(&_M_syncbuf); }
+
+      streambuf_type* get_wrapped() const noexcept
+      { return _M_syncbuf.get_wrapped(); }
+
+      void emit()
+      {
+	if (!_M_syncbuf.emit())
+	  this->setstate(ios_base::failbit);
+      }
+    };
+
+  template <class _CharT, class _Traits, class _Allocator>
+    inline void
+    swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __x,
+	 basic_syncbuf<_CharT, _Traits, _Allocator>& __y) noexcept
+    { __x.swap(__y); }
+
+  using syncbuf = basic_syncbuf<char>;
+  using wsyncbuf = basic_syncbuf<wchar_t>;
+
+  using osyncstream = basic_osyncstream<char>;
+  using wosyncstream = basic_osyncstream<wchar_t>;
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // C++2a
+#endif	/* _GLIBCXX_SYNCSTREAM */
+
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index d5d42ed0a72..5542d51087e 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -226,6 +226,10 @@
 #define __cpp_lib_span 202002L
 #define __cpp_lib_ssize 201902L
 #define __cpp_lib_starts_ends_with 201711L
+# ifdef _GLIBCXX_USE_CXX11_ABI
+// Only supported with cx11-abi
+#  define __cpp_lib_syncbuf 201803L
+# endif
 #define __cpp_lib_to_address 201711L
 #define __cpp_lib_to_array 201907L
 #endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
new file mode 100644
index 00000000000..ebea9becdb3
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
new file mode 100644
index 00000000000..43538f89fb8
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.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 <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
new file mode 100644
index 00000000000..98e00dea1f1
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
@@ -0,0 +1,138 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::syncbuf s1;
+    VERIFY( s1.get_wrapped() == nullptr );
+
+    std::stringbuf b;
+    std::syncbuf s2(&b);
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+
+    sbuf_t b;
+
+    alloc_type aa;
+    sbuf_t s1(&b, aa);
+    VERIFY( aa == s1.get_allocator() );
+
+    alloc_type aaa(42);
+    sbuf_t s2(&b, aaa);
+    VERIFY( aaa == s2.get_allocator() );
+
+    VERIFY( s1.get_allocator() != s2.get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2;
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b;
+  std::syncbuf s1(&b);
+
+  std::syncbuf s2;
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == nullptr );
+  VERIFY( s2.get_wrapped() == &b );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( b.str() != txt );
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() != txt );
+
+    VERIFY( s.emit() );
+    VERIFY( b.str() == txt );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+    s.set_emit_on_sync(true);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() == txt );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
new file mode 100644
index 00000000000..49266b4abf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
@@ -0,0 +1,42 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
new file mode 100644
index 00000000000..097da110178
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
@@ -0,0 +1,130 @@
+// 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 -pthread -Wl,--no-demangle" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <syncstream>
+#include <thread>
+#include <vector>
+#include <unordered_map>
+#include <utility>
+
+#include <testsuite_hooks.h>
+
+int
+main()
+{
+  using namespace std::chrono_literals;
+
+  std::stringbuf b;
+  std::atomic<unsigned> running(0);
+
+  auto const cstr = "This is a test";
+
+  constexpr int ct = 1000;
+  auto const body = [&]{
+    ++running;
+    auto tid = std::this_thread::get_id();
+    std::syncbuf s(&b);
+    for (auto i = 0; i < ct; ++i)
+    {
+      std::stringstream stm;
+      stm << tid << ' ' << cstr << ' ' << i << std::endl;
+      auto sv = stm.view();
+      s.sputn(sv.data(), sv.size());
+      VERIFY( s.emit() );
+    }
+  };
+
+  const auto tct = 8;
+  std::vector<std::thread> ts;
+  ts.reserve(tct);
+
+  for (auto i = 0; i < tct; ++i)
+    ts.emplace_back(std::thread(body));
+
+  do
+  {
+    std::this_thread::sleep_for(100ms);
+  }
+  while (running.load() < tct);
+
+  std::unordered_map<std::string, int> tids;
+  for (auto&& t : ts)
+  {
+    std::stringstream stm;
+    stm << t.get_id();
+    tids.emplace(std::make_pair(stm.str(), 0));
+  };
+
+  for (auto&& t : ts)
+     t.join();
+
+  std::vector<std::string_view> lines;
+  const auto lct = ct * ts.size();
+  lines.reserve(lct);
+
+  std::size_t last = 0;
+  auto sv = b.view();
+  auto p = sv.find('\n');
+  while (p != std::string_view::npos)
+  {
+    lines.emplace_back(sv.substr(last, p - last));
+    last = p+1;
+    p = sv.find('\n', last);
+  }
+  VERIFY( lines.size() == lct );
+
+  auto sep = "";
+  auto i = 0;
+  sv = std::string_view(cstr);
+
+  for (auto&& l : lines)
+  {
+    auto p = l.find(' ');
+    VERIFY( p != std::string_view::npos );
+    std::string tid(l.substr(0, p));
+    ++p;
+
+    VERIFY( l.substr(p, sv.size()) == sv );
+    std::string s(l.substr(++p + sv.size()));
+    std::stringstream stm(s);
+    int n;
+    stm >> n;
+    VERIFY( stm.eof() );
+    VERIFY( n >= 0 && n < ct );
+    auto it = tids.find(tid);
+    VERIFY( it != std::end(tids) );
+    ++(it->second);
+  }
+
+  for (auto const& t : tids)
+  {
+    VERIFY( t.second == ct );
+  }
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
new file mode 100644
index 00000000000..ebea9becdb3
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
new file mode 100644
index 00000000000..43538f89fb8
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.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 <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf!= 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
new file mode 100644
index 00000000000..e2cb6404cbe
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
@@ -0,0 +1,135 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+    VERIFY( s.rdbuf() != nullptr );
+    VERIFY( s.get_wrapped() == &b );
+  }
+
+  {
+    std::ostringstream stm;
+    std::osyncstream s(stm);
+    VERIFY( s.get_wrapped() == stm.rdbuf() );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+    using stream_t = std::basic_osyncstream<char, std::char_traits<char>,
+					    alloc_type>;
+    using str_t = std::basic_ostringstream<char, std::char_traits<char>,
+                                           alloc_type>;
+    sbuf_t b;
+
+    alloc_type aa;
+    stream_t s1(&b, aa);
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+
+    alloc_type aaa(42);
+    stream_t s2(&b, aaa);
+    VERIFY( aaa == s2.rdbuf()->get_allocator() );
+
+    VERIFY( s1.rdbuf()->get_allocator() != s2.rdbuf()->get_allocator() );
+
+    str_t stm;
+    stream_t s3(stm, aa);
+    VERIFY( s3.get_wrapped() == stm.rdbuf() );
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s1(&b);
+
+    std::osyncstream s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b1;
+    std::osyncstream s1(&b1);
+
+    std::stringbuf b2;
+    std::osyncstream s2(&b2);
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b1 );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b1;
+  std::osyncstream s1(&b1);
+  
+  std::stringbuf b2;
+  std::osyncstream s2(&b2);
+
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == &b2 );
+  VERIFY( s2.get_wrapped() == &b1 );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+
+    const std::string_view txt("This is a test");
+    s << txt;
+
+    s.emit();
+    VERIFY( b.str() == txt );
+  }
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
new file mode 100644
index 00000000000..dfc0b72efcd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
@@ -0,0 +1,43 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+    using syncbuf_type = test_type::syncbuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
-- 
2.26.2


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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-21 16:53   ` Thomas Rodgers
@ 2020-10-21 17:34     ` Jonathan Wakely
  2020-10-21 19:39       ` Thomas Rodgers
  2020-10-29 16:18     ` Jonathan Wakely
  1 sibling, 1 reply; 16+ messages in thread
From: Jonathan Wakely @ 2020-10-21 17:34 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 21/10/20 09:53 -0700, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>libstdc++/Changelog:
>	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
>	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
>	libstdc++-v3/include/Makefile.in: Regenerate.
>	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
>	libstdc++-v3/include/std/streambuf
>        (__detail::__streambuf_core_access): Define.
>        (basic_streambuf): Befriend __detail::__streambuf_core_access.

This file is no longer part of the commit, so the server will reject
this changelog. Please ensure the changelog is accurate (the
gcc-verify alias created by contrib/gcc-git-customization.sh can do
that) and push, thanks.


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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-21 17:34     ` Jonathan Wakely
@ 2020-10-21 19:39       ` Thomas Rodgers
  0 siblings, 0 replies; 16+ messages in thread
From: Thomas Rodgers @ 2020-10-21 19:39 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: gcc-patches List, libstdc++, Thomas Rodgers



> On Oct 21, 2020, at 10:34 AM, Jonathan Wakely <jwakely@redhat.com> wrote:
> 
> On 21/10/20 09:53 -0700, Thomas Rodgers wrote:
>> From: Thomas Rodgers <trodgers@redhat.com>
>> 
>> libstdc++/Changelog:
>> 	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
>> 	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
>> 	libstdc++-v3/include/Makefile.in: Regenerate.
>> 	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
>> 	libstdc++-v3/include/std/streambuf
>>       (__detail::__streambuf_core_access): Define.
>>       (basic_streambuf): Befriend __detail::__streambuf_core_access.
> 
> This file is no longer part of the commit, so the server will reject
> this changelog. Please ensure the changelog is accurate (the
> gcc-verify alias created by contrib/gcc-git-customization.sh can do
> that) and push, thanks.
> 

This patch is dependent on the changes to <sstream> so I can’t push until that patch lands.

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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-21 16:53   ` Thomas Rodgers
  2020-10-21 17:34     ` Jonathan Wakely
@ 2020-10-29 16:18     ` Jonathan Wakely
  2020-10-29 21:12       ` Thomas Rodgers
  1 sibling, 1 reply; 16+ messages in thread
From: Jonathan Wakely @ 2020-10-29 16:18 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 21/10/20 09:53 -0700, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>libstdc++/Changelog:
>	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
>	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
>	libstdc++-v3/include/Makefile.in: Regenerate.
>	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
>	libstdc++-v3/include/std/streambuf
>        (__detail::__streambuf_core_access): Define.
>        (basic_streambuf): Befriend __detail::__streambuf_core_access.
>	libstdc++-v3/include/std/syncstream: New header.
>	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
>        Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
> libstdc++-v3/include/std/syncstream           | 279 ++++++++++++++++++
> libstdc++-v3/include/std/version              |   4 +
> .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
> .../testsuite/27_io/basic_syncbuf/2.cc        |  27 ++
> .../27_io/basic_syncbuf/basic_ops/1.cc        | 138 +++++++++
> .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
> .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 ++++++++
> .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
> .../testsuite/27_io/basic_syncstream/2.cc     |  27 ++
> .../27_io/basic_syncstream/basic_ops/1.cc     | 135 +++++++++
> .../basic_syncstream/requirements/types.cc    |  43 +++
> 15 files changed, 885 insertions(+), 1 deletion(-)
> create mode 100644 libstdc++-v3/include/std/syncstream
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
>
>diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
>index 9b49a15d31b..320f6dea688 100644
>--- a/libstdc++-v3/doc/doxygen/user.cfg.in
>+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
>@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
>                          include/streambuf \
>                          include/string \
>                          include/string_view \
>+                         include/syncstream \
>                          include/system_error \
>                          include/thread \
>                          include/tuple \
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index 28d273924ee..61aaff7a2f4 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -73,6 +73,7 @@ std_headers = \
> 	${std_srcdir}/shared_mutex \
> 	${std_srcdir}/span \
> 	${std_srcdir}/sstream \
>+	${std_srcdir}/syncstream \
> 	${std_srcdir}/stack \
> 	${std_srcdir}/stdexcept \
> 	${std_srcdir}/stop_token \
>diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
>index 7518a98c25a..8899c323a28 100644
>--- a/libstdc++-v3/include/precompiled/stdc++.h
>+++ b/libstdc++-v3/include/precompiled/stdc++.h
>@@ -141,6 +141,6 @@
> #include <ranges>
> #include <span>
> #include <stop_token>
>-// #include <syncstream>
>+#include <syncstream>
> #include <version>
> #endif
>diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
>new file mode 100644
>index 00000000000..3f78cef1d8d
>--- /dev/null
>+++ b/libstdc++-v3/include/std/syncstream
>@@ -0,0 +1,279 @@
>+// <syncstream> -*- 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.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file include/syncstream
>+ *  This is a Standard C++ Library header.
>+ */
>+
>+ #ifndef _GLIBCXX_SYNCSTREAM
>+ #define _GLIBCXX_SYNCSTREAM 1

These two lines are indented one column, they shouldn't be.

>+
>+#if __cplusplus > 201703L && _GLIBCXX_USE_CXX11_ABI

The _GLIBCXX_USE_CXX11_ABI check needs to come after including at
least one libstdc++ header, because otherwise it's not been set yet.
It gets set to a default value in <bits/c++config.h> so if you check
it before that (or any other header that includes it) has been
included, then it won't be defined.

As discussed, either the entire header needs to be guarded with
#ifdef _GLIBCXX_HAS_GTHREADS or the uses of the mutex need to be
guarded by that (making it usable without threads, even though it does
no synchronization).

>diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
>index d5d42ed0a72..5542d51087e 100644
>--- a/libstdc++-v3/include/std/version
>+++ b/libstdc++-v3/include/std/version
>@@ -226,6 +226,10 @@
> #define __cpp_lib_span 202002L
> #define __cpp_lib_ssize 201902L
> #define __cpp_lib_starts_ends_with 201711L
>+# ifdef _GLIBCXX_USE_CXX11_ABI

This should be #if not #ifdef, otherwise the  condition is true for
_GLIBCXX_USE_CXX11_ABI=0 as wekk as _GLIBCXX_USE_CXX11_ABI=1

>+// Only supported with cx11-abi
>+#  define __cpp_lib_syncbuf 201803L
>+# endif
> #define __cpp_lib_to_address 201711L
> #define __cpp_lib_to_array 201907L
> #endif

Also several of the tests still do:

#elif __cpp_lib_syncbuf!= 201803L

with no space before the operator.


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

* [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-29 16:18     ` Jonathan Wakely
@ 2020-10-29 21:12       ` Thomas Rodgers
  2020-11-02 15:58         ` Thomas Rodgers
  2020-11-02 16:10         ` Thomas Rodgers
  0 siblings, 2 replies; 16+ messages in thread
From: Thomas Rodgers @ 2020-10-29 21:12 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

Addresses latest patch feedback. Changes <syncstream> to also work on
single threaded configurations.

libstdc++/ChangeLog:
	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
	libstdc++-v3/include/Makefile.in: Regenerate.
	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
	(basic_streambuf): Befriend __detail::__streambuf_core_access.
	libstdc++-v3/include/std/syncstream: New header.
	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
 libstdc++-v3/include/std/syncstream           | 328 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   4 +
 .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
 .../testsuite/27_io/basic_syncbuf/2.cc        |  28 ++
 .../27_io/basic_syncbuf/basic_ops/1.cc        | 137 ++++++++
 .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
 .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
 .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
 .../testsuite/27_io/basic_syncstream/2.cc     |  28 ++
 .../27_io/basic_syncstream/basic_ops/1.cc     | 134 +++++++
 .../basic_syncstream/requirements/types.cc    |  43 +++
 15 files changed, 934 insertions(+), 1 deletion(-)
 create mode 100644 libstdc++-v3/include/std/syncstream
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc

diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 9b49a15d31b..320f6dea688 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
                          include/streambuf \
                          include/string \
                          include/string_view \
+                         include/syncstream \
                          include/system_error \
                          include/thread \
                          include/tuple \
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index c90ac555e15..8652b921274 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -73,6 +73,7 @@ std_headers = \
 	${std_srcdir}/shared_mutex \
 	${std_srcdir}/span \
 	${std_srcdir}/sstream \
+	${std_srcdir}/syncstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
 	${std_srcdir}/stop_token \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index 7518a98c25a..8899c323a28 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -141,6 +141,6 @@
 #include <ranges>
 #include <span>
 #include <stop_token>
-// #include <syncstream>
+#include <syncstream>
 #include <version>
 #endif
diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
new file mode 100644
index 00000000000..88452c2ed10
--- /dev/null
+++ b/libstdc++-v3/include/std/syncstream
@@ -0,0 +1,328 @@
+// <syncstream> -*- 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/syncstream
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_SYNCSTREAM
+#define _GLIBCXX_SYNCSTREAM 1
+
+#if __cplusplus > 201703L 
+
+#include <bits/c++config.h>
+#if _GLIBCXX_USE_CXX11_ABI
+
+#define __cpp_lib_syncbuf 201803L
+
+#pragma GCC system_header
+
+#include <sstream>
+
+#include <bits/alloc_traits.h>
+#include <bits/allocator.h>
+#include <bits/functexcept.h>
+#include <bits/functional_hash.h>
+
+#if _GLIBCXX_HAS_GTHREADS
+# include <bits/std_mutex.h>
+#endif
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
+    {
+    public:
+      using char_type = _CharT;
+      using int_type = typename _Traits::int_type;
+      using pos_type = typename _Traits::pos_type;
+      using off_type = typename _Traits::off_type;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
+
+      basic_syncbuf()
+      : basic_syncbuf(nullptr, allocator_type{})
+      { }
+
+      explicit
+      basic_syncbuf(streambuf_type* __obuf)
+	: basic_syncbuf(__obuf, allocator_type{})
+      { }
+
+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
+	: _M_wrapped(__obuf)
+	, _M_impl(__alloc)
+	, _M_locker(__obuf)
+      { }
+
+      basic_syncbuf(basic_syncbuf&& __other)
+	: _M_wrapped(__other._M_wrapped)
+	, _M_impl(std::move(__other._M_impl))
+	, _M_locker(std::move(__other._M_locker))
+	, _M_emit_on_sync(__other._M_emit_on_sync)
+	, _M_needs_sync(__other._M_needs_sync)
+      {
+	__other._M_wrapped = nullptr;
+      }
+
+      ~basic_syncbuf()
+      {
+	__try
+	  {
+	    emit();
+	  }
+	__catch (...)
+	  { }
+      }
+
+      basic_syncbuf& operator=(basic_syncbuf&& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    emit();
+
+	    _M_impl = std::move(__other._M_impl);
+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
+	    _M_locker = std::move(__other._M_locker);
+	    _M_emit_on_sync = __other._M_emit_on_sync;
+	    _M_needs_sync = __other._M_needs_sync;
+	  }
+	return *this;
+      }
+
+      void
+      swap(basic_syncbuf& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    std::swap(_M_impl, __other._M_impl);
+	    std::swap(_M_wrapped, __other._M_wrapped);
+	    std::swap(_M_locker, __other._M_locker);
+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
+	    std::swap(_M_needs_sync, __other._M_needs_sync);
+	  }
+      }
+
+      bool
+      emit()
+      {
+	if (!_M_wrapped)
+	  return false;
+
+	auto __s = _M_impl.view();
+	if (__s.empty())
+	  return true;
+
+	auto res = _M_locker([&, this]
+	    {
+	      if (_M_wrapped->sputn(__s.data(), __s.size()) != __s.size())
+		return false;
+
+	      if (_M_needs_sync)
+		{
+		  _M_needs_sync = false;
+		  if (_M_wrapped->pubsync() != 0)
+		    return false;
+		}
+	    });
+
+	_M_impl.str("");
+	return true;
+      }
+
+      streambuf_type*
+      get_wrapped() const noexcept
+      { return _M_wrapped; }
+
+      allocator_type get_allocator() const noexcept
+      { return _M_impl.get_allocator(); }
+
+      void
+      set_emit_on_sync(bool __b) noexcept
+      { _M_emit_on_sync = __b; }
+
+    protected:
+      int
+      sync() override
+      {
+	auto __res = _M_impl.pubsync();
+	if (__res == 0)
+	  {
+	    _M_needs_sync = true;
+	    if (_M_emit_on_sync)
+	      return emit() ? 0 : -1;
+	  }
+	return __res;
+      }
+
+      streamsize
+      xsputn(const char_type* __s, streamsize __n) override
+      { return _M_impl.sputn(__s, __n); }
+
+    private:
+      streambuf_type* _M_wrapped;
+
+      using __impl_type = basic_stringbuf<char_type, traits_type,
+					  allocator_type>;
+      __impl_type _M_impl;
+
+      struct __with_lock
+      {
+#if _GLIBCXX_HAS_GTHREADS
+	mutex* _M_mtx;
+
+	__with_lock(void* __t)
+	  : _M_mtx(__t ? &_S_get_mutex(__t) : nullptr)
+	{ }
+
+	void
+	swap(__with_lock& __other) noexcept
+	{ std::swap(_M_mtx, __other._M_mtx); }
+
+	template<typename _Func>
+	  bool
+	  operator()(_Func __f)
+	  {
+	    const lock_guard<mutex> __l(*_M_mtx);
+	    return __f();
+	  }
+
+	// FIXME: This should be put in the .so
+	static mutex&
+	_S_get_mutex(void* __t)
+	{
+	  const unsigned char __mask = 0xf;
+	  static mutex __m[__mask + 1];
+
+	  auto __key = _Hash_impl::hash(__t) & __mask;
+	  return __m[__key];
+	}
+#else
+	__with_lock(void*)
+	{ }
+
+	void
+	swap(__with_lock&) noexcept
+	{ }
+
+	template<typename _Func>
+	  bool
+	  operator()(_Func && __f)
+	  {
+	    return __f();
+	  }
+#endif
+	__with_lock(const __with_lock&) = delete;
+	__with_lock& operator=(const __with_lock&) = delete;
+
+	__with_lock(__with_lock&&) = default;
+	__with_lock& operator=(__with_lock&&) = default;
+      };
+      __with_lock _M_locker;
+
+      bool _M_emit_on_sync = false;
+      bool _M_needs_sync = false;
+    };
+
+  template <typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_osyncstream : public basic_ostream<_CharT, _Traits>
+    {
+      using __ostream_type = basic_ostream<_CharT, _Traits>;
+
+    public:
+      // Types:
+      using char_type = _CharT;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using int_type = typename traits_type::int_type;
+      using pos_type = typename traits_type::pos_type;
+      using off_type = typename traits_type::off_type;
+      using syncbuf_type = basic_syncbuf<_CharT, _Traits, _Alloc>;
+      using streambuf_type = typename syncbuf_type::streambuf_type;
+
+    private:
+      syncbuf_type _M_syncbuf;
+
+    public:
+      basic_osyncstream(streambuf_type* __buf, const allocator_type& __a)
+	: _M_syncbuf(__buf, __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(streambuf_type* __buf)
+	: _M_syncbuf(__buf)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_ostream<char_type, traits_type>& __os,
+		        const allocator_type& __a)
+	: basic_osyncstream(__os.rdbuf(), __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
+	: basic_osyncstream(__os.rdbuf())
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_osyncstream&& __rhs) noexcept
+	: __ostream_type(std::move(__rhs)),
+	_M_syncbuf(std::move(__rhs._M_syncbuf))
+      { __ostream_type::set_rdbuf(std::__addressof(_M_syncbuf)); }
+
+      ~basic_osyncstream() = default;
+
+      basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default;
+
+      syncbuf_type* rdbuf() const noexcept
+      { return const_cast<syncbuf_type*>(&_M_syncbuf); }
+
+      streambuf_type* get_wrapped() const noexcept
+      { return _M_syncbuf.get_wrapped(); }
+
+      void emit()
+      {
+	if (!_M_syncbuf.emit())
+	  this->setstate(ios_base::failbit);
+      }
+    };
+
+  template <class _CharT, class _Traits, class _Allocator>
+    inline void
+    swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __x,
+	 basic_syncbuf<_CharT, _Traits, _Allocator>& __y) noexcept
+    { __x.swap(__y); }
+
+  using syncbuf = basic_syncbuf<char>;
+  using wsyncbuf = basic_syncbuf<wchar_t>;
+
+  using osyncstream = basic_osyncstream<char>;
+  using wosyncstream = basic_osyncstream<wchar_t>;
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // _GLIBCXX_USE_CXX11_ABI
+#endif // C++2a
+#endif	/* _GLIBCXX_SYNCSTREAM */
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index ebb50a04d24..7f51ef3a6c4 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -229,6 +229,10 @@
 #define __cpp_lib_span 202002L
 #define __cpp_lib_ssize 201902L
 #define __cpp_lib_starts_ends_with 201711L
+# if _GLIBCXX_USE_CXX11_ABI
+// Only supported with cx11-abi
+#  define __cpp_lib_syncbuf 201803L
+# endif
 #define __cpp_lib_to_address 201711L
 #define __cpp_lib_to_array 201907L
 #endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
new file mode 100644
index 00000000000..1b3ae9330bb
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
new file mode 100644
index 00000000000..4ffcb0410fd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
new file mode 100644
index 00000000000..d5062bab70b
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
@@ -0,0 +1,137 @@
+// 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-additional-options "-pthread" { target pthread } }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::syncbuf s1;
+    VERIFY( s1.get_wrapped() == nullptr );
+
+    std::stringbuf b;
+    std::syncbuf s2(&b);
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+
+    sbuf_t b;
+
+    alloc_type aa;
+    sbuf_t s1(&b, aa);
+    VERIFY( aa == s1.get_allocator() );
+
+    alloc_type aaa(42);
+    sbuf_t s2(&b, aaa);
+    VERIFY( aaa == s2.get_allocator() );
+
+    VERIFY( s1.get_allocator() != s2.get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2;
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b;
+  std::syncbuf s1(&b);
+
+  std::syncbuf s2;
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == nullptr );
+  VERIFY( s2.get_wrapped() == &b );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( b.str() != txt );
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() != txt );
+
+    VERIFY( s.emit() );
+    VERIFY( b.str() == txt );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+    s.set_emit_on_sync(true);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() == txt );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
new file mode 100644
index 00000000000..49266b4abf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
@@ -0,0 +1,42 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
new file mode 100644
index 00000000000..90d20f1a607
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
@@ -0,0 +1,130 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <syncstream>
+#include <thread>
+#include <vector>
+#include <unordered_map>
+#include <utility>
+
+#include <testsuite_hooks.h>
+
+int
+main()
+{
+  using namespace std::chrono_literals;
+
+  std::stringbuf b;
+  std::atomic<unsigned> running(0);
+
+  auto const cstr = "This is a test";
+
+  constexpr int ct = 1000;
+  auto const body = [&]{
+    ++running;
+    auto tid = std::this_thread::get_id();
+    std::syncbuf s(&b);
+    for (auto i = 0; i < ct; ++i)
+    {
+      std::stringstream stm;
+      stm << tid << ' ' << cstr << ' ' << i << std::endl;
+      auto sv = stm.view();
+      s.sputn(sv.data(), sv.size());
+      VERIFY( s.emit() );
+    }
+  };
+
+  const auto tct = 8;
+  std::vector<std::thread> ts;
+  ts.reserve(tct);
+
+  for (auto i = 0; i < tct; ++i)
+    ts.emplace_back(std::thread(body));
+
+  do
+  {
+    std::this_thread::sleep_for(100ms);
+  }
+  while (running.load() < tct);
+
+  std::unordered_map<std::string, int> tids;
+  for (auto&& t : ts)
+  {
+    std::stringstream stm;
+    stm << t.get_id();
+    tids.emplace(std::make_pair(stm.str(), 0));
+  };
+
+  for (auto&& t : ts)
+     t.join();
+
+  std::vector<std::string_view> lines;
+  const auto lct = ct * ts.size();
+  lines.reserve(lct);
+
+  std::size_t last = 0;
+  auto sv = b.view();
+  auto p = sv.find('\n');
+  while (p != std::string_view::npos)
+  {
+    lines.emplace_back(sv.substr(last, p - last));
+    last = p+1;
+    p = sv.find('\n', last);
+  }
+  VERIFY( lines.size() == lct );
+
+  auto sep = "";
+  auto i = 0;
+  sv = std::string_view(cstr);
+
+  for (auto&& l : lines)
+  {
+    auto p = l.find(' ');
+    VERIFY( p != std::string_view::npos );
+    std::string tid(l.substr(0, p));
+    ++p;
+
+    VERIFY( l.substr(p, sv.size()) == sv );
+    std::string s(l.substr(++p + sv.size()));
+    std::stringstream stm(s);
+    int n;
+    stm >> n;
+    VERIFY( stm.eof() );
+    VERIFY( n >= 0 && n < ct );
+    auto it = tids.find(tid);
+    VERIFY( it != std::end(tids) );
+    ++(it->second);
+  }
+
+  for (auto const& t : tids)
+  {
+    VERIFY( t.second == ct );
+  }
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
new file mode 100644
index 00000000000..1b3ae9330bb
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
new file mode 100644
index 00000000000..4ffcb0410fd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
new file mode 100644
index 00000000000..a40cd162fbd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
@@ -0,0 +1,134 @@
+// 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-additional-options "-pthread" { target pthread } }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+    VERIFY( s.rdbuf() != nullptr );
+    VERIFY( s.get_wrapped() == &b );
+  }
+
+  {
+    std::ostringstream stm;
+    std::osyncstream s(stm);
+    VERIFY( s.get_wrapped() == stm.rdbuf() );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+    using stream_t = std::basic_osyncstream<char, std::char_traits<char>,
+					    alloc_type>;
+    using str_t = std::basic_ostringstream<char, std::char_traits<char>,
+                                      alloc_type>;
+    sbuf_t b;
+
+    alloc_type aa;
+    stream_t s1(&b, aa);
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+
+    alloc_type aaa(42);
+    stream_t s2(&b, aaa);
+    VERIFY( aaa == s2.rdbuf()->get_allocator() );
+
+    VERIFY( s1.rdbuf()->get_allocator() != s2.rdbuf()->get_allocator() );
+
+    str_t stm;
+    stream_t s3(stm, aa);
+    VERIFY( s3.get_wrapped() == stm.rdbuf() );
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s1(&b);
+
+    std::osyncstream s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b1;
+    std::osyncstream s1(&b1);
+
+    std::stringbuf b2;
+    std::osyncstream s2(&b2);
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b1 );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b1;
+  std::osyncstream s1(&b1);
+
+  std::stringbuf b2;
+  std::osyncstream s2(&b2);
+
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == &b2 );
+  VERIFY( s2.get_wrapped() == &b1 );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+
+    const std::string_view txt("This is a test");
+    s << txt;
+
+    s.emit();
+    VERIFY( b.str() == txt );
+  }
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
new file mode 100644
index 00000000000..dfc0b72efcd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
@@ -0,0 +1,43 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+    using syncbuf_type = test_type::syncbuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
-- 
2.26.2


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

* [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-29 21:12       ` Thomas Rodgers
@ 2020-11-02 15:58         ` Thomas Rodgers
  2020-11-02 16:10         ` Thomas Rodgers
  1 sibling, 0 replies; 16+ messages in thread
From: Thomas Rodgers @ 2020-11-02 15:58 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

Changes implementation to use a private __mutex type as discussed on
IRC.

libstdc++/ChangeLog:
	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
	libstdc++-v3/include/Makefile.in: Regenerate.
	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
	(basic_streambuf): Befriend __detail::__streambuf_core_access.
	libstdc++-v3/include/std/syncstream: New header.
	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
 libstdc++-v3/include/std/syncstream           | 328 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   4 +
 .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
 .../testsuite/27_io/basic_syncbuf/2.cc        |  28 ++
 .../27_io/basic_syncbuf/basic_ops/1.cc        | 137 ++++++++
 .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
 .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
 .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
 .../testsuite/27_io/basic_syncstream/2.cc     |  28 ++
 .../27_io/basic_syncstream/basic_ops/1.cc     | 134 +++++++
 .../basic_syncstream/requirements/types.cc    |  43 +++
 15 files changed, 934 insertions(+), 1 deletion(-)
 create mode 100644 libstdc++-v3/include/std/syncstream
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc

diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 9b49a15d31b..320f6dea688 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
                          include/streambuf \
                          include/string \
                          include/string_view \
+                         include/syncstream \
                          include/system_error \
                          include/thread \
                          include/tuple \
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index c90ac555e15..8652b921274 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -73,6 +73,7 @@ std_headers = \
 	${std_srcdir}/shared_mutex \
 	${std_srcdir}/span \
 	${std_srcdir}/sstream \
+	${std_srcdir}/syncstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
 	${std_srcdir}/stop_token \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index 7518a98c25a..8899c323a28 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -141,6 +141,6 @@
 #include <ranges>
 #include <span>
 #include <stop_token>
-// #include <syncstream>
+#include <syncstream>
 #include <version>
 #endif
diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
new file mode 100644
index 00000000000..88452c2ed10
--- /dev/null
+++ b/libstdc++-v3/include/std/syncstream
@@ -0,0 +1,328 @@
+// <syncstream> -*- 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/syncstream
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_SYNCSTREAM
+#define _GLIBCXX_SYNCSTREAM 1
+
+#if __cplusplus > 201703L 
+
+#include <bits/c++config.h>
+#if _GLIBCXX_USE_CXX11_ABI
+
+#define __cpp_lib_syncbuf 201803L
+
+#pragma GCC system_header
+
+#include <sstream>
+
+#include <bits/alloc_traits.h>
+#include <bits/allocator.h>
+#include <bits/functexcept.h>
+#include <bits/functional_hash.h>
+
+#if _GLIBCXX_HAS_GTHREADS
+# include <bits/std_mutex.h>
+#endif
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
+    {
+    public:
+      using char_type = _CharT;
+      using int_type = typename _Traits::int_type;
+      using pos_type = typename _Traits::pos_type;
+      using off_type = typename _Traits::off_type;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
+
+      basic_syncbuf()
+      : basic_syncbuf(nullptr, allocator_type{})
+      { }
+
+      explicit
+      basic_syncbuf(streambuf_type* __obuf)
+	: basic_syncbuf(__obuf, allocator_type{})
+      { }
+
+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
+	: _M_wrapped(__obuf)
+	, _M_impl(__alloc)
+	, _M_locker(__obuf)
+      { }
+
+      basic_syncbuf(basic_syncbuf&& __other)
+	: _M_wrapped(__other._M_wrapped)
+	, _M_impl(std::move(__other._M_impl))
+	, _M_locker(std::move(__other._M_locker))
+	, _M_emit_on_sync(__other._M_emit_on_sync)
+	, _M_needs_sync(__other._M_needs_sync)
+      {
+	__other._M_wrapped = nullptr;
+      }
+
+      ~basic_syncbuf()
+      {
+	__try
+	  {
+	    emit();
+	  }
+	__catch (...)
+	  { }
+      }
+
+      basic_syncbuf& operator=(basic_syncbuf&& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    emit();
+
+	    _M_impl = std::move(__other._M_impl);
+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
+	    _M_locker = std::move(__other._M_locker);
+	    _M_emit_on_sync = __other._M_emit_on_sync;
+	    _M_needs_sync = __other._M_needs_sync;
+	  }
+	return *this;
+      }
+
+      void
+      swap(basic_syncbuf& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    std::swap(_M_impl, __other._M_impl);
+	    std::swap(_M_wrapped, __other._M_wrapped);
+	    std::swap(_M_locker, __other._M_locker);
+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
+	    std::swap(_M_needs_sync, __other._M_needs_sync);
+	  }
+      }
+
+      bool
+      emit()
+      {
+	if (!_M_wrapped)
+	  return false;
+
+	auto __s = _M_impl.view();
+	if (__s.empty())
+	  return true;
+
+	auto res = _M_locker([&, this]
+	    {
+	      if (_M_wrapped->sputn(__s.data(), __s.size()) != __s.size())
+		return false;
+
+	      if (_M_needs_sync)
+		{
+		  _M_needs_sync = false;
+		  if (_M_wrapped->pubsync() != 0)
+		    return false;
+		}
+	    });
+
+	_M_impl.str("");
+	return true;
+      }
+
+      streambuf_type*
+      get_wrapped() const noexcept
+      { return _M_wrapped; }
+
+      allocator_type get_allocator() const noexcept
+      { return _M_impl.get_allocator(); }
+
+      void
+      set_emit_on_sync(bool __b) noexcept
+      { _M_emit_on_sync = __b; }
+
+    protected:
+      int
+      sync() override
+      {
+	auto __res = _M_impl.pubsync();
+	if (__res == 0)
+	  {
+	    _M_needs_sync = true;
+	    if (_M_emit_on_sync)
+	      return emit() ? 0 : -1;
+	  }
+	return __res;
+      }
+
+      streamsize
+      xsputn(const char_type* __s, streamsize __n) override
+      { return _M_impl.sputn(__s, __n); }
+
+    private:
+      streambuf_type* _M_wrapped;
+
+      using __impl_type = basic_stringbuf<char_type, traits_type,
+					  allocator_type>;
+      __impl_type _M_impl;
+
+      struct __with_lock
+      {
+#if _GLIBCXX_HAS_GTHREADS
+	mutex* _M_mtx;
+
+	__with_lock(void* __t)
+	  : _M_mtx(__t ? &_S_get_mutex(__t) : nullptr)
+	{ }
+
+	void
+	swap(__with_lock& __other) noexcept
+	{ std::swap(_M_mtx, __other._M_mtx); }
+
+	template<typename _Func>
+	  bool
+	  operator()(_Func __f)
+	  {
+	    const lock_guard<mutex> __l(*_M_mtx);
+	    return __f();
+	  }
+
+	// FIXME: This should be put in the .so
+	static mutex&
+	_S_get_mutex(void* __t)
+	{
+	  const unsigned char __mask = 0xf;
+	  static mutex __m[__mask + 1];
+
+	  auto __key = _Hash_impl::hash(__t) & __mask;
+	  return __m[__key];
+	}
+#else
+	__with_lock(void*)
+	{ }
+
+	void
+	swap(__with_lock&) noexcept
+	{ }
+
+	template<typename _Func>
+	  bool
+	  operator()(_Func && __f)
+	  {
+	    return __f();
+	  }
+#endif
+	__with_lock(const __with_lock&) = delete;
+	__with_lock& operator=(const __with_lock&) = delete;
+
+	__with_lock(__with_lock&&) = default;
+	__with_lock& operator=(__with_lock&&) = default;
+      };
+      __with_lock _M_locker;
+
+      bool _M_emit_on_sync = false;
+      bool _M_needs_sync = false;
+    };
+
+  template <typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_osyncstream : public basic_ostream<_CharT, _Traits>
+    {
+      using __ostream_type = basic_ostream<_CharT, _Traits>;
+
+    public:
+      // Types:
+      using char_type = _CharT;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using int_type = typename traits_type::int_type;
+      using pos_type = typename traits_type::pos_type;
+      using off_type = typename traits_type::off_type;
+      using syncbuf_type = basic_syncbuf<_CharT, _Traits, _Alloc>;
+      using streambuf_type = typename syncbuf_type::streambuf_type;
+
+    private:
+      syncbuf_type _M_syncbuf;
+
+    public:
+      basic_osyncstream(streambuf_type* __buf, const allocator_type& __a)
+	: _M_syncbuf(__buf, __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(streambuf_type* __buf)
+	: _M_syncbuf(__buf)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_ostream<char_type, traits_type>& __os,
+		        const allocator_type& __a)
+	: basic_osyncstream(__os.rdbuf(), __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
+	: basic_osyncstream(__os.rdbuf())
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_osyncstream&& __rhs) noexcept
+	: __ostream_type(std::move(__rhs)),
+	_M_syncbuf(std::move(__rhs._M_syncbuf))
+      { __ostream_type::set_rdbuf(std::__addressof(_M_syncbuf)); }
+
+      ~basic_osyncstream() = default;
+
+      basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default;
+
+      syncbuf_type* rdbuf() const noexcept
+      { return const_cast<syncbuf_type*>(&_M_syncbuf); }
+
+      streambuf_type* get_wrapped() const noexcept
+      { return _M_syncbuf.get_wrapped(); }
+
+      void emit()
+      {
+	if (!_M_syncbuf.emit())
+	  this->setstate(ios_base::failbit);
+      }
+    };
+
+  template <class _CharT, class _Traits, class _Allocator>
+    inline void
+    swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __x,
+	 basic_syncbuf<_CharT, _Traits, _Allocator>& __y) noexcept
+    { __x.swap(__y); }
+
+  using syncbuf = basic_syncbuf<char>;
+  using wsyncbuf = basic_syncbuf<wchar_t>;
+
+  using osyncstream = basic_osyncstream<char>;
+  using wosyncstream = basic_osyncstream<wchar_t>;
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // _GLIBCXX_USE_CXX11_ABI
+#endif // C++2a
+#endif	/* _GLIBCXX_SYNCSTREAM */
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index ebb50a04d24..7f51ef3a6c4 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -229,6 +229,10 @@
 #define __cpp_lib_span 202002L
 #define __cpp_lib_ssize 201902L
 #define __cpp_lib_starts_ends_with 201711L
+# if _GLIBCXX_USE_CXX11_ABI
+// Only supported with cx11-abi
+#  define __cpp_lib_syncbuf 201803L
+# endif
 #define __cpp_lib_to_address 201711L
 #define __cpp_lib_to_array 201907L
 #endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
new file mode 100644
index 00000000000..1b3ae9330bb
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
new file mode 100644
index 00000000000..4ffcb0410fd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
new file mode 100644
index 00000000000..d5062bab70b
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
@@ -0,0 +1,137 @@
+// 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-additional-options "-pthread" { target pthread } }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::syncbuf s1;
+    VERIFY( s1.get_wrapped() == nullptr );
+
+    std::stringbuf b;
+    std::syncbuf s2(&b);
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+
+    sbuf_t b;
+
+    alloc_type aa;
+    sbuf_t s1(&b, aa);
+    VERIFY( aa == s1.get_allocator() );
+
+    alloc_type aaa(42);
+    sbuf_t s2(&b, aaa);
+    VERIFY( aaa == s2.get_allocator() );
+
+    VERIFY( s1.get_allocator() != s2.get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2;
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b;
+  std::syncbuf s1(&b);
+
+  std::syncbuf s2;
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == nullptr );
+  VERIFY( s2.get_wrapped() == &b );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( b.str() != txt );
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() != txt );
+
+    VERIFY( s.emit() );
+    VERIFY( b.str() == txt );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+    s.set_emit_on_sync(true);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() == txt );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
new file mode 100644
index 00000000000..49266b4abf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
@@ -0,0 +1,42 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
new file mode 100644
index 00000000000..90d20f1a607
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
@@ -0,0 +1,130 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <syncstream>
+#include <thread>
+#include <vector>
+#include <unordered_map>
+#include <utility>
+
+#include <testsuite_hooks.h>
+
+int
+main()
+{
+  using namespace std::chrono_literals;
+
+  std::stringbuf b;
+  std::atomic<unsigned> running(0);
+
+  auto const cstr = "This is a test";
+
+  constexpr int ct = 1000;
+  auto const body = [&]{
+    ++running;
+    auto tid = std::this_thread::get_id();
+    std::syncbuf s(&b);
+    for (auto i = 0; i < ct; ++i)
+    {
+      std::stringstream stm;
+      stm << tid << ' ' << cstr << ' ' << i << std::endl;
+      auto sv = stm.view();
+      s.sputn(sv.data(), sv.size());
+      VERIFY( s.emit() );
+    }
+  };
+
+  const auto tct = 8;
+  std::vector<std::thread> ts;
+  ts.reserve(tct);
+
+  for (auto i = 0; i < tct; ++i)
+    ts.emplace_back(std::thread(body));
+
+  do
+  {
+    std::this_thread::sleep_for(100ms);
+  }
+  while (running.load() < tct);
+
+  std::unordered_map<std::string, int> tids;
+  for (auto&& t : ts)
+  {
+    std::stringstream stm;
+    stm << t.get_id();
+    tids.emplace(std::make_pair(stm.str(), 0));
+  };
+
+  for (auto&& t : ts)
+     t.join();
+
+  std::vector<std::string_view> lines;
+  const auto lct = ct * ts.size();
+  lines.reserve(lct);
+
+  std::size_t last = 0;
+  auto sv = b.view();
+  auto p = sv.find('\n');
+  while (p != std::string_view::npos)
+  {
+    lines.emplace_back(sv.substr(last, p - last));
+    last = p+1;
+    p = sv.find('\n', last);
+  }
+  VERIFY( lines.size() == lct );
+
+  auto sep = "";
+  auto i = 0;
+  sv = std::string_view(cstr);
+
+  for (auto&& l : lines)
+  {
+    auto p = l.find(' ');
+    VERIFY( p != std::string_view::npos );
+    std::string tid(l.substr(0, p));
+    ++p;
+
+    VERIFY( l.substr(p, sv.size()) == sv );
+    std::string s(l.substr(++p + sv.size()));
+    std::stringstream stm(s);
+    int n;
+    stm >> n;
+    VERIFY( stm.eof() );
+    VERIFY( n >= 0 && n < ct );
+    auto it = tids.find(tid);
+    VERIFY( it != std::end(tids) );
+    ++(it->second);
+  }
+
+  for (auto const& t : tids)
+  {
+    VERIFY( t.second == ct );
+  }
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
new file mode 100644
index 00000000000..1b3ae9330bb
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
new file mode 100644
index 00000000000..4ffcb0410fd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
new file mode 100644
index 00000000000..a40cd162fbd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
@@ -0,0 +1,134 @@
+// 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-additional-options "-pthread" { target pthread } }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+    VERIFY( s.rdbuf() != nullptr );
+    VERIFY( s.get_wrapped() == &b );
+  }
+
+  {
+    std::ostringstream stm;
+    std::osyncstream s(stm);
+    VERIFY( s.get_wrapped() == stm.rdbuf() );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+    using stream_t = std::basic_osyncstream<char, std::char_traits<char>,
+					    alloc_type>;
+    using str_t = std::basic_ostringstream<char, std::char_traits<char>,
+                                      alloc_type>;
+    sbuf_t b;
+
+    alloc_type aa;
+    stream_t s1(&b, aa);
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+
+    alloc_type aaa(42);
+    stream_t s2(&b, aaa);
+    VERIFY( aaa == s2.rdbuf()->get_allocator() );
+
+    VERIFY( s1.rdbuf()->get_allocator() != s2.rdbuf()->get_allocator() );
+
+    str_t stm;
+    stream_t s3(stm, aa);
+    VERIFY( s3.get_wrapped() == stm.rdbuf() );
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s1(&b);
+
+    std::osyncstream s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b1;
+    std::osyncstream s1(&b1);
+
+    std::stringbuf b2;
+    std::osyncstream s2(&b2);
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b1 );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b1;
+  std::osyncstream s1(&b1);
+
+  std::stringbuf b2;
+  std::osyncstream s2(&b2);
+
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == &b2 );
+  VERIFY( s2.get_wrapped() == &b1 );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+
+    const std::string_view txt("This is a test");
+    s << txt;
+
+    s.emit();
+    VERIFY( b.str() == txt );
+  }
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
new file mode 100644
index 00000000000..dfc0b72efcd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
@@ -0,0 +1,43 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+    using syncbuf_type = test_type::syncbuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
-- 
2.26.2


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

* [PATCH] libstdc++: Add c++2a <syncstream>
  2020-10-29 21:12       ` Thomas Rodgers
  2020-11-02 15:58         ` Thomas Rodgers
@ 2020-11-02 16:10         ` Thomas Rodgers
  2020-11-02 17:38           ` Jonathan Wakely
  1 sibling, 1 reply; 16+ messages in thread
From: Thomas Rodgers @ 2020-11-02 16:10 UTC (permalink / raw)
  To: gcc-patches, libstdc++; +Cc: trodgers

From: Thomas Rodgers <trodgers@redhat.com>

IGNORE the previous patch.

Changes implementation to use a private __mutex type as discussed on
IRC.

libstdc++/ChangeLog:
	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
	libstdc++-v3/include/Makefile.in: Regenerate.
	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
	(basic_streambuf): Befriend __detail::__streambuf_core_access.
	libstdc++-v3/include/std/syncstream: New header.
	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
	Likewise.
	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
 libstdc++-v3/include/std/syncstream           | 333 ++++++++++++++++++
 libstdc++-v3/include/std/version              |   4 +
 .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
 .../testsuite/27_io/basic_syncbuf/2.cc        |  28 ++
 .../27_io/basic_syncbuf/basic_ops/1.cc        | 137 +++++++
 .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
 .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
 .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
 .../testsuite/27_io/basic_syncstream/2.cc     |  28 ++
 .../27_io/basic_syncstream/basic_ops/1.cc     | 134 +++++++
 .../basic_syncstream/requirements/types.cc    |  43 +++
 15 files changed, 939 insertions(+), 1 deletion(-)
 create mode 100644 libstdc++-v3/include/std/syncstream
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
 create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc

diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
index 9b49a15d31b..320f6dea688 100644
--- a/libstdc++-v3/doc/doxygen/user.cfg.in
+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
                          include/streambuf \
                          include/string \
                          include/string_view \
+                         include/syncstream \
                          include/system_error \
                          include/thread \
                          include/tuple \
diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index c90ac555e15..8652b921274 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -73,6 +73,7 @@ std_headers = \
 	${std_srcdir}/shared_mutex \
 	${std_srcdir}/span \
 	${std_srcdir}/sstream \
+	${std_srcdir}/syncstream \
 	${std_srcdir}/stack \
 	${std_srcdir}/stdexcept \
 	${std_srcdir}/stop_token \
diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
index 7518a98c25a..8899c323a28 100644
--- a/libstdc++-v3/include/precompiled/stdc++.h
+++ b/libstdc++-v3/include/precompiled/stdc++.h
@@ -141,6 +141,6 @@
 #include <ranges>
 #include <span>
 #include <stop_token>
-// #include <syncstream>
+#include <syncstream>
 #include <version>
 #endif
diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
new file mode 100644
index 00000000000..ff96ca6cf59
--- /dev/null
+++ b/libstdc++-v3/include/std/syncstream
@@ -0,0 +1,333 @@
+// <syncstream> -*- 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.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/syncstream
+ *  This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_SYNCSTREAM
+#define _GLIBCXX_SYNCSTREAM 1
+
+#if __cplusplus > 201703L 
+
+#include <bits/c++config.h>
+#if _GLIBCXX_USE_CXX11_ABI
+
+#define __cpp_lib_syncbuf 201803L
+
+#pragma GCC system_header
+
+#include <sstream>
+
+#include <bits/alloc_traits.h>
+#include <bits/allocator.h>
+#include <bits/functexcept.h>
+#include <bits/functional_hash.h>
+
+#if _GLIBCXX_HAS_GTHREADS
+# include <bits/std_mutex.h>
+#endif
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
+    {
+    public:
+      using char_type = _CharT;
+      using int_type = typename _Traits::int_type;
+      using pos_type = typename _Traits::pos_type;
+      using off_type = typename _Traits::off_type;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
+
+      basic_syncbuf()
+      : basic_syncbuf(nullptr, allocator_type{})
+      { }
+
+      explicit
+      basic_syncbuf(streambuf_type* __obuf)
+	: basic_syncbuf(__obuf, allocator_type{})
+      { }
+
+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
+	: _M_wrapped(__obuf)
+	, _M_impl(__alloc)
+	, _M_mtx(__obuf)
+      { }
+
+      basic_syncbuf(basic_syncbuf&& __other)
+	: _M_wrapped(__other._M_wrapped)
+	, _M_impl(std::move(__other._M_impl))
+	, _M_mtx(std::move(__other._M_mtx))
+	, _M_emit_on_sync(__other._M_emit_on_sync)
+	, _M_needs_sync(__other._M_needs_sync)
+      {
+	__other._M_wrapped = nullptr;
+      }
+
+      ~basic_syncbuf()
+      {
+	__try
+	  {
+	    emit();
+	  }
+	__catch (...)
+	  { }
+      }
+
+      basic_syncbuf& operator=(basic_syncbuf&& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    emit();
+
+	    _M_impl = std::move(__other._M_impl);
+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
+	    _M_mtx = std::move(__other._M_mtx);
+	    _M_emit_on_sync = __other._M_emit_on_sync;
+	    _M_needs_sync = __other._M_needs_sync;
+	  }
+	return *this;
+      }
+
+      void
+      swap(basic_syncbuf& __other)
+      {
+	if (std::__addressof(__other) != this)
+	  {
+	    std::swap(_M_impl, __other._M_impl);
+	    std::swap(_M_wrapped, __other._M_wrapped);
+	    std::swap(_M_mtx, __other._M_mtx);
+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
+	    std::swap(_M_needs_sync, __other._M_needs_sync);
+	  }
+      }
+
+      bool
+      emit()
+      {
+	if (!_M_wrapped)
+	  return false;
+
+	auto __s = _M_impl.view();
+	if (__s.empty())
+	  return true;
+
+	const lock_guard<__mutex> __l(_M_mtx);
+	if (_M_wrapped->sputn(__s.data(), __s.size()) != __s.size())
+	  return false;
+
+	if (_M_needs_sync)
+	  {
+	    _M_needs_sync = false;
+	    if (_M_wrapped->pubsync() != 0)
+	      return false;
+	  }
+
+	_M_impl.str("");
+	return true;
+      }
+
+      streambuf_type*
+      get_wrapped() const noexcept
+      { return _M_wrapped; }
+
+      allocator_type get_allocator() const noexcept
+      { return _M_impl.get_allocator(); }
+
+      void
+      set_emit_on_sync(bool __b) noexcept
+      { _M_emit_on_sync = __b; }
+
+    protected:
+      int
+      sync() override
+      {
+	auto __res = _M_impl.pubsync();
+	if (__res == 0)
+	  {
+	    _M_needs_sync = true;
+	    if (_M_emit_on_sync)
+	      return emit() ? 0 : -1;
+	  }
+	return __res;
+      }
+
+      streamsize
+      xsputn(const char_type* __s, streamsize __n) override
+      { return _M_impl.sputn(__s, __n); }
+
+    private:
+      streambuf_type* _M_wrapped;
+
+      using __impl_type = basic_stringbuf<char_type, traits_type,
+					  allocator_type>;
+      __impl_type _M_impl;
+
+      struct __mutex
+      {
+#if _GLIBCXX_HAS_GTHREADS
+	mutex* _M_mtx;
+
+	__mutex(void* __t)
+	  : _M_mtx(__t ? &_S_get_mutex(__t) : nullptr)
+	{ }
+
+	void
+	swap(__mutex& __other) noexcept
+	{ std::swap(_M_mtx, __other._M_mtx); }
+
+	void
+	lock()
+	{
+	  if (_M_mtx)
+	    _M_mtx->lock();
+	}
+
+	void
+	unlock()
+	{
+	  if (_M_mtx)
+	    _M_mtx->unlock();
+	}
+
+	// FIXME: This should be put in the .so
+	static mutex&
+	_S_get_mutex(void* __t)
+	{
+	  const unsigned char __mask = 0xf;
+	  static mutex __m[__mask + 1];
+
+	  auto __key = _Hash_impl::hash(__t) & __mask;
+	  return __m[__key];
+	}
+#else
+	__mutex(void*)
+	{ }
+
+	void
+	swap(__mutex&&) noexcept
+	{ }
+
+	void
+	lock()
+	{ }
+
+	void
+	unlock()
+	{ }
+#endif
+	__mutex(const __mutex&) = delete;
+	__mutex& operator=(const __mutex&) = delete;
+
+	__mutex(__mutex&&) = default;
+	__mutex& operator=(__mutex&&) = default;
+      };
+      __mutex _M_mtx;
+
+      bool _M_emit_on_sync = false;
+      bool _M_needs_sync = false;
+    };
+
+  template <typename _CharT, typename _Traits = char_traits<_CharT>,
+	    typename _Alloc = allocator<_CharT>>
+    class basic_osyncstream : public basic_ostream<_CharT, _Traits>
+    {
+      using __ostream_type = basic_ostream<_CharT, _Traits>;
+
+    public:
+      // Types:
+      using char_type = _CharT;
+      using traits_type = _Traits;
+      using allocator_type = _Alloc;
+      using int_type = typename traits_type::int_type;
+      using pos_type = typename traits_type::pos_type;
+      using off_type = typename traits_type::off_type;
+      using syncbuf_type = basic_syncbuf<_CharT, _Traits, _Alloc>;
+      using streambuf_type = typename syncbuf_type::streambuf_type;
+
+    private:
+      syncbuf_type _M_syncbuf;
+
+    public:
+      basic_osyncstream(streambuf_type* __buf, const allocator_type& __a)
+	: _M_syncbuf(__buf, __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(streambuf_type* __buf)
+	: _M_syncbuf(__buf)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_ostream<char_type, traits_type>& __os,
+		        const allocator_type& __a)
+	: basic_osyncstream(__os.rdbuf(), __a)
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      explicit basic_osyncstream(basic_ostream<char_type, traits_type>& __os)
+	: basic_osyncstream(__os.rdbuf())
+      { this->init(std::__addressof(_M_syncbuf)); }
+
+      basic_osyncstream(basic_osyncstream&& __rhs) noexcept
+	: __ostream_type(std::move(__rhs)),
+	_M_syncbuf(std::move(__rhs._M_syncbuf))
+      { __ostream_type::set_rdbuf(std::__addressof(_M_syncbuf)); }
+
+      ~basic_osyncstream() = default;
+
+      basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default;
+
+      syncbuf_type* rdbuf() const noexcept
+      { return const_cast<syncbuf_type*>(&_M_syncbuf); }
+
+      streambuf_type* get_wrapped() const noexcept
+      { return _M_syncbuf.get_wrapped(); }
+
+      void emit()
+      {
+	if (!_M_syncbuf.emit())
+	  this->setstate(ios_base::failbit);
+      }
+    };
+
+  template <class _CharT, class _Traits, class _Allocator>
+    inline void
+    swap(basic_syncbuf<_CharT, _Traits, _Allocator>& __x,
+	 basic_syncbuf<_CharT, _Traits, _Allocator>& __y) noexcept
+    { __x.swap(__y); }
+
+  using syncbuf = basic_syncbuf<char>;
+  using wsyncbuf = basic_syncbuf<wchar_t>;
+
+  using osyncstream = basic_osyncstream<char>;
+  using wosyncstream = basic_osyncstream<wchar_t>;
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // _GLIBCXX_USE_CXX11_ABI
+#endif // C++2a
+#endif	/* _GLIBCXX_SYNCSTREAM */
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index ebb50a04d24..7f51ef3a6c4 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -229,6 +229,10 @@
 #define __cpp_lib_span 202002L
 #define __cpp_lib_ssize 201902L
 #define __cpp_lib_starts_ends_with 201711L
+# if _GLIBCXX_USE_CXX11_ABI
+// Only supported with cx11-abi
+#  define __cpp_lib_syncbuf 201803L
+# endif
 #define __cpp_lib_to_address 201711L
 #define __cpp_lib_to_array 201907L
 #endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
new file mode 100644
index 00000000000..1b3ae9330bb
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
new file mode 100644
index 00000000000..4ffcb0410fd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
new file mode 100644
index 00000000000..d5062bab70b
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
@@ -0,0 +1,137 @@
+// 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-additional-options "-pthread" { target pthread } }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::syncbuf s1;
+    VERIFY( s1.get_wrapped() == nullptr );
+
+    std::stringbuf b;
+    std::syncbuf s2(&b);
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+
+    sbuf_t b;
+
+    alloc_type aa;
+    sbuf_t s1(&b, aa);
+    VERIFY( aa == s1.get_allocator() );
+
+    alloc_type aaa(42);
+    sbuf_t s2(&b, aaa);
+    VERIFY( aaa == s2.get_allocator() );
+
+    VERIFY( s1.get_allocator() != s2.get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s1(&b);
+
+    std::syncbuf s2;
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b;
+  std::syncbuf s1(&b);
+
+  std::syncbuf s2;
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == nullptr );
+  VERIFY( s2.get_wrapped() == &b );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( b.str() != txt );
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() != txt );
+
+    VERIFY( s.emit() );
+    VERIFY( b.str() == txt );
+  }
+
+  {
+    std::stringbuf b;
+    std::syncbuf s(&b);
+    s.set_emit_on_sync(true);
+
+    const std::string_view txt("This is a test");
+    s.sputn(txt.data(), txt.size());
+
+    VERIFY( s.pubsync() == 0 );
+    VERIFY( b.str() == txt );
+  }
+}
+
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
new file mode 100644
index 00000000000..49266b4abf0
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
@@ -0,0 +1,42 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
new file mode 100644
index 00000000000..90d20f1a607
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
@@ -0,0 +1,130 @@
+// 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 -pthread" }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target pthread }
+// { dg-require-effective-target cxx11-abi }
+// { dg-require-gthreads "" }
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <sstream>
+#include <string>
+#include <string_view>
+#include <syncstream>
+#include <thread>
+#include <vector>
+#include <unordered_map>
+#include <utility>
+
+#include <testsuite_hooks.h>
+
+int
+main()
+{
+  using namespace std::chrono_literals;
+
+  std::stringbuf b;
+  std::atomic<unsigned> running(0);
+
+  auto const cstr = "This is a test";
+
+  constexpr int ct = 1000;
+  auto const body = [&]{
+    ++running;
+    auto tid = std::this_thread::get_id();
+    std::syncbuf s(&b);
+    for (auto i = 0; i < ct; ++i)
+    {
+      std::stringstream stm;
+      stm << tid << ' ' << cstr << ' ' << i << std::endl;
+      auto sv = stm.view();
+      s.sputn(sv.data(), sv.size());
+      VERIFY( s.emit() );
+    }
+  };
+
+  const auto tct = 8;
+  std::vector<std::thread> ts;
+  ts.reserve(tct);
+
+  for (auto i = 0; i < tct; ++i)
+    ts.emplace_back(std::thread(body));
+
+  do
+  {
+    std::this_thread::sleep_for(100ms);
+  }
+  while (running.load() < tct);
+
+  std::unordered_map<std::string, int> tids;
+  for (auto&& t : ts)
+  {
+    std::stringstream stm;
+    stm << t.get_id();
+    tids.emplace(std::make_pair(stm.str(), 0));
+  };
+
+  for (auto&& t : ts)
+     t.join();
+
+  std::vector<std::string_view> lines;
+  const auto lct = ct * ts.size();
+  lines.reserve(lct);
+
+  std::size_t last = 0;
+  auto sv = b.view();
+  auto p = sv.find('\n');
+  while (p != std::string_view::npos)
+  {
+    lines.emplace_back(sv.substr(last, p - last));
+    last = p+1;
+    p = sv.find('\n', last);
+  }
+  VERIFY( lines.size() == lct );
+
+  auto sep = "";
+  auto i = 0;
+  sv = std::string_view(cstr);
+
+  for (auto&& l : lines)
+  {
+    auto p = l.find(' ');
+    VERIFY( p != std::string_view::npos );
+    std::string tid(l.substr(0, p));
+    ++p;
+
+    VERIFY( l.substr(p, sv.size()) == sv );
+    std::string s(l.substr(++p + sv.size()));
+    std::stringstream stm(s);
+    int n;
+    stm >> n;
+    VERIFY( stm.eof() );
+    VERIFY( n >= 0 && n < ct );
+    auto it = tids.find(tid);
+    VERIFY( it != std::end(tids) );
+    ++(it->second);
+  }
+
+  for (auto const& t : tids)
+  {
+    VERIFY( t.second == ct );
+  }
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
new file mode 100644
index 00000000000..1b3ae9330bb
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <syncstream>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <syncstream>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
new file mode 100644
index 00000000000..4ffcb0410fd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
@@ -0,0 +1,28 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <version>
+
+#ifndef __cpp_lib_syncbuf
+# error "Feature-test macro for syncbuf missing in <version>"
+#elif __cpp_lib_syncbuf != 201803L
+# error "Feature-test macro for syncbuf has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
new file mode 100644
index 00000000000..a40cd162fbd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
@@ -0,0 +1,134 @@
+// 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-additional-options "-pthread" { target pthread } }
+// { dg-do run { target c++2a } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <sstream>
+#include <string_view>
+#include <syncstream>
+#include <testsuite_allocator.h>
+#include <testsuite_hooks.h>
+
+void
+test01() // construction
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+    VERIFY( s.rdbuf() != nullptr );
+    VERIFY( s.get_wrapped() == &b );
+  }
+
+  {
+    std::ostringstream stm;
+    std::osyncstream s(stm);
+    VERIFY( s.get_wrapped() == stm.rdbuf() );
+  }
+
+  {
+    using alloc_type = __gnu_test::uneq_allocator<char>;
+    using sbuf_t = std::basic_syncbuf<char, std::char_traits<char>,
+				      alloc_type>;
+    using stream_t = std::basic_osyncstream<char, std::char_traits<char>,
+					    alloc_type>;
+    using str_t = std::basic_ostringstream<char, std::char_traits<char>,
+                                      alloc_type>;
+    sbuf_t b;
+
+    alloc_type aa;
+    stream_t s1(&b, aa);
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+
+    alloc_type aaa(42);
+    stream_t s2(&b, aaa);
+    VERIFY( aaa == s2.rdbuf()->get_allocator() );
+
+    VERIFY( s1.rdbuf()->get_allocator() != s2.rdbuf()->get_allocator() );
+
+    str_t stm;
+    stream_t s3(stm, aa);
+    VERIFY( s3.get_wrapped() == stm.rdbuf() );
+    VERIFY( aa == s1.rdbuf()->get_allocator() );
+  }
+}
+
+void
+test02() // moving
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s1(&b);
+
+    std::osyncstream s2(std::move(s1));
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b );
+  }
+
+  {
+    std::stringbuf b1;
+    std::osyncstream s1(&b1);
+
+    std::stringbuf b2;
+    std::osyncstream s2(&b2);
+    s2 = std::move(s1);
+
+    VERIFY( s1.get_wrapped() == nullptr );
+    VERIFY( s2.get_wrapped() == &b1 );
+  }
+}
+
+void
+test03() // swaping
+{
+  std::stringbuf b1;
+  std::osyncstream s1(&b1);
+
+  std::stringbuf b2;
+  std::osyncstream s2(&b2);
+
+  std::swap(s1, s2);
+
+  VERIFY( s1.get_wrapped() == &b2 );
+  VERIFY( s2.get_wrapped() == &b1 );
+}
+
+void
+test04() // emitting
+{
+  {
+    std::stringbuf b;
+    std::osyncstream s(&b);
+
+    const std::string_view txt("This is a test");
+    s << txt;
+
+    s.emit();
+    VERIFY( b.str() == txt );
+  }
+}
+int main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
new file mode 100644
index 00000000000..dfc0b72efcd
--- /dev/null
+++ b/libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
@@ -0,0 +1,43 @@
+// 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 } }
+// { dg-require-effective-target cxx11-abi }
+
+#include <syncstream>
+
+template<typename T>
+  struct type_reqs
+  {
+    using test_type = T;
+    using char_type = test_type::char_type;
+    using int_type = test_type::int_type;
+    using pos_type = test_type::pos_type;
+    using off_Type = test_type::off_type;
+    using traits_type = test_type::traits_type;
+    using allocator_type = test_type::allocator_type;
+    using streambuf_type = test_type::streambuf_type;
+    using syncbuf_type = test_type::syncbuf_type;
+  };
+
+void test01()
+{
+  // Check for required typedefs
+  using test_type = type_reqs<std::osyncstream>;
+  using wtest_type = type_reqs<std::wosyncstream>;
+}
-- 
2.26.2


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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-11-02 16:10         ` Thomas Rodgers
@ 2020-11-02 17:38           ` Jonathan Wakely
  2020-11-02 18:43             ` Thomas Rodgers
  0 siblings, 1 reply; 16+ messages in thread
From: Jonathan Wakely @ 2020-11-02 17:38 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: gcc-patches, libstdc++, trodgers

On 02/11/20 08:10 -0800, Thomas Rodgers wrote:
>From: Thomas Rodgers <trodgers@redhat.com>
>
>IGNORE the previous patch.
>
>Changes implementation to use a private __mutex type as discussed on
>IRC.
>
>libstdc++/ChangeLog:
>	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
>	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
>	libstdc++-v3/include/Makefile.in: Regenerate.
>	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
>	(basic_streambuf): Befriend __detail::__streambuf_core_access.
>	libstdc++-v3/include/std/syncstream: New header.
>	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
>	Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
>	Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
>	Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
>	Likewise.
>	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
> libstdc++-v3/include/std/syncstream           | 333 ++++++++++++++++++
> libstdc++-v3/include/std/version              |   4 +
> .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
> .../testsuite/27_io/basic_syncbuf/2.cc        |  28 ++
> .../27_io/basic_syncbuf/basic_ops/1.cc        | 137 +++++++
> .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
> .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
> .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
> .../testsuite/27_io/basic_syncstream/2.cc     |  28 ++
> .../27_io/basic_syncstream/basic_ops/1.cc     | 134 +++++++
> .../basic_syncstream/requirements/types.cc    |  43 +++
> 15 files changed, 939 insertions(+), 1 deletion(-)
> create mode 100644 libstdc++-v3/include/std/syncstream
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
>
>diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
>index 9b49a15d31b..320f6dea688 100644
>--- a/libstdc++-v3/doc/doxygen/user.cfg.in
>+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
>@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
>                          include/streambuf \
>                          include/string \
>                          include/string_view \
>+                         include/syncstream \
>                          include/system_error \
>                          include/thread \
>                          include/tuple \
>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>index c90ac555e15..8652b921274 100644
>--- a/libstdc++-v3/include/Makefile.am
>+++ b/libstdc++-v3/include/Makefile.am
>@@ -73,6 +73,7 @@ std_headers = \
> 	${std_srcdir}/shared_mutex \
> 	${std_srcdir}/span \
> 	${std_srcdir}/sstream \
>+	${std_srcdir}/syncstream \
> 	${std_srcdir}/stack \
> 	${std_srcdir}/stdexcept \
> 	${std_srcdir}/stop_token \
>diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
>index 7518a98c25a..8899c323a28 100644
>--- a/libstdc++-v3/include/precompiled/stdc++.h
>+++ b/libstdc++-v3/include/precompiled/stdc++.h
>@@ -141,6 +141,6 @@
> #include <ranges>
> #include <span>
> #include <stop_token>
>-// #include <syncstream>
>+#include <syncstream>
> #include <version>
> #endif
>diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
>new file mode 100644
>index 00000000000..ff96ca6cf59
>--- /dev/null
>+++ b/libstdc++-v3/include/std/syncstream
>@@ -0,0 +1,333 @@
>+// <syncstream> -*- 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.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file include/syncstream
>+ *  This is a Standard C++ Library header.
>+ */
>+
>+#ifndef _GLIBCXX_SYNCSTREAM
>+#define _GLIBCXX_SYNCSTREAM 1
>+
>+#if __cplusplus > 201703L
>+
>+#include <bits/c++config.h>
>+#if _GLIBCXX_USE_CXX11_ABI
>+
>+#define __cpp_lib_syncbuf 201803L
>+
>+#pragma GCC system_header
>+
>+#include <sstream>
>+
>+#include <bits/alloc_traits.h>
>+#include <bits/allocator.h>
>+#include <bits/functexcept.h>
>+#include <bits/functional_hash.h>
>+
>+#if _GLIBCXX_HAS_GTHREADS
>+# include <bits/std_mutex.h>
>+#endif
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
>+	    typename _Alloc = allocator<_CharT>>
>+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
>+    {
>+    public:
>+      using char_type = _CharT;
>+      using int_type = typename _Traits::int_type;
>+      using pos_type = typename _Traits::pos_type;
>+      using off_type = typename _Traits::off_type;
>+      using traits_type = _Traits;
>+      using allocator_type = _Alloc;
>+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
>+
>+      basic_syncbuf()
>+      : basic_syncbuf(nullptr, allocator_type{})
>+      { }
>+
>+      explicit
>+      basic_syncbuf(streambuf_type* __obuf)
>+	: basic_syncbuf(__obuf, allocator_type{})
>+      { }
>+
>+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
>+	: _M_wrapped(__obuf)
>+	, _M_impl(__alloc)
>+	, _M_mtx(__obuf)
>+      { }
>+
>+      basic_syncbuf(basic_syncbuf&& __other)
>+	: _M_wrapped(__other._M_wrapped)
>+	, _M_impl(std::move(__other._M_impl))
>+	, _M_mtx(std::move(__other._M_mtx))
>+	, _M_emit_on_sync(__other._M_emit_on_sync)
>+	, _M_needs_sync(__other._M_needs_sync)
>+      {
>+	__other._M_wrapped = nullptr;
>+      }
>+
>+      ~basic_syncbuf()
>+      {
>+	__try
>+	  {
>+	    emit();
>+	  }
>+	__catch (...)
>+	  { }
>+      }
>+
>+      basic_syncbuf& operator=(basic_syncbuf&& __other)
>+      {
>+	if (std::__addressof(__other) != this)
>+	  {
>+	    emit();
>+
>+	    _M_impl = std::move(__other._M_impl);
>+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
>+	    _M_mtx = std::move(__other._M_mtx);
>+	    _M_emit_on_sync = __other._M_emit_on_sync;
>+	    _M_needs_sync = __other._M_needs_sync;
>+	  }
>+	return *this;
>+      }
>+
>+      void
>+      swap(basic_syncbuf& __other)
>+      {
>+	if (std::__addressof(__other) != this)
>+	  {
>+	    std::swap(_M_impl, __other._M_impl);
>+	    std::swap(_M_wrapped, __other._M_wrapped);
>+	    std::swap(_M_mtx, __other._M_mtx);
>+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
>+	    std::swap(_M_needs_sync, __other._M_needs_sync);
>+	  }
>+      }
>+
>+      bool
>+      emit()
>+      {
>+	if (!_M_wrapped)
>+	  return false;
>+
>+	auto __s = _M_impl.view();
>+	if (__s.empty())
>+	  return true;
>+
>+	const lock_guard<__mutex> __l(_M_mtx);
>+	if (_M_wrapped->sputn(__s.data(), __s.size()) != __s.size())
>+	  return false;
>+
>+	if (_M_needs_sync)
>+	  {
>+	    _M_needs_sync = false;
>+	    if (_M_wrapped->pubsync() != 0)
>+	      return false;
>+	  }
>+
>+	_M_impl.str("");
>+	return true;
>+      }
>+
>+      streambuf_type*
>+      get_wrapped() const noexcept
>+      { return _M_wrapped; }
>+
>+      allocator_type get_allocator() const noexcept
>+      { return _M_impl.get_allocator(); }
>+
>+      void
>+      set_emit_on_sync(bool __b) noexcept
>+      { _M_emit_on_sync = __b; }
>+
>+    protected:
>+      int
>+      sync() override
>+      {
>+	auto __res = _M_impl.pubsync();
>+	if (__res == 0)
>+	  {
>+	    _M_needs_sync = true;
>+	    if (_M_emit_on_sync)
>+	      return emit() ? 0 : -1;
>+	  }
>+	return __res;
>+      }
>+
>+      streamsize
>+      xsputn(const char_type* __s, streamsize __n) override
>+      { return _M_impl.sputn(__s, __n); }
>+
>+    private:
>+      streambuf_type* _M_wrapped;
>+
>+      using __impl_type = basic_stringbuf<char_type, traits_type,
>+					  allocator_type>;
>+      __impl_type _M_impl;
>+
>+      struct __mutex
>+      {
>+#if _GLIBCXX_HAS_GTHREADS
>+	mutex* _M_mtx;
>+
>+	__mutex(void* __t)

Make this 'explicit' please.

>+	  : _M_mtx(__t ? &_S_get_mutex(__t) : nullptr)
>+	{ }
>+
>+	void
>+	swap(__mutex& __other) noexcept
>+	{ std::swap(_M_mtx, __other._M_mtx); }
>+
>+	void
>+	lock()
>+	{
>+	  if (_M_mtx)
>+	    _M_mtx->lock();
>+	}
>+
>+	void
>+	unlock()
>+	{
>+	  if (_M_mtx)
>+	    _M_mtx->unlock();
>+	}
>+
>+	// FIXME: This should be put in the .so
>+	static mutex&
>+	_S_get_mutex(void* __t)
>+	{
>+	  const unsigned char __mask = 0xf;
>+	  static mutex __m[__mask + 1];
>+
>+	  auto __key = _Hash_impl::hash(__t) & __mask;
>+	  return __m[__key];
>+	}
>+#else
>+	__mutex(void*)

And 'explicit' here too.

>+	{ }
>+
>+	void
>+	swap(__mutex&&) noexcept

This needs to be an lvalue reference, or it won't compile.

>+	{ }
>+
>+	void
>+	lock()
>+	{ }
>+
>+	void
>+	unlock()
>+	{ }

All these completely empty functions can be put on one line:

       void unlock() { }

There's no need for these no-op members take up so many lines.

>+#endif
>+	__mutex(const __mutex&) = delete;
>+	__mutex& operator=(const __mutex&) = delete;

These are redundant (the user-declared moves below cause the copies to
be deleted) but harmless.

>+
>+	__mutex(__mutex&&) = default;
>+	__mutex& operator=(__mutex&&) = default;
>+      };
>+      __mutex _M_mtx;
>+
>+      bool _M_emit_on_sync = false;
>+      bool _M_needs_sync = false;
>+    };


OK for trunk, thanks.



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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-11-02 17:38           ` Jonathan Wakely
@ 2020-11-02 18:43             ` Thomas Rodgers
  2020-11-03 21:22               ` Christophe Lyon
  0 siblings, 1 reply; 16+ messages in thread
From: Thomas Rodgers @ 2020-11-02 18:43 UTC (permalink / raw)
  To: Jonathan Wakely; +Cc: Thomas Rodgers, gcc-patches, libstdc++


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

Jonathan Wakely writes:

> On 02/11/20 08:10 -0800, Thomas Rodgers wrote:
>>From: Thomas Rodgers <trodgers@redhat.com>
>>
>>IGNORE the previous patch.
>>
>>Changes implementation to use a private __mutex type as discussed on
>>IRC.
>>
>>libstdc++/ChangeLog:
>>	libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
>>	libstdc++-v3/include/Makefile.am (std_headers): Add new header.
>>	libstdc++-v3/include/Makefile.in: Regenerate.
>>	libstdc++-v3/include/precompiled/stdc++.h: Include new header.
>>	(basic_streambuf): Befriend __detail::__streambuf_core_access.
>>	libstdc++-v3/include/std/syncstream: New header.
>>	libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
>>	libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
>>	libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
>>	libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
>>	Likewise.
>>	libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
>>	Likewise.
>>	libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
>>	Likewise.
>>	libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
>>	libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
>>	libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
>>	Likewise.
>>	libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
>> libstdc++-v3/include/std/syncstream           | 333 ++++++++++++++++++
>> libstdc++-v3/include/std/version              |   4 +
>> .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
>> .../testsuite/27_io/basic_syncbuf/2.cc        |  28 ++
>> .../27_io/basic_syncbuf/basic_ops/1.cc        | 137 +++++++
>> .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
>> .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
>> .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
>> .../testsuite/27_io/basic_syncstream/2.cc     |  28 ++
>> .../27_io/basic_syncstream/basic_ops/1.cc     | 134 +++++++
>> .../basic_syncstream/requirements/types.cc    |  43 +++
>> 15 files changed, 939 insertions(+), 1 deletion(-)
>> create mode 100644 libstdc++-v3/include/std/syncstream
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
>> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
>>
>>diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
>>index 9b49a15d31b..320f6dea688 100644
>>--- a/libstdc++-v3/doc/doxygen/user.cfg.in
>>+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
>>@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
>>                          include/streambuf \
>>                          include/string \
>>                          include/string_view \
>>+                         include/syncstream \
>>                          include/system_error \
>>                          include/thread \
>>                          include/tuple \
>>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
>>index c90ac555e15..8652b921274 100644
>>--- a/libstdc++-v3/include/Makefile.am
>>+++ b/libstdc++-v3/include/Makefile.am
>>@@ -73,6 +73,7 @@ std_headers = \
>> 	${std_srcdir}/shared_mutex \
>> 	${std_srcdir}/span \
>> 	${std_srcdir}/sstream \
>>+	${std_srcdir}/syncstream \
>> 	${std_srcdir}/stack \
>> 	${std_srcdir}/stdexcept \
>> 	${std_srcdir}/stop_token \
>>diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
>>index 7518a98c25a..8899c323a28 100644
>>--- a/libstdc++-v3/include/precompiled/stdc++.h
>>+++ b/libstdc++-v3/include/precompiled/stdc++.h
>>@@ -141,6 +141,6 @@
>> #include <ranges>
>> #include <span>
>> #include <stop_token>
>>-// #include <syncstream>
>>+#include <syncstream>
>> #include <version>
>> #endif
>>diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
>>new file mode 100644
>>index 00000000000..ff96ca6cf59
>>--- /dev/null
>>+++ b/libstdc++-v3/include/std/syncstream
>>@@ -0,0 +1,333 @@
>>+// <syncstream> -*- 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.
>>+
>>+// Under Section 7 of GPL version 3, you are granted additional
>>+// permissions described in the GCC Runtime Library Exception, version
>>+// 3.1, as published by the Free Software Foundation.
>>+
>>+// You should have received a copy of the GNU General Public License and
>>+// a copy of the GCC Runtime Library Exception along with this program;
>>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>>+// <http://www.gnu.org/licenses/>.
>>+
>>+/** @file include/syncstream
>>+ *  This is a Standard C++ Library header.
>>+ */
>>+
>>+#ifndef _GLIBCXX_SYNCSTREAM
>>+#define _GLIBCXX_SYNCSTREAM 1
>>+
>>+#if __cplusplus > 201703L
>>+
>>+#include <bits/c++config.h>
>>+#if _GLIBCXX_USE_CXX11_ABI
>>+
>>+#define __cpp_lib_syncbuf 201803L
>>+
>>+#pragma GCC system_header
>>+
>>+#include <sstream>
>>+
>>+#include <bits/alloc_traits.h>
>>+#include <bits/allocator.h>
>>+#include <bits/functexcept.h>
>>+#include <bits/functional_hash.h>
>>+
>>+#if _GLIBCXX_HAS_GTHREADS
>>+# include <bits/std_mutex.h>
>>+#endif
>>+
>>+namespace std _GLIBCXX_VISIBILITY(default)
>>+{
>>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>>+
>>+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
>>+	    typename _Alloc = allocator<_CharT>>
>>+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
>>+    {
>>+    public:
>>+      using char_type = _CharT;
>>+      using int_type = typename _Traits::int_type;
>>+      using pos_type = typename _Traits::pos_type;
>>+      using off_type = typename _Traits::off_type;
>>+      using traits_type = _Traits;
>>+      using allocator_type = _Alloc;
>>+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
>>+
>>+      basic_syncbuf()
>>+      : basic_syncbuf(nullptr, allocator_type{})
>>+      { }
>>+
>>+      explicit
>>+      basic_syncbuf(streambuf_type* __obuf)
>>+	: basic_syncbuf(__obuf, allocator_type{})
>>+      { }
>>+
>>+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
>>+	: _M_wrapped(__obuf)
>>+	, _M_impl(__alloc)
>>+	, _M_mtx(__obuf)
>>+      { }
>>+
>>+      basic_syncbuf(basic_syncbuf&& __other)
>>+	: _M_wrapped(__other._M_wrapped)
>>+	, _M_impl(std::move(__other._M_impl))
>>+	, _M_mtx(std::move(__other._M_mtx))
>>+	, _M_emit_on_sync(__other._M_emit_on_sync)
>>+	, _M_needs_sync(__other._M_needs_sync)
>>+      {
>>+	__other._M_wrapped = nullptr;
>>+      }
>>+
>>+      ~basic_syncbuf()
>>+      {
>>+	__try
>>+	  {
>>+	    emit();
>>+	  }
>>+	__catch (...)
>>+	  { }
>>+      }
>>+
>>+      basic_syncbuf& operator=(basic_syncbuf&& __other)
>>+      {
>>+	if (std::__addressof(__other) != this)
>>+	  {
>>+	    emit();
>>+
>>+	    _M_impl = std::move(__other._M_impl);
>>+	    _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
>>+	    _M_mtx = std::move(__other._M_mtx);
>>+	    _M_emit_on_sync = __other._M_emit_on_sync;
>>+	    _M_needs_sync = __other._M_needs_sync;
>>+	  }
>>+	return *this;
>>+      }
>>+
>>+      void
>>+      swap(basic_syncbuf& __other)
>>+      {
>>+	if (std::__addressof(__other) != this)
>>+	  {
>>+	    std::swap(_M_impl, __other._M_impl);
>>+	    std::swap(_M_wrapped, __other._M_wrapped);
>>+	    std::swap(_M_mtx, __other._M_mtx);
>>+	    std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
>>+	    std::swap(_M_needs_sync, __other._M_needs_sync);
>>+	  }
>>+      }
>>+
>>+      bool
>>+      emit()
>>+      {
>>+	if (!_M_wrapped)
>>+	  return false;
>>+
>>+	auto __s = _M_impl.view();
>>+	if (__s.empty())
>>+	  return true;
>>+
>>+	const lock_guard<__mutex> __l(_M_mtx);
>>+	if (_M_wrapped->sputn(__s.data(), __s.size()) != __s.size())
>>+	  return false;
>>+
>>+	if (_M_needs_sync)
>>+	  {
>>+	    _M_needs_sync = false;
>>+	    if (_M_wrapped->pubsync() != 0)
>>+	      return false;
>>+	  }
>>+
>>+	_M_impl.str("");
>>+	return true;
>>+      }
>>+
>>+      streambuf_type*
>>+      get_wrapped() const noexcept
>>+      { return _M_wrapped; }
>>+
>>+      allocator_type get_allocator() const noexcept
>>+      { return _M_impl.get_allocator(); }
>>+
>>+      void
>>+      set_emit_on_sync(bool __b) noexcept
>>+      { _M_emit_on_sync = __b; }
>>+
>>+    protected:
>>+      int
>>+      sync() override
>>+      {
>>+	auto __res = _M_impl.pubsync();
>>+	if (__res == 0)
>>+	  {
>>+	    _M_needs_sync = true;
>>+	    if (_M_emit_on_sync)
>>+	      return emit() ? 0 : -1;
>>+	  }
>>+	return __res;
>>+      }
>>+
>>+      streamsize
>>+      xsputn(const char_type* __s, streamsize __n) override
>>+      { return _M_impl.sputn(__s, __n); }
>>+
>>+    private:
>>+      streambuf_type* _M_wrapped;
>>+
>>+      using __impl_type = basic_stringbuf<char_type, traits_type,
>>+					  allocator_type>;
>>+      __impl_type _M_impl;
>>+
>>+      struct __mutex
>>+      {
>>+#if _GLIBCXX_HAS_GTHREADS
>>+	mutex* _M_mtx;
>>+
>>+	__mutex(void* __t)
>
> Make this 'explicit' please.
>
>>+	  : _M_mtx(__t ? &_S_get_mutex(__t) : nullptr)
>>+	{ }
>>+
>>+	void
>>+	swap(__mutex& __other) noexcept
>>+	{ std::swap(_M_mtx, __other._M_mtx); }
>>+
>>+	void
>>+	lock()
>>+	{
>>+	  if (_M_mtx)
>>+	    _M_mtx->lock();
>>+	}
>>+
>>+	void
>>+	unlock()
>>+	{
>>+	  if (_M_mtx)
>>+	    _M_mtx->unlock();
>>+	}
>>+
>>+	// FIXME: This should be put in the .so
>>+	static mutex&
>>+	_S_get_mutex(void* __t)
>>+	{
>>+	  const unsigned char __mask = 0xf;
>>+	  static mutex __m[__mask + 1];
>>+
>>+	  auto __key = _Hash_impl::hash(__t) & __mask;
>>+	  return __m[__key];
>>+	}
>>+#else
>>+	__mutex(void*)
>
> And 'explicit' here too.
>
>>+	{ }
>>+
>>+	void
>>+	swap(__mutex&&) noexcept
>
> This needs to be an lvalue reference, or it won't compile.
>
>>+	{ }
>>+
>>+	void
>>+	lock()
>>+	{ }
>>+
>>+	void
>>+	unlock()
>>+	{ }
>
> All these completely empty functions can be put on one line:
>
>       void unlock() { }
>
> There's no need for these no-op members take up so many lines.
>
>>+#endif
>>+	__mutex(const __mutex&) = delete;
>>+	__mutex& operator=(const __mutex&) = delete;
>
> These are redundant (the user-declared moves below cause the copies to
> be deleted) but harmless.
>
>>+
>>+	__mutex(__mutex&&) = default;
>>+	__mutex& operator=(__mutex&&) = default;
>>+      };
>>+      __mutex _M_mtx;
>>+
>>+      bool _M_emit_on_sync = false;
>>+      bool _M_needs_sync = false;
>>+    };
>
>
> OK for trunk, thanks.


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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-11-02 18:43             ` Thomas Rodgers
@ 2020-11-03 21:22               ` Christophe Lyon
  2020-11-03 22:00                 ` Jonathan Wakely
  0 siblings, 1 reply; 16+ messages in thread
From: Christophe Lyon @ 2020-11-03 21:22 UTC (permalink / raw)
  To: Thomas Rodgers; +Cc: Jonathan Wakely, gcc Patches, libstdc++

On Mon, 2 Nov 2020 at 19:43, Thomas Rodgers via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
>
> Testsed x86_64-pc-linux-gnu, committed to master.
>

Hi,

I can see the new tests failing on bare-metal targets using newlib
(arm-eabi, aarch64-elf):
    27_io/basic_syncbuf/1.cc (test for excess errors)
    27_io/basic_syncbuf/basic_ops/1.cc (test for excess errors)
    27_io/basic_syncbuf/requirements/types.cc (test for excess errors)
    27_io/basic_syncstream/1.cc (test for excess errors)
    27_io/basic_syncstream/basic_ops/1.cc (test for excess errors)
    27_io/basic_syncstream/requirements/types.cc (test for excess errors)

because:
libstdc++-v3/include/syncstream:142: error: 'lock_guard' does not name a type

Christophe

> Jonathan Wakely writes:
>
> > On 02/11/20 08:10 -0800, Thomas Rodgers wrote:
> >>From: Thomas Rodgers <trodgers@redhat.com>
> >>
> >>IGNORE the previous patch.
> >>
> >>Changes implementation to use a private __mutex type as discussed on
> >>IRC.
> >>
> >>libstdc++/ChangeLog:
> >>      libstdc++-v3/doc/doxygen/user.cfg.in (INPUT): Add new header.
> >>      libstdc++-v3/include/Makefile.am (std_headers): Add new header.
> >>      libstdc++-v3/include/Makefile.in: Regenerate.
> >>      libstdc++-v3/include/precompiled/stdc++.h: Include new header.
> >>      (basic_streambuf): Befriend __detail::__streambuf_core_access.
> >>      libstdc++-v3/include/std/syncstream: New header.
> >>      libstdc++-v3/include/std/version: Add __cpp_lib_syncbuf:
> >>      libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc: New test.
> >>      libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc: Likewise.
> >>      libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc:
> >>      Likewise.
> >>      libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc:
> >>      Likewise.
> >>      libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc:
> >>      Likewise.
> >>      libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc: Likewise.
> >>      libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc: Likewise.
> >>      libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc:
> >>      Likewise.
> >>      libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.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     |   2 +-
> >> libstdc++-v3/include/std/syncstream           | 333 ++++++++++++++++++
> >> libstdc++-v3/include/std/version              |   4 +
> >> .../testsuite/27_io/basic_syncbuf/1.cc        |  28 ++
> >> .../testsuite/27_io/basic_syncbuf/2.cc        |  28 ++
> >> .../27_io/basic_syncbuf/basic_ops/1.cc        | 137 +++++++
> >> .../27_io/basic_syncbuf/requirements/types.cc |  42 +++
> >> .../27_io/basic_syncbuf/sync_ops/1.cc         | 130 +++++++
> >> .../testsuite/27_io/basic_syncstream/1.cc     |  28 ++
> >> .../testsuite/27_io/basic_syncstream/2.cc     |  28 ++
> >> .../27_io/basic_syncstream/basic_ops/1.cc     | 134 +++++++
> >> .../basic_syncstream/requirements/types.cc    |  43 +++
> >> 15 files changed, 939 insertions(+), 1 deletion(-)
> >> create mode 100644 libstdc++-v3/include/std/syncstream
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/1.cc
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/2.cc
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/basic_ops/1.cc
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/requirements/types.cc
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncbuf/sync_ops/1.cc
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/1.cc
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/2.cc
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/basic_ops/1.cc
> >> create mode 100644 libstdc++-v3/testsuite/27_io/basic_syncstream/requirements/types.cc
> >>
> >>diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in
> >>index 9b49a15d31b..320f6dea688 100644
> >>--- a/libstdc++-v3/doc/doxygen/user.cfg.in
> >>+++ b/libstdc++-v3/doc/doxygen/user.cfg.in
> >>@@ -897,6 +897,7 @@ INPUT                  = @srcdir@/doc/doxygen/doxygroups.cc \
> >>                          include/streambuf \
> >>                          include/string \
> >>                          include/string_view \
> >>+                         include/syncstream \
> >>                          include/system_error \
> >>                          include/thread \
> >>                          include/tuple \
> >>diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
> >>index c90ac555e15..8652b921274 100644
> >>--- a/libstdc++-v3/include/Makefile.am
> >>+++ b/libstdc++-v3/include/Makefile.am
> >>@@ -73,6 +73,7 @@ std_headers = \
> >>      ${std_srcdir}/shared_mutex \
> >>      ${std_srcdir}/span \
> >>      ${std_srcdir}/sstream \
> >>+     ${std_srcdir}/syncstream \
> >>      ${std_srcdir}/stack \
> >>      ${std_srcdir}/stdexcept \
> >>      ${std_srcdir}/stop_token \
> >>diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
> >>index 7518a98c25a..8899c323a28 100644
> >>--- a/libstdc++-v3/include/precompiled/stdc++.h
> >>+++ b/libstdc++-v3/include/precompiled/stdc++.h
> >>@@ -141,6 +141,6 @@
> >> #include <ranges>
> >> #include <span>
> >> #include <stop_token>
> >>-// #include <syncstream>
> >>+#include <syncstream>
> >> #include <version>
> >> #endif
> >>diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
> >>new file mode 100644
> >>index 00000000000..ff96ca6cf59
> >>--- /dev/null
> >>+++ b/libstdc++-v3/include/std/syncstream
> >>@@ -0,0 +1,333 @@
> >>+// <syncstream> -*- 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.
> >>+
> >>+// Under Section 7 of GPL version 3, you are granted additional
> >>+// permissions described in the GCC Runtime Library Exception, version
> >>+// 3.1, as published by the Free Software Foundation.
> >>+
> >>+// You should have received a copy of the GNU General Public License and
> >>+// a copy of the GCC Runtime Library Exception along with this program;
> >>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
> >>+// <http://www.gnu.org/licenses/>.
> >>+
> >>+/** @file include/syncstream
> >>+ *  This is a Standard C++ Library header.
> >>+ */
> >>+
> >>+#ifndef _GLIBCXX_SYNCSTREAM
> >>+#define _GLIBCXX_SYNCSTREAM 1
> >>+
> >>+#if __cplusplus > 201703L
> >>+
> >>+#include <bits/c++config.h>
> >>+#if _GLIBCXX_USE_CXX11_ABI
> >>+
> >>+#define __cpp_lib_syncbuf 201803L
> >>+
> >>+#pragma GCC system_header
> >>+
> >>+#include <sstream>
> >>+
> >>+#include <bits/alloc_traits.h>
> >>+#include <bits/allocator.h>
> >>+#include <bits/functexcept.h>
> >>+#include <bits/functional_hash.h>
> >>+
> >>+#if _GLIBCXX_HAS_GTHREADS
> >>+# include <bits/std_mutex.h>
> >>+#endif
> >>+
> >>+namespace std _GLIBCXX_VISIBILITY(default)
> >>+{
> >>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>+
> >>+  template<typename _CharT, typename _Traits = char_traits<_CharT>,
> >>+         typename _Alloc = allocator<_CharT>>
> >>+    class basic_syncbuf : public basic_streambuf<_CharT, _Traits>
> >>+    {
> >>+    public:
> >>+      using char_type = _CharT;
> >>+      using int_type = typename _Traits::int_type;
> >>+      using pos_type = typename _Traits::pos_type;
> >>+      using off_type = typename _Traits::off_type;
> >>+      using traits_type = _Traits;
> >>+      using allocator_type = _Alloc;
> >>+      using streambuf_type = basic_streambuf<_CharT, _Traits>;
> >>+
> >>+      basic_syncbuf()
> >>+      : basic_syncbuf(nullptr, allocator_type{})
> >>+      { }
> >>+
> >>+      explicit
> >>+      basic_syncbuf(streambuf_type* __obuf)
> >>+     : basic_syncbuf(__obuf, allocator_type{})
> >>+      { }
> >>+
> >>+      basic_syncbuf(streambuf_type* __obuf, const allocator_type& __alloc)
> >>+     : _M_wrapped(__obuf)
> >>+     , _M_impl(__alloc)
> >>+     , _M_mtx(__obuf)
> >>+      { }
> >>+
> >>+      basic_syncbuf(basic_syncbuf&& __other)
> >>+     : _M_wrapped(__other._M_wrapped)
> >>+     , _M_impl(std::move(__other._M_impl))
> >>+     , _M_mtx(std::move(__other._M_mtx))
> >>+     , _M_emit_on_sync(__other._M_emit_on_sync)
> >>+     , _M_needs_sync(__other._M_needs_sync)
> >>+      {
> >>+     __other._M_wrapped = nullptr;
> >>+      }
> >>+
> >>+      ~basic_syncbuf()
> >>+      {
> >>+     __try
> >>+       {
> >>+         emit();
> >>+       }
> >>+     __catch (...)
> >>+       { }
> >>+      }
> >>+
> >>+      basic_syncbuf& operator=(basic_syncbuf&& __other)
> >>+      {
> >>+     if (std::__addressof(__other) != this)
> >>+       {
> >>+         emit();
> >>+
> >>+         _M_impl = std::move(__other._M_impl);
> >>+         _M_wrapped = __other._M_wrapped; __other._M_wrapped = nullptr;
> >>+         _M_mtx = std::move(__other._M_mtx);
> >>+         _M_emit_on_sync = __other._M_emit_on_sync;
> >>+         _M_needs_sync = __other._M_needs_sync;
> >>+       }
> >>+     return *this;
> >>+      }
> >>+
> >>+      void
> >>+      swap(basic_syncbuf& __other)
> >>+      {
> >>+     if (std::__addressof(__other) != this)
> >>+       {
> >>+         std::swap(_M_impl, __other._M_impl);
> >>+         std::swap(_M_wrapped, __other._M_wrapped);
> >>+         std::swap(_M_mtx, __other._M_mtx);
> >>+         std::swap(_M_emit_on_sync, __other._M_emit_on_sync);
> >>+         std::swap(_M_needs_sync, __other._M_needs_sync);
> >>+       }
> >>+      }
> >>+
> >>+      bool
> >>+      emit()
> >>+      {
> >>+     if (!_M_wrapped)
> >>+       return false;
> >>+
> >>+     auto __s = _M_impl.view();
> >>+     if (__s.empty())
> >>+       return true;
> >>+
> >>+     const lock_guard<__mutex> __l(_M_mtx);
> >>+     if (_M_wrapped->sputn(__s.data(), __s.size()) != __s.size())
> >>+       return false;
> >>+
> >>+     if (_M_needs_sync)
> >>+       {
> >>+         _M_needs_sync = false;
> >>+         if (_M_wrapped->pubsync() != 0)
> >>+           return false;
> >>+       }
> >>+
> >>+     _M_impl.str("");
> >>+     return true;
> >>+      }
> >>+
> >>+      streambuf_type*
> >>+      get_wrapped() const noexcept
> >>+      { return _M_wrapped; }
> >>+
> >>+      allocator_type get_allocator() const noexcept
> >>+      { return _M_impl.get_allocator(); }
> >>+
> >>+      void
> >>+      set_emit_on_sync(bool __b) noexcept
> >>+      { _M_emit_on_sync = __b; }
> >>+
> >>+    protected:
> >>+      int
> >>+      sync() override
> >>+      {
> >>+     auto __res = _M_impl.pubsync();
> >>+     if (__res == 0)
> >>+       {
> >>+         _M_needs_sync = true;
> >>+         if (_M_emit_on_sync)
> >>+           return emit() ? 0 : -1;
> >>+       }
> >>+     return __res;
> >>+      }
> >>+
> >>+      streamsize
> >>+      xsputn(const char_type* __s, streamsize __n) override
> >>+      { return _M_impl.sputn(__s, __n); }
> >>+
> >>+    private:
> >>+      streambuf_type* _M_wrapped;
> >>+
> >>+      using __impl_type = basic_stringbuf<char_type, traits_type,
> >>+                                       allocator_type>;
> >>+      __impl_type _M_impl;
> >>+
> >>+      struct __mutex
> >>+      {
> >>+#if _GLIBCXX_HAS_GTHREADS
> >>+     mutex* _M_mtx;
> >>+
> >>+     __mutex(void* __t)
> >
> > Make this 'explicit' please.
> >
> >>+       : _M_mtx(__t ? &_S_get_mutex(__t) : nullptr)
> >>+     { }
> >>+
> >>+     void
> >>+     swap(__mutex& __other) noexcept
> >>+     { std::swap(_M_mtx, __other._M_mtx); }
> >>+
> >>+     void
> >>+     lock()
> >>+     {
> >>+       if (_M_mtx)
> >>+         _M_mtx->lock();
> >>+     }
> >>+
> >>+     void
> >>+     unlock()
> >>+     {
> >>+       if (_M_mtx)
> >>+         _M_mtx->unlock();
> >>+     }
> >>+
> >>+     // FIXME: This should be put in the .so
> >>+     static mutex&
> >>+     _S_get_mutex(void* __t)
> >>+     {
> >>+       const unsigned char __mask = 0xf;
> >>+       static mutex __m[__mask + 1];
> >>+
> >>+       auto __key = _Hash_impl::hash(__t) & __mask;
> >>+       return __m[__key];
> >>+     }
> >>+#else
> >>+     __mutex(void*)
> >
> > And 'explicit' here too.
> >
> >>+     { }
> >>+
> >>+     void
> >>+     swap(__mutex&&) noexcept
> >
> > This needs to be an lvalue reference, or it won't compile.
> >
> >>+     { }
> >>+
> >>+     void
> >>+     lock()
> >>+     { }
> >>+
> >>+     void
> >>+     unlock()
> >>+     { }
> >
> > All these completely empty functions can be put on one line:
> >
> >       void unlock() { }
> >
> > There's no need for these no-op members take up so many lines.
> >
> >>+#endif
> >>+     __mutex(const __mutex&) = delete;
> >>+     __mutex& operator=(const __mutex&) = delete;
> >
> > These are redundant (the user-declared moves below cause the copies to
> > be deleted) but harmless.
> >
> >>+
> >>+     __mutex(__mutex&&) = default;
> >>+     __mutex& operator=(__mutex&&) = default;
> >>+      };
> >>+      __mutex _M_mtx;
> >>+
> >>+      bool _M_emit_on_sync = false;
> >>+      bool _M_needs_sync = false;
> >>+    };
> >
> >
> > OK for trunk, thanks.
>

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

* Re: [PATCH] libstdc++: Add c++2a <syncstream>
  2020-11-03 21:22               ` Christophe Lyon
@ 2020-11-03 22:00                 ` Jonathan Wakely
  0 siblings, 0 replies; 16+ messages in thread
From: Jonathan Wakely @ 2020-11-03 22:00 UTC (permalink / raw)
  To: Christophe Lyon; +Cc: Thomas Rodgers, libstdc++, gcc Patches

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

On 03/11/20 22:22 +0100, Christophe Lyon via Libstdc++ wrote:
>On Mon, 2 Nov 2020 at 19:43, Thomas Rodgers via Gcc-patches
><gcc-patches@gcc.gnu.org> wrote:
>>
>>
>> Testsed x86_64-pc-linux-gnu, committed to master.
>>
>
>Hi,
>
>I can see the new tests failing on bare-metal targets using newlib
>(arm-eabi, aarch64-elf):
>    27_io/basic_syncbuf/1.cc (test for excess errors)
>    27_io/basic_syncbuf/basic_ops/1.cc (test for excess errors)
>    27_io/basic_syncbuf/requirements/types.cc (test for excess errors)
>    27_io/basic_syncstream/1.cc (test for excess errors)
>    27_io/basic_syncstream/basic_ops/1.cc (test for excess errors)
>    27_io/basic_syncstream/requirements/types.cc (test for excess errors)
>
>because:
>libstdc++-v3/include/syncstream:142: error: 'lock_guard' does not name a type

Fixed like so. Tested powerpc64le-linux (--disable-threads) and pushed
to trunk.



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

commit e1276e334298251d73303999d2adc688abbfc856
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Nov 3 21:56:44 2020

    libstdc++: Ensure std::lock_guard is declared
    
    libstdc++-v3/ChangeLog:
    
            * include/std/syncstream: Include <bits/std_mutex.h>
            unconditionally.

diff --git a/libstdc++-v3/include/std/syncstream b/libstdc++-v3/include/std/syncstream
index 5e0864ec54a8..9d1db0cf286e 100644
--- a/libstdc++-v3/include/std/syncstream
+++ b/libstdc++-v3/include/std/syncstream
@@ -44,10 +44,7 @@
 #include <bits/allocator.h>
 #include <bits/functexcept.h>
 #include <bits/functional_hash.h>
-
-#if _GLIBCXX_HAS_GTHREADS
-# include <bits/std_mutex.h>
-#endif
+#include <bits/std_mutex.h>
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {

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

end of thread, other threads:[~2020-11-03 22:00 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-15 14:46 [PATCH] libstdc++: Add c++2a <syncstream> Thomas Rodgers
2020-10-15 18:13 ` Jonathan Wakely
2020-10-15 18:18 ` Jonathan Wakely
2020-10-15 19:29 ` Jonathan Wakely
2020-10-16  0:24   ` Thomas Rodgers
2020-10-21 16:53   ` Thomas Rodgers
2020-10-21 17:34     ` Jonathan Wakely
2020-10-21 19:39       ` Thomas Rodgers
2020-10-29 16:18     ` Jonathan Wakely
2020-10-29 21:12       ` Thomas Rodgers
2020-11-02 15:58         ` Thomas Rodgers
2020-11-02 16:10         ` Thomas Rodgers
2020-11-02 17:38           ` Jonathan Wakely
2020-11-02 18:43             ` Thomas Rodgers
2020-11-03 21:22               ` Christophe Lyon
2020-11-03 22:00                 ` Jonathan Wakely

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).