diff mbox series

libstdc++: Add c++2a <syncstream>

Message ID 20201015144606.1402141-1-rodgert@appliantology.com
State New
Headers show
Series libstdc++: Add c++2a <syncstream> | expand

Commit Message

Thomas Rodgers Oct. 15, 2020, 2:46 p.m. UTC
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

Comments

Jonathan Wakely Oct. 15, 2020, 6:13 p.m. UTC | #1
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
Jonathan Wakely Oct. 15, 2020, 6:18 p.m. UTC | #2
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.
Jonathan Wakely Oct. 15, 2020, 7:29 p.m. UTC | #3
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.
diff mbox series

Patch

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>;
+}