diff mbox series

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

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

Commit Message

Thomas Rodgers Oct. 29, 2020, 9:12 p.m. UTC
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 mbox series

Patch

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