From 440613da406f3c5f88a700340dcb472f72fe807a Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Mon, 11 Jan 2021 00:21:27 +0000 Subject: [PATCH 01/60] Implementation proposal of P0053R7 basic_syncbuf --- stl/inc/iosfwd | 8 + stl/inc/syncstream | 356 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 364 insertions(+) create mode 100644 stl/inc/syncstream diff --git a/stl/inc/iosfwd b/stl/inc/iosfwd index 2eea4b96749..fcfac770605 100644 --- a/stl/inc/iosfwd +++ b/stl/inc/iosfwd @@ -200,6 +200,10 @@ template > class basic_ofstream; template > class basic_fstream; +template , class _Alloc = allocator<_Elem>> +class basic_syncbuf; +template , class _Alloc = allocator<_Elem>> +class basic_osyncstream; #if defined(_DLL_CPPLIB) template @@ -224,6 +228,8 @@ using filebuf = basic_filebuf>; using ifstream = basic_ifstream>; using ofstream = basic_ofstream>; using fstream = basic_fstream>; +using syncbuf = basic_syncbuf; +using osyncstream = basic_osyncstream; // wchar_t TYPEDEFS using wios = basic_ios>; @@ -239,6 +245,8 @@ using wfilebuf = basic_filebuf>; using wifstream = basic_ifstream>; using wofstream = basic_ofstream>; using wfstream = basic_fstream>; +using wsyncbuf = basic_syncbuf; +using wosyncstream = basic_osyncstream; #if defined(_CRTBLD) // unsigned short TYPEDEFS diff --git a/stl/inc/syncstream b/stl/inc/syncstream new file mode 100644 index 00000000000..5f110765f75 --- /dev/null +++ b/stl/inc/syncstream @@ -0,0 +1,356 @@ +// syncstream standard header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _SYNCSTREAM_ +#define _SYNCSTREAM_ +#include +#if _STL_COMPILER_PREPROCESSOR +#define __cpp_lib_syncbuf // Remove before commit, visual +#ifndef __cpp_lib_syncbuf +#pragma message("The contents of are available only with C++20 syncbuf support.") +#else // ^^^ !defined(__cpp_lib_syncbuf) / defined(__cpp_lib_syncbuf) vvv +#include +#include +#include +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#pragma push_macro("emit") +#undef new +#undef emit + +_STD_BEGIN +struct _Basic_syncbuf_global_mutex { +private: + struct _Mutex_count_pair { + mutex* _Mutex; + uint64_t _Ref_count; + + _Mutex_count_pair() : _Ref_count(0) { + _Mutex = new mutex; + } + _Mutex_count_pair(_Mutex_count_pair&& _Elem) noexcept + : _Mutex(_STD move(_Elem._Mutex)), _Ref_count(_Elem._Ref_count) { + _Elem._Mutex = nullptr; + _Elem._Ref_count = 0; + } + _Mutex_count_pair& operator=(_Mutex_count_pair&& _Elem) noexcept { + _Mutex = _STD move(_Elem._Mutex); + _Ref_count = _Elem._Ref_count; + _Elem._Mutex = nullptr; + _Elem._Ref_count = 0; + + return *this; + } + ~_Mutex_count_pair() { + if (_Mutex) + delete _Mutex; + } + _Mutex_count_pair(const _Mutex_count_pair&) = delete; + _Mutex_count_pair& operator=(const _Mutex_count_pair&) = delete; + }; + static map& _Get_mutex_map() noexcept { + static map _Mutex_Map; + return _Mutex_Map; + } + static mutex& _Get_map_mutex() noexcept { + static mutex _Mutex; + return _Mutex; + } + static mutex& _Get_mutex_for_instance(void* _Ptr) { + mutex& _Mutex = _Get_map_mutex(); + lock_guard _Guard(_Mutex); + + map& _Mutex_map = _Get_mutex_map(); + _Mutex_count_pair& _Instance_mutex = _Mutex_map[_Ptr]; + _STL_ASSERT(_Instance_mutex._Ref_count != 0, "No mutex exists for given instance!"); + return *_Instance_mutex._Mutex; + } + static void _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept { + mutex& _Mutex = _Get_map_mutex(); + lock_guard _Guard(_Mutex); + + map& _Mutex_map = _Get_mutex_map(); + if (!_Mutex_map.contains(_Ptr)) { + _Mutex_map[_Ptr] = _STD move(_Mutex_count_pair{}); + } + _Mutex_map[_Ptr]._Ref_count++; + } + static void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept { + mutex& _Mutex = _Get_map_mutex(); + lock_guard _Guard(_Mutex); + + map& _Mutex_map = _Get_mutex_map(); + if (!_Mutex_map.contains(_Ptr)) { + return; + } + _Mutex_count_pair& _Instance_mutex = _Mutex_map[_Ptr]; + --_Instance_mutex._Ref_count; + if (_Instance_mutex._Ref_count == 0) { + _Mutex_map.erase(_Ptr); + } + } + + template , class _Alloc = allocator<_Elem>> + friend class basic_syncbuf; +}; + +// CLASS TEMPLATE basic_syncbuf +template , class _Alloc = allocator<_Elem>> +class basic_syncbuf : public basic_streambuf<_Elem, _Traits> { +private: + enum { // constant for minimum buffer size + _MINSIZE = 32 + }; + + void _Init() { + const pointer _New_ptr = _Unfancy(_Al.allocate(_MINSIZE)); + streambuf_type::setp(_New_ptr, _New_ptr + _MINSIZE); + } + + void _Tidy() noexcept { + const auto _Buf_size = static_cast::size_type>( + streambuf_type::epptr() - streambuf_type::pbase()); + _Al.deallocate(pointer_traits::pointer_to(*streambuf_type::pbase()), _Buf_size); + + streambuf_type::setp(nullptr, nullptr, nullptr); + _Wrapped = nullptr; + } + + void _Assign_rv(basic_syncbuf&& _Right) { + if (this != _STD addressof(_Right)) { + _Tidy(); + this->_Swap_except_al(_Right); + } + } + +public: + using int_type = typename _Traits::int_type; + using pos_type = typename _Traits::pos_type; + using off_type = typename _Traits::off_type; + using allocator_type = _Alloc; + using streambuf_type = basic_streambuf<_Elem, _Traits>; + + using pointer = typename allocator_traits<_Alloc>::pointer; + using size_type = typename allocator_traits::size_type; + + basic_syncbuf() : basic_syncbuf(nullptr) {} + + explicit basic_syncbuf(streambuf_type* _StrBuf) : basic_syncbuf(_StrBuf, _Alloc()) {} + + basic_syncbuf(streambuf_type* _StrBuf, const _Alloc& _Al) : _Wrapped(_StrBuf), _Al(_Al) { + _Basic_syncbuf_global_mutex::_Add_mutex_for_instance_or_increment(static_cast(_Wrapped)); + _Init(); + } + + basic_syncbuf(basic_syncbuf&& _Right) { + _Al = _STD move(_Right._Al); + _Assign_rv(_STD move(_Right)); + } + + ~basic_syncbuf() { + _Emit(); + const auto _Buf_size = streambuf_type::epptr() - streambuf_type::pbase(); + _Al.deallocate(streambuf_type::pbase(), _Buf_size); + _Basic_syncbuf_global_mutex::_Delete_mutex_for_instance_or_decrement(static_cast(_Wrapped)); + } + +private: + void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) noexcept { + _Tidy(); + _Pocma(_Getal(), _Right._Getal()); + this->_Swap_except_al(_Right); + } + + void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) noexcept { + _Tidy(); + _Pocma(_Getal(), _Right._Getal()); + this->_Swap_except_al(_Right); + } + + void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { + if (_Getal() == _Right._Getal()) { + _Move_assign(_Right, _Equal_allocators{}); + } else { + const auto _Right_Buf_size = _Right._Get_buffer_size(); + const auto _Right_Data_size = _Right._Get_data_size(); + + const pointer _New_ptr = _Unfancy(_Al.allocate(_Right_Buf_size)); + _Traits::copy(_New_ptr, pointer_traits::pointer_to(*_Right.pbase()), _Right_Data_size); + + streambuf_type::setp(_New_ptr, _New_ptr + _Right_Data_size, _New_ptr + _Right_Buf_size); + streambuf_type::_Plocale = _STD move(_Right._Plocale); + + _Right._Tidy(); + } + } + +public: + basic_syncbuf& operator=(basic_syncbuf&& _Right) { + _Emit(); + _Move_assign(_STD move(_Right), _Choose_pocma<_Alloc>{}); + return *this; + } + +private: + void _Swap_except_al(basic_syncbuf& _Right) { + streambuf_type::swap(_Right); + _STD swap(_Emit_on_sync, _Right._Emit_on_sync); + _STD swap(_Record_sync, _Right._Record_sync); + _STD swap(_Wrapped, _Right._Wrapped); + } + +public: + void swap(basic_syncbuf& _Right) { + if (this != _STD addressof(_Right)) { + _Pocs(_Getal(), _Right._Getal()); + _Swap_except_al(_Right); + } + } + +private: + bool _Emit() noexcept { + _TRY_BEGIN + return emit(); + _CATCH_ALL + _CATCH_END + } + +public: + bool emit() { + if (!_Wrapped) { + return false; + } + + bool _Result = true; + const auto _Data_size = _Get_data_size(); + const auto _Begin_seq_ptr = streambuf_type::pbase(); + if (_Data_size > 0 || _Record_sync) { + lock_guard _Guard( + _Basic_syncbuf_global_mutex::_Get_mutex_for_instance(static_cast(_Wrapped))); + + if (_Data_size > 0 && _Data_size != _Wrapped->sputn(_Begin_seq_ptr, _Data_size)) { + _Result = false; + } + + if (_Record_sync) { + if (_Wrapped->pubsync() == -1) { + _Result = false; + } + } + } + _Record_sync = false; + streambuf_type::setp(_Begin_seq_ptr, streambuf_type::epptr()); // reset written data + return _Result; + } + + streambuf_type* get_wrapped() const noexcept { + return _Wrapped; + } + + allocator_type get_allocator() const noexcept { + return _Getal(); + } + + void set_emit_on_sync(bool _Val) noexcept { + _Emit_on_sync = _Val; + } + +protected: + int sync() override { + _Record_sync = true; + + if (_Emit_on_sync) { + if (!emit()) { + return -1; + } + } + return 0; + } + + int_type overflow(int_type _Current_elem) override { + if (!_Wrapped) + return _Traits::eof(); + const bool _Chk_eof = _Traits::eq_int_type(_Current_elem, _Traits::eof()); + if (_Chk_eof) + return _Traits::not_eof(_Current_elem); + + const size_type _Buf_size = _Get_buffer_size(); + const size_type _Max_allocation = allocator_traits::max_size(_Al); + if (_Buf_size == _Max_allocation) + return _Traits::eof(); + + const size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); + const pointer _Old_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); + const size_type _Old_data_size = _Get_data_size(); + + const pointer _New_ptr = _Al.allocate(_New_capacity); + ::memcpy(_New_ptr, _Old_ptr, _Old_data_size); + + streambuf_type::setp(_New_ptr, _New_ptr + _Old_data_size, _New_ptr + _New_capacity); + streambuf_type::sputc(_Current_elem); + + return _Current_elem; + } + +private: + size_type _Calculate_growth(const size_type _Oldsize, const size_type _Newsize, const size_type _Maxsize) const { + if (_Oldsize > _Maxsize - _Oldsize / 2) { + return _Maxsize; // geometric growth would overflow + } + + const size_type _Geometric = _Oldsize + _Oldsize / 2; + + if (_Geometric < _Newsize) { + return _Newsize; // geometric growth would be insufficient + } + + return _Geometric; // geometric growth is sufficient + } + + size_type _Get_data_size() const noexcept { + return static_cast::size_type>( + streambuf_type::pptr() - streambuf_type::pbase()); + } + + size_type _Get_buffer_size() const noexcept { + return static_cast::size_type>( + streambuf_type::epptr() - streambuf_type::pbase()); + } + + _Alloc& _Getal() noexcept { + return _Al; + } + + const _Alloc& _Getal() const noexcept { + return _Al; + } + + streambuf_type* _Wrapped; + allocator_type _Al; // the allocator object + bool _Emit_on_sync; + bool _Record_sync; +}; + +template +void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, basic_syncbuf<_Elem, _Traits, _Alloc>& _Right) { + _Left.swap(_Right); +} + +_STD_END + +#pragma pop_macro("emit") +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // __cpp_lib_syncbuf +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _SYNCSTREAM_ \ No newline at end of file From d8e0433cc2c41c99a4cac0f0da09c661937153e6 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Mon, 11 Jan 2021 14:43:56 +0000 Subject: [PATCH 02/60] Updates to syncbuf implementation, syncstream header to build system - Re-structure _Basic_sync_global_mutex - Re-organize functions in basic_syncbuf - Update related files when adding a new header --- stl/CMakeLists.txt | 1 + stl/inc/__msvc_all_public_headers.hpp | 1 + stl/inc/syncstream | 275 ++++++++---------- .../custom_format.py | 2 +- .../custombuild.pl | 2 +- .../test.cpp | 2 +- .../include_each_header_alone_matrix.lst | 1 + 7 files changed, 135 insertions(+), 149 deletions(-) diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 6f2686e6de2..cf3ab6edf98 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -186,6 +186,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/string ${CMAKE_CURRENT_LIST_DIR}/inc/string_view ${CMAKE_CURRENT_LIST_DIR}/inc/strstream + ${CMAKE_CURRENT_LIST_DIR}/inc/syncstream ${CMAKE_CURRENT_LIST_DIR}/inc/system_error ${CMAKE_CURRENT_LIST_DIR}/inc/thread ${CMAKE_CURRENT_LIST_DIR}/inc/tuple diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index b238fdb7602..65b45f62770 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -120,6 +120,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 5f110765f75..1e4669f3b56 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -6,7 +6,7 @@ #pragma once #ifndef _SYNCSTREAM_ #define _SYNCSTREAM_ -#include +#include #if _STL_COMPILER_PREPROCESSOR #define __cpp_lib_syncbuf // Remove before commit, visual #ifndef __cpp_lib_syncbuf @@ -30,31 +30,15 @@ _STD_BEGIN struct _Basic_syncbuf_global_mutex { private: struct _Mutex_count_pair { - mutex* _Mutex; + mutex _Mutex; uint64_t _Ref_count; - _Mutex_count_pair() : _Ref_count(0) { - _Mutex = new mutex; - } - _Mutex_count_pair(_Mutex_count_pair&& _Elem) noexcept - : _Mutex(_STD move(_Elem._Mutex)), _Ref_count(_Elem._Ref_count) { - _Elem._Mutex = nullptr; - _Elem._Ref_count = 0; - } - _Mutex_count_pair& operator=(_Mutex_count_pair&& _Elem) noexcept { - _Mutex = _STD move(_Elem._Mutex); - _Ref_count = _Elem._Ref_count; - _Elem._Mutex = nullptr; - _Elem._Ref_count = 0; - - return *this; - } - ~_Mutex_count_pair() { - if (_Mutex) - delete _Mutex; - } + _Mutex_count_pair() : _Ref_count(0), _Mutex(mutex{}) {} + ~_Mutex_count_pair() = default; + _Mutex_count_pair(_Mutex_count_pair&&) = delete; _Mutex_count_pair(const _Mutex_count_pair&) = delete; _Mutex_count_pair& operator=(const _Mutex_count_pair&) = delete; + _Mutex_count_pair& operator=(_Mutex_count_pair&&) = delete; }; static map& _Get_mutex_map() noexcept { static map _Mutex_Map; @@ -66,35 +50,39 @@ private: } static mutex& _Get_mutex_for_instance(void* _Ptr) { mutex& _Mutex = _Get_map_mutex(); - lock_guard _Guard(_Mutex); + { + lock_guard _Guard(_Mutex); - map& _Mutex_map = _Get_mutex_map(); - _Mutex_count_pair& _Instance_mutex = _Mutex_map[_Ptr]; - _STL_ASSERT(_Instance_mutex._Ref_count != 0, "No mutex exists for given instance!"); - return *_Instance_mutex._Mutex; + auto& _Mutex_map = _Get_mutex_map(); + auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); + + _STL_ASSERT(_Instance_mutex_itr != _Mutex_map.end(), "No mutex exists for given instance!"); + return _Instance_mutex_itr->second._Mutex; + } } static void _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept { mutex& _Mutex = _Get_map_mutex(); - lock_guard _Guard(_Mutex); + { + lock_guard _Guard(_Mutex); - map& _Mutex_map = _Get_mutex_map(); - if (!_Mutex_map.contains(_Ptr)) { - _Mutex_map[_Ptr] = _STD move(_Mutex_count_pair{}); + auto& _Mutex_map = _Get_mutex_map(); + _Mutex_map.emplace(std::piecewise_construct, std::forward_as_tuple(_Ptr), std::forward_as_tuple()) + .first->second._Ref_count++; } - _Mutex_map[_Ptr]._Ref_count++; } static void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept { mutex& _Mutex = _Get_map_mutex(); - lock_guard _Guard(_Mutex); - - map& _Mutex_map = _Get_mutex_map(); - if (!_Mutex_map.contains(_Ptr)) { - return; - } - _Mutex_count_pair& _Instance_mutex = _Mutex_map[_Ptr]; - --_Instance_mutex._Ref_count; - if (_Instance_mutex._Ref_count == 0) { - _Mutex_map.erase(_Ptr); + { + lock_guard _Guard(_Mutex); + + auto& _Mutex_map = _Get_mutex_map(); + auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); + if (_Instance_mutex_itr == _Mutex_map.end()) + return; + _Instance_mutex_itr->second._Ref_count--; + if (_Instance_mutex_itr->second._Ref_count == 0) { + _Mutex_map.erase(_Ptr); + } } } @@ -105,32 +93,6 @@ private: // CLASS TEMPLATE basic_syncbuf template , class _Alloc = allocator<_Elem>> class basic_syncbuf : public basic_streambuf<_Elem, _Traits> { -private: - enum { // constant for minimum buffer size - _MINSIZE = 32 - }; - - void _Init() { - const pointer _New_ptr = _Unfancy(_Al.allocate(_MINSIZE)); - streambuf_type::setp(_New_ptr, _New_ptr + _MINSIZE); - } - - void _Tidy() noexcept { - const auto _Buf_size = static_cast::size_type>( - streambuf_type::epptr() - streambuf_type::pbase()); - _Al.deallocate(pointer_traits::pointer_to(*streambuf_type::pbase()), _Buf_size); - - streambuf_type::setp(nullptr, nullptr, nullptr); - _Wrapped = nullptr; - } - - void _Assign_rv(basic_syncbuf&& _Right) { - if (this != _STD addressof(_Right)) { - _Tidy(); - this->_Swap_except_al(_Right); - } - } - public: using int_type = typename _Traits::int_type; using pos_type = typename _Traits::pos_type; @@ -139,14 +101,16 @@ public: using streambuf_type = basic_streambuf<_Elem, _Traits>; using pointer = typename allocator_traits<_Alloc>::pointer; - using size_type = typename allocator_traits::size_type; + using size_type = typename allocator_traits<_Alloc>::size_type; - basic_syncbuf() : basic_syncbuf(nullptr) {} + basic_syncbuf() : basic_syncbuf(nullptr){}; - explicit basic_syncbuf(streambuf_type* _StrBuf) : basic_syncbuf(_StrBuf, _Alloc()) {} + explicit basic_syncbuf(streambuf_type* _StrBuf) : basic_syncbuf(_StrBuf, _Alloc{}) {} basic_syncbuf(streambuf_type* _StrBuf, const _Alloc& _Al) : _Wrapped(_StrBuf), _Al(_Al) { - _Basic_syncbuf_global_mutex::_Add_mutex_for_instance_or_increment(static_cast(_Wrapped)); + if (_Wrapped != nullptr) { + _Basic_syncbuf_global_mutex::_Add_mutex_for_instance_or_increment(static_cast(_Wrapped)); + } _Init(); } @@ -157,57 +121,15 @@ public: ~basic_syncbuf() { _Emit(); - const auto _Buf_size = streambuf_type::epptr() - streambuf_type::pbase(); - _Al.deallocate(streambuf_type::pbase(), _Buf_size); - _Basic_syncbuf_global_mutex::_Delete_mutex_for_instance_or_decrement(static_cast(_Wrapped)); - } - -private: - void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) noexcept { _Tidy(); - _Pocma(_Getal(), _Right._Getal()); - this->_Swap_except_al(_Right); - } - - void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) noexcept { - _Tidy(); - _Pocma(_Getal(), _Right._Getal()); - this->_Swap_except_al(_Right); - } - - void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { - if (_Getal() == _Right._Getal()) { - _Move_assign(_Right, _Equal_allocators{}); - } else { - const auto _Right_Buf_size = _Right._Get_buffer_size(); - const auto _Right_Data_size = _Right._Get_data_size(); - - const pointer _New_ptr = _Unfancy(_Al.allocate(_Right_Buf_size)); - _Traits::copy(_New_ptr, pointer_traits::pointer_to(*_Right.pbase()), _Right_Data_size); - - streambuf_type::setp(_New_ptr, _New_ptr + _Right_Data_size, _New_ptr + _Right_Buf_size); - streambuf_type::_Plocale = _STD move(_Right._Plocale); - - _Right._Tidy(); - } } -public: basic_syncbuf& operator=(basic_syncbuf&& _Right) { _Emit(); _Move_assign(_STD move(_Right), _Choose_pocma<_Alloc>{}); return *this; } -private: - void _Swap_except_al(basic_syncbuf& _Right) { - streambuf_type::swap(_Right); - _STD swap(_Emit_on_sync, _Right._Emit_on_sync); - _STD swap(_Record_sync, _Right._Record_sync); - _STD swap(_Wrapped, _Right._Wrapped); - } - -public: void swap(basic_syncbuf& _Right) { if (this != _STD addressof(_Right)) { _Pocs(_Getal(), _Right._Getal()); @@ -215,23 +137,14 @@ public: } } -private: - bool _Emit() noexcept { - _TRY_BEGIN - return emit(); - _CATCH_ALL - _CATCH_END - } - -public: bool emit() { - if (!_Wrapped) { + if (_Wrapped == nullptr) { return false; } - bool _Result = true; - const auto _Data_size = _Get_data_size(); - const auto _Begin_seq_ptr = streambuf_type::pbase(); + bool _Result = true; + const size_type _Data_size = _Get_data_size(); + const pointer _Begin_seq_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); if (_Data_size > 0 || _Record_sync) { lock_guard _Guard( _Basic_syncbuf_global_mutex::_Get_mutex_for_instance(static_cast(_Wrapped))); @@ -247,7 +160,8 @@ public: } } _Record_sync = false; - streambuf_type::setp(_Begin_seq_ptr, streambuf_type::epptr()); // reset written data + streambuf_type::setp( + _Begin_seq_ptr, pointer_traits::pointer_to(*streambuf_type::epptr())); // reset written data return _Result; } @@ -276,14 +190,14 @@ protected: } int_type overflow(int_type _Current_elem) override { - if (!_Wrapped) + if (_Wrapped == nullptr) return _Traits::eof(); const bool _Chk_eof = _Traits::eq_int_type(_Current_elem, _Traits::eof()); if (_Chk_eof) return _Traits::not_eof(_Current_elem); const size_type _Buf_size = _Get_buffer_size(); - const size_type _Max_allocation = allocator_traits::max_size(_Al); + const size_type _Max_allocation = allocator_traits<_Alloc>::max_size(_Al); if (_Buf_size == _Max_allocation) return _Traits::eof(); @@ -291,8 +205,8 @@ protected: const pointer _Old_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); const size_type _Old_data_size = _Get_data_size(); - const pointer _New_ptr = _Al.allocate(_New_capacity); - ::memcpy(_New_ptr, _Old_ptr, _Old_data_size); + const pointer _New_ptr = _Unfancy(_Al.allocate(_New_capacity)); + _Traits::copy(_New_ptr, _Old_ptr, _Old_data_size); streambuf_type::setp(_New_ptr, _New_ptr + _Old_data_size, _New_ptr + _New_capacity); streambuf_type::sputc(_Current_elem); @@ -301,7 +215,78 @@ protected: } private: - size_type _Calculate_growth(const size_type _Oldsize, const size_type _Newsize, const size_type _Maxsize) const { + enum { // constant for minimum buffer size + _MINSIZE = 32 + }; + + void _Init() { + const pointer _New_ptr = _Unfancy(_Al.allocate(_MINSIZE)); + streambuf_type::setp(_New_ptr, _New_ptr + _MINSIZE); + } + + void _Tidy() noexcept { + const size_type _Buf_size = _Get_buffer_size(); + _Al.deallocate(pointer_traits::pointer_to(*streambuf_type::pbase()), _Buf_size); + + streambuf_type::setp(nullptr, nullptr, nullptr); + if (_Wrapped != nullptr) { + _Basic_syncbuf_global_mutex::_Delete_mutex_for_instance_or_decrement(static_cast(_Wrapped)); + _Wrapped = nullptr; + } + } + + void _Assign_rv(basic_syncbuf&& _Right) { + if (this != _STD addressof(_Right)) { + _Tidy(); + this->_Swap_except_al(_Right); + } + } + + void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) noexcept { + _Tidy(); + _Pocma(_Getal(), _Right._Getal()); + this->_Swap_except_al(_Right); + } + + void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) noexcept { + _Tidy(); + _Pocma(_Getal(), _Right._Getal()); + this->_Swap_except_al(_Right); + } + + void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { + if (_Getal() == _Right._Getal()) { + _Move_assign(_Right, _Equal_allocators{}); + } else { + const size_type _Right_Buf_size = _Right._Get_buffer_size(); + const size_type _Right_Data_size = _Right._Get_data_size(); + + const pointer _New_ptr = _Unfancy(_Al.allocate(_Right_Buf_size)); + _Traits::copy(_New_ptr, pointer_traits::pointer_to(*_Right.pbase()), _Right_Data_size); + + streambuf_type::setp(_New_ptr, _New_ptr + _Right_Data_size, _New_ptr + _Right_Buf_size); + streambuf_type::_Plocale = _STD move(_Right._Plocale); + + _Right._Tidy(); + } + } + + void _Swap_except_al(basic_syncbuf& _Right) { + streambuf_type::swap(_Right); + _STD swap(_Emit_on_sync, _Right._Emit_on_sync); + _STD swap(_Record_sync, _Right._Record_sync); + _STD swap(_Wrapped, _Right._Wrapped); + } + + bool _Emit() noexcept { + _TRY_BEGIN + return emit(); + _CATCH_ALL + _CATCH_END + } + + constexpr size_type _Calculate_growth( + const size_type _Oldsize, const size_type _Newsize, const size_type _Maxsize) const { if (_Oldsize > _Maxsize - _Oldsize / 2) { return _Maxsize; // geometric growth would overflow } @@ -315,28 +300,26 @@ private: return _Geometric; // geometric growth is sufficient } - size_type _Get_data_size() const noexcept { - return static_cast::size_type>( - streambuf_type::pptr() - streambuf_type::pbase()); + constexpr size_type _Get_data_size() const noexcept { + return static_cast(streambuf_type::pptr() - streambuf_type::pbase()); } - size_type _Get_buffer_size() const noexcept { - return static_cast::size_type>( - streambuf_type::epptr() - streambuf_type::pbase()); + constexpr size_type _Get_buffer_size() const noexcept { + return static_cast(streambuf_type::epptr() - streambuf_type::pbase()); } - _Alloc& _Getal() noexcept { + constexpr _Alloc& _Getal() noexcept { return _Al; } - const _Alloc& _Getal() const noexcept { + constexpr const _Alloc& _Getal() const noexcept { return _Al; } - streambuf_type* _Wrapped; - allocator_type _Al; // the allocator object - bool _Emit_on_sync; - bool _Record_sync; + streambuf_type* _Wrapped = nullptr; + allocator_type _Al; + bool _Emit_on_sync = false; + bool _Record_sync = false; }; template @@ -353,4 +336,4 @@ _STL_RESTORE_CLANG_WARNINGS #pragma pack(pop) #endif // __cpp_lib_syncbuf #endif // _STL_COMPILER_PREPROCESSOR -#endif // _SYNCSTREAM_ \ No newline at end of file +#endif // _SYNCSTREAM_ diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py index 8fe322f324d..ddf75ebbd8d 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py +++ b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py @@ -73,7 +73,7 @@ def getBuildSteps(self, test, litConfig, shared): 'string_view', 'string', 'strstream', - # 'syncstream', + 'syncstream', 'system_error', 'thread', 'tuple', diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl index b0b03595c17..563a25f985f 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl +++ b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl @@ -72,7 +72,7 @@ () "string_view", "string", "strstream", - # "syncstream", + "syncstream", "system_error", "thread", "tuple", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index d660f96e77b..ee3bad3492f 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -73,7 +73,7 @@ import ; import ; import ; import ; -// import ; +import ; import ; import ; import ; diff --git a/tests/std/tests/include_each_header_alone_matrix.lst b/tests/std/tests/include_each_header_alone_matrix.lst index 6728e9f7124..1a7ff0a6cd4 100644 --- a/tests/std/tests/include_each_header_alone_matrix.lst +++ b/tests/std/tests/include_each_header_alone_matrix.lst @@ -67,6 +67,7 @@ PM_CL="/DMEOW_HEADER=streambuf" PM_CL="/DMEOW_HEADER=string" PM_CL="/DMEOW_HEADER=string_view" PM_CL="/DMEOW_HEADER=strstream /D_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING" +PM_CL="/DMEOW_HEADER=syncstream" PM_CL="/DMEOW_HEADER=system_error" PM_CL="/DMEOW_HEADER=thread" PM_CL="/DMEOW_HEADER=tuple" From 71c6504c6cdda22536428521265ef992c3cc1289 Mon Sep 17 00:00:00 2001 From: "Michael S. Rizkalla" Date: Mon, 11 Jan 2021 15:37:13 +0000 Subject: [PATCH 03/60] Apply suggestions from code review Co-authored-by: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/syncstream | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 1e4669f3b56..af12c32aeb6 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -31,14 +31,7 @@ struct _Basic_syncbuf_global_mutex { private: struct _Mutex_count_pair { mutex _Mutex; - uint64_t _Ref_count; - - _Mutex_count_pair() : _Ref_count(0), _Mutex(mutex{}) {} - ~_Mutex_count_pair() = default; - _Mutex_count_pair(_Mutex_count_pair&&) = delete; - _Mutex_count_pair(const _Mutex_count_pair&) = delete; - _Mutex_count_pair& operator=(const _Mutex_count_pair&) = delete; - _Mutex_count_pair& operator=(_Mutex_count_pair&&) = delete; + uint64_t _Ref_count = 0; }; static map& _Get_mutex_map() noexcept { static map _Mutex_Map; @@ -51,7 +44,7 @@ private: static mutex& _Get_mutex_for_instance(void* _Ptr) { mutex& _Mutex = _Get_map_mutex(); { - lock_guard _Guard(_Mutex); + scoped_lock _Guard(_Mutex); auto& _Mutex_map = _Get_mutex_map(); auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); @@ -63,17 +56,16 @@ private: static void _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept { mutex& _Mutex = _Get_map_mutex(); { - lock_guard _Guard(_Mutex); + scoped_lock _Guard(_Mutex); auto& _Mutex_map = _Get_mutex_map(); - _Mutex_map.emplace(std::piecewise_construct, std::forward_as_tuple(_Ptr), std::forward_as_tuple()) - .first->second._Ref_count++; + _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; } } static void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept { mutex& _Mutex = _Get_map_mutex(); { - lock_guard _Guard(_Mutex); + scoped_lock _Guard(_Mutex); auto& _Mutex_map = _Get_mutex_map(); auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); @@ -91,7 +83,7 @@ private: }; // CLASS TEMPLATE basic_syncbuf -template , class _Alloc = allocator<_Elem>> +template class basic_syncbuf : public basic_streambuf<_Elem, _Traits> { public: using int_type = typename _Traits::int_type; @@ -146,7 +138,7 @@ public: const size_type _Data_size = _Get_data_size(); const pointer _Begin_seq_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); if (_Data_size > 0 || _Record_sync) { - lock_guard _Guard( + scoped_lock _Guard( _Basic_syncbuf_global_mutex::_Get_mutex_for_instance(static_cast(_Wrapped))); if (_Data_size > 0 && _Data_size != _Wrapped->sputn(_Begin_seq_ptr, _Data_size)) { From a41104f61a0b2716ce61ab2651ec696f6c934736 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Mon, 11 Jan 2021 16:01:49 +0000 Subject: [PATCH 04/60] clang-format Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/syncstream | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index af12c32aeb6..95d30a17667 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -138,8 +138,7 @@ public: const size_type _Data_size = _Get_data_size(); const pointer _Begin_seq_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); if (_Data_size > 0 || _Record_sync) { - scoped_lock _Guard( - _Basic_syncbuf_global_mutex::_Get_mutex_for_instance(static_cast(_Wrapped))); + scoped_lock _Guard(_Basic_syncbuf_global_mutex::_Get_mutex_for_instance(static_cast(_Wrapped))); if (_Data_size > 0 && _Data_size != _Wrapped->sputn(_Begin_seq_ptr, _Data_size)) { _Result = false; From 4f469e8ac06b8a4224808587edc90a4b8c6637d3 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Mon, 11 Jan 2021 16:12:32 +0000 Subject: [PATCH 05/60] Remove redefinition of default parameter occurrence Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/syncstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 95d30a17667..654d2ecbeaf 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -78,7 +78,7 @@ private: } } - template , class _Alloc = allocator<_Elem>> + template friend class basic_syncbuf; }; From 215307cfdf224a0de9705f4d2b51f143a742270b Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Mon, 11 Jan 2021 19:52:54 +0000 Subject: [PATCH 06/60] Remove a mistaken definition Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/syncstream | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 654d2ecbeaf..15715d2fc36 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -8,7 +8,6 @@ #define _SYNCSTREAM_ #include #if _STL_COMPILER_PREPROCESSOR -#define __cpp_lib_syncbuf // Remove before commit, visual #ifndef __cpp_lib_syncbuf #pragma message("The contents of are available only with C++20 syncbuf support.") #else // ^^^ !defined(__cpp_lib_syncbuf) / defined(__cpp_lib_syncbuf) vvv From 6afbbc737a45bf114759ccd6e7f9e895436b4498 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Mon, 11 Jan 2021 21:45:01 +0000 Subject: [PATCH 07/60] Move global variables to syncstream.cpp - this commit requires a maintainer's input --- stl/CMakeLists.txt | 1 + stl/inc/syncstream | 91 +++++++++++++++++------------------------- stl/src/syncstream.cpp | 26 ++++++++++++ 3 files changed, 64 insertions(+), 54 deletions(-) create mode 100644 stl/src/syncstream.cpp diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index cf3ab6edf98..ba00e5db432 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -249,6 +249,7 @@ set(IMPLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/locale0_implib.cpp ${CMAKE_CURRENT_LIST_DIR}/src/nothrow.cpp ${CMAKE_CURRENT_LIST_DIR}/src/sharedmutex.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/syncstream.cpp ${CMAKE_CURRENT_LIST_DIR}/src/syserror_import_lib.cpp ${CMAKE_CURRENT_LIST_DIR}/src/vector_algorithms.cpp ${CMAKE_CURRENT_LIST_DIR}/src/xonce2.cpp diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 15715d2fc36..1d7e03e5b85 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -8,12 +8,17 @@ #define _SYNCSTREAM_ #include #if _STL_COMPILER_PREPROCESSOR + +#ifdef _M_CEE_PURE +#error is not supported when compiling with /clr:pure +#endif // _M_CEE_PURE + #ifndef __cpp_lib_syncbuf #pragma message("The contents of are available only with C++20 syncbuf support.") #else // ^^^ !defined(__cpp_lib_syncbuf) / defined(__cpp_lib_syncbuf) vvv #include #include -#include +#include #include #pragma pack(push, _CRT_PACKING) @@ -26,60 +31,38 @@ _STL_DISABLE_CLANG_WARNINGS #undef emit _STD_BEGIN -struct _Basic_syncbuf_global_mutex { -private: - struct _Mutex_count_pair { - mutex _Mutex; - uint64_t _Ref_count = 0; - }; - static map& _Get_mutex_map() noexcept { - static map _Mutex_Map; - return _Mutex_Map; - } - static mutex& _Get_map_mutex() noexcept { - static mutex _Mutex; - return _Mutex; - } - static mutex& _Get_mutex_for_instance(void* _Ptr) { - mutex& _Mutex = _Get_map_mutex(); - { - scoped_lock _Guard(_Mutex); - auto& _Mutex_map = _Get_mutex_map(); - auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); +struct _Mutex_count_pair { + shared_mutex _Mutex; + uint64_t _Ref_count = 0; +}; - _STL_ASSERT(_Instance_mutex_itr != _Mutex_map.end(), "No mutex exists for given instance!"); - return _Instance_mutex_itr->second._Mutex; - } - } - static void _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept { - mutex& _Mutex = _Get_map_mutex(); - { - scoped_lock _Guard(_Mutex); +_CRTIMP2_PURE map& __cdecl _Get_mutex_map(); +_CRTIMP2_PURE shared_mutex& __cdecl _Get_map_mutex(); - auto& _Mutex_map = _Get_mutex_map(); - _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; - } - } - static void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept { - mutex& _Mutex = _Get_map_mutex(); - { - scoped_lock _Guard(_Mutex); - - auto& _Mutex_map = _Get_mutex_map(); - auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); - if (_Instance_mutex_itr == _Mutex_map.end()) - return; - _Instance_mutex_itr->second._Ref_count--; - if (_Instance_mutex_itr->second._Ref_count == 0) { - _Mutex_map.erase(_Ptr); - } - } +shared_mutex& _Get_mutex_for_instance(void* _Ptr) { + scoped_lock _Guard(_Get_map_mutex()); + auto& _Mutex_map = _Get_mutex_map(); + auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); + _STL_ASSERT(_Instance_mutex_itr != _Mutex_map.end(), "No mutex exists for given instance!"); + return _Instance_mutex_itr->second._Mutex; +} +void _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept { + scoped_lock _Guard(_Get_map_mutex()); + auto& _Mutex_map = _Get_mutex_map(); + _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; +} +void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept { + scoped_lock _Guard(_Get_map_mutex()); + auto& _Mutex_map = _Get_mutex_map(); + auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); + if (_Instance_mutex_itr == _Mutex_map.end()) + return; + _Instance_mutex_itr->second._Ref_count--; + if (_Instance_mutex_itr->second._Ref_count == 0) { + _Mutex_map.erase(_Ptr); } - - template - friend class basic_syncbuf; -}; +} // CLASS TEMPLATE basic_syncbuf template @@ -100,7 +83,7 @@ public: basic_syncbuf(streambuf_type* _StrBuf, const _Alloc& _Al) : _Wrapped(_StrBuf), _Al(_Al) { if (_Wrapped != nullptr) { - _Basic_syncbuf_global_mutex::_Add_mutex_for_instance_or_increment(static_cast(_Wrapped)); + _Add_mutex_for_instance_or_increment(static_cast(_Wrapped)); } _Init(); } @@ -137,7 +120,7 @@ public: const size_type _Data_size = _Get_data_size(); const pointer _Begin_seq_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); if (_Data_size > 0 || _Record_sync) { - scoped_lock _Guard(_Basic_syncbuf_global_mutex::_Get_mutex_for_instance(static_cast(_Wrapped))); + scoped_lock _Guard(_Get_mutex_for_instance(static_cast(_Wrapped))); if (_Data_size > 0 && _Data_size != _Wrapped->sputn(_Begin_seq_ptr, _Data_size)) { _Result = false; @@ -220,7 +203,7 @@ private: streambuf_type::setp(nullptr, nullptr, nullptr); if (_Wrapped != nullptr) { - _Basic_syncbuf_global_mutex::_Delete_mutex_for_instance_or_decrement(static_cast(_Wrapped)); + _Delete_mutex_for_instance_or_decrement(static_cast(_Wrapped)); _Wrapped = nullptr; } } diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp new file mode 100644 index 00000000000..9e62a0fd8ec --- /dev/null +++ b/stl/src/syncstream.cpp @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// initialize syncstream mutex map + +#include + +#ifdef __cpp_lib_syncbuf +#pragma warning(disable : 4074) +#pragma init_seg(compiler) + +_STD_BEGIN +// OBJECT DECLARATIONS +map _Mutex_map{}; +shared_mutex _Mutex{}; + +shared_mutex& _Get_map_mutex() { + return _Mutex; +} + +map& _Get_mutex_map() { + return _Mutex_map; +} + +_STD_END +#endif From 56bf7d603f981648d02df1615317e5ddfbf50c99 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Mon, 11 Jan 2021 21:59:14 +0000 Subject: [PATCH 08/60] Update syncstream.cpp with static variables --- stl/src/syncstream.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 9e62a0fd8ec..3a6a63a9a9c 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -11,8 +11,8 @@ _STD_BEGIN // OBJECT DECLARATIONS -map _Mutex_map{}; -shared_mutex _Mutex{}; +static map _Mutex_map{}; +static shared_mutex _Mutex{}; shared_mutex& _Get_map_mutex() { return _Mutex; From 1f7123251e9f58f730faf4832902120e38024da1 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Tue, 12 Jan 2021 18:22:51 +0000 Subject: [PATCH 09/60] Apply code review and changes to global mutex design Co-Authored-By: Stephan T. Lavavej Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/syncstream | 101 +++++++++++++++-------------------------- stl/src/syncstream.cpp | 27 +++++++++-- 2 files changed, 58 insertions(+), 70 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 1d7e03e5b85..214e7efd1da 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -9,10 +9,7 @@ #include #if _STL_COMPILER_PREPROCESSOR -#ifdef _M_CEE_PURE -#error is not supported when compiling with /clr:pure -#endif // _M_CEE_PURE - +#define __cpp_lib_syncbuf #ifndef __cpp_lib_syncbuf #pragma message("The contents of are available only with C++20 syncbuf support.") #else // ^^^ !defined(__cpp_lib_syncbuf) / defined(__cpp_lib_syncbuf) vvv @@ -32,37 +29,9 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -struct _Mutex_count_pair { - shared_mutex _Mutex; - uint64_t _Ref_count = 0; -}; - -_CRTIMP2_PURE map& __cdecl _Get_mutex_map(); -_CRTIMP2_PURE shared_mutex& __cdecl _Get_map_mutex(); - -shared_mutex& _Get_mutex_for_instance(void* _Ptr) { - scoped_lock _Guard(_Get_map_mutex()); - auto& _Mutex_map = _Get_mutex_map(); - auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); - _STL_ASSERT(_Instance_mutex_itr != _Mutex_map.end(), "No mutex exists for given instance!"); - return _Instance_mutex_itr->second._Mutex; -} -void _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept { - scoped_lock _Guard(_Get_map_mutex()); - auto& _Mutex_map = _Get_mutex_map(); - _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; -} -void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept { - scoped_lock _Guard(_Get_map_mutex()); - auto& _Mutex_map = _Get_mutex_map(); - auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); - if (_Instance_mutex_itr == _Mutex_map.end()) - return; - _Instance_mutex_itr->second._Ref_count--; - if (_Instance_mutex_itr->second._Ref_count == 0) { - _Mutex_map.erase(_Ptr); - } -} +extern "C" shared_mutex& __cdecl _Get_mutex_for_instance(void* _Ptr); +extern "C" void __cdecl _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept; +extern "C" void __cdecl _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept; // CLASS TEMPLATE basic_syncbuf template @@ -77,13 +46,13 @@ public: using pointer = typename allocator_traits<_Alloc>::pointer; using size_type = typename allocator_traits<_Alloc>::size_type; - basic_syncbuf() : basic_syncbuf(nullptr){}; + basic_syncbuf() = default; - explicit basic_syncbuf(streambuf_type* _StrBuf) : basic_syncbuf(_StrBuf, _Alloc{}) {} + explicit basic_syncbuf(streambuf_type* _Strbuf) : basic_syncbuf(_Strbuf, _Alloc{}) {} - basic_syncbuf(streambuf_type* _StrBuf, const _Alloc& _Al) : _Wrapped(_StrBuf), _Al(_Al) { + basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { if (_Wrapped != nullptr) { - _Add_mutex_for_instance_or_increment(static_cast(_Wrapped)); + _Add_mutex_for_instance_or_increment(_Wrapped); } _Init(); } @@ -104,7 +73,7 @@ public: return *this; } - void swap(basic_syncbuf& _Right) { + void swap(basic_syncbuf& _Right) noexcept { if (this != _STD addressof(_Right)) { _Pocs(_Getal(), _Right._Getal()); _Swap_except_al(_Right); @@ -120,7 +89,7 @@ public: const size_type _Data_size = _Get_data_size(); const pointer _Begin_seq_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); if (_Data_size > 0 || _Record_sync) { - scoped_lock _Guard(_Get_mutex_for_instance(static_cast(_Wrapped))); + scoped_lock _Guard(_Get_mutex_for_instance(_Wrapped)); if (_Data_size > 0 && _Data_size != _Wrapped->sputn(_Begin_seq_ptr, _Data_size)) { _Result = false; @@ -138,11 +107,11 @@ public: return _Result; } - streambuf_type* get_wrapped() const noexcept { + _NODISCARD streambuf_type* get_wrapped() const noexcept { return _Wrapped; } - allocator_type get_allocator() const noexcept { + _NODISCARD allocator_type get_allocator() const noexcept { return _Getal(); } @@ -163,38 +132,39 @@ protected: } int_type overflow(int_type _Current_elem) override { - if (_Wrapped == nullptr) + if (_Wrapped == nullptr) { return _Traits::eof(); + } const bool _Chk_eof = _Traits::eq_int_type(_Current_elem, _Traits::eof()); - if (_Chk_eof) + if (_Chk_eof) { return _Traits::not_eof(_Current_elem); + } const size_type _Buf_size = _Get_buffer_size(); const size_type _Max_allocation = allocator_traits<_Alloc>::max_size(_Al); - if (_Buf_size == _Max_allocation) + if (_Buf_size == _Max_allocation) { return _Traits::eof(); + } const size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); const pointer _Old_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); const size_type _Old_data_size = _Get_data_size(); - const pointer _New_ptr = _Unfancy(_Al.allocate(_New_capacity)); + const pointer _New_ptr = _Al.allocate(_New_capacity); _Traits::copy(_New_ptr, _Old_ptr, _Old_data_size); streambuf_type::setp(_New_ptr, _New_ptr + _Old_data_size, _New_ptr + _New_capacity); - streambuf_type::sputc(_Current_elem); + streambuf_type::sputc(_Traits::to_char_type(_Current_elem)); return _Current_elem; } private: - enum { // constant for minimum buffer size - _MINSIZE = 32 - }; + static constexpr size_type _Min_size = 32; // constant for minimum buffer size void _Init() { - const pointer _New_ptr = _Unfancy(_Al.allocate(_MINSIZE)); - streambuf_type::setp(_New_ptr, _New_ptr + _MINSIZE); + const pointer _New_ptr = _Al.allocate(_Min_size); + streambuf_type::setp(_New_ptr, _New_ptr + _Min_size); } void _Tidy() noexcept { @@ -203,7 +173,7 @@ private: streambuf_type::setp(nullptr, nullptr, nullptr); if (_Wrapped != nullptr) { - _Delete_mutex_for_instance_or_decrement(static_cast(_Wrapped)); + _Delete_mutex_for_instance_or_decrement(_Wrapped); _Wrapped = nullptr; } } @@ -234,7 +204,7 @@ private: const size_type _Right_Buf_size = _Right._Get_buffer_size(); const size_type _Right_Data_size = _Right._Get_data_size(); - const pointer _New_ptr = _Unfancy(_Al.allocate(_Right_Buf_size)); + const pointer _New_ptr = _Al.allocate(_Right_Buf_size); _Traits::copy(_New_ptr, pointer_traits::pointer_to(*_Right.pbase()), _Right_Data_size); streambuf_type::setp(_New_ptr, _New_ptr + _Right_Data_size, _New_ptr + _Right_Buf_size); @@ -244,7 +214,7 @@ private: } } - void _Swap_except_al(basic_syncbuf& _Right) { + void _Swap_except_al(basic_syncbuf& _Right) noexcept { streambuf_type::swap(_Right); _STD swap(_Emit_on_sync, _Right._Emit_on_sync); _STD swap(_Record_sync, _Right._Record_sync); @@ -255,10 +225,11 @@ private: _TRY_BEGIN return emit(); _CATCH_ALL + return false; _CATCH_END } - constexpr size_type _Calculate_growth( + _NODISCARD constexpr size_type _Calculate_growth( const size_type _Oldsize, const size_type _Newsize, const size_type _Maxsize) const { if (_Oldsize > _Maxsize - _Oldsize / 2) { return _Maxsize; // geometric growth would overflow @@ -273,26 +244,26 @@ private: return _Geometric; // geometric growth is sufficient } - constexpr size_type _Get_data_size() const noexcept { + _NODISCARD constexpr size_type _Get_data_size() const noexcept { return static_cast(streambuf_type::pptr() - streambuf_type::pbase()); } - constexpr size_type _Get_buffer_size() const noexcept { + _NODISCARD constexpr size_type _Get_buffer_size() const noexcept { return static_cast(streambuf_type::epptr() - streambuf_type::pbase()); } - constexpr _Alloc& _Getal() noexcept { + _NODISCARD constexpr _Alloc& _Getal() noexcept { return _Al; } - constexpr const _Alloc& _Getal() const noexcept { + _NODISCARD constexpr const _Alloc& _Getal() const noexcept { return _Al; } - streambuf_type* _Wrapped = nullptr; - allocator_type _Al; - bool _Emit_on_sync = false; - bool _Record_sync = false; + streambuf_type* _Wrapped{nullptr}; + allocator_type _Al{}; + bool _Emit_on_sync{false}; + bool _Record_sync{false}; }; template diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 3a6a63a9a9c..309bacffaa9 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -11,15 +11,32 @@ _STD_BEGIN // OBJECT DECLARATIONS +struct _Mutex_count_pair { + shared_mutex _Mutex; + uint64_t _Ref_count = 0; +}; static map _Mutex_map{}; static shared_mutex _Mutex{}; -shared_mutex& _Get_map_mutex() { - return _Mutex; +extern "C" _CRTIMP2 shared_mutex& _Get_mutex_for_instance(void* _Ptr) { + scoped_lock _Guard(_Mutex); + auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); + _ASSERT_EXPR(_Instance_mutex_itr != _Mutex_map.end(), "No mutex exists for given instance!"); + return _Instance_mutex_itr->second._Mutex; } - -map& _Get_mutex_map() { - return _Mutex_map; +extern "C" _CRTIMP2 void _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept { + scoped_lock _Guard(_Mutex); + _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; +} +extern "C" _CRTIMP2 void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept { + scoped_lock _Guard(_Mutex); + auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); + if (_Instance_mutex_itr == _Mutex_map.end()) { + return; + } + if (--_Instance_mutex_itr->second._Ref_count == 0) { + _Mutex_map.erase(_Ptr); + } } _STD_END From 146d5defaeb71b9ec0371d3d489eb5dab68d3633 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Tue, 12 Jan 2021 18:24:22 +0000 Subject: [PATCH 10/60] Remove unnecessary guards --- stl/inc/syncstream | 1 - stl/src/syncstream.cpp | 2 -- 2 files changed, 3 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 214e7efd1da..26d3009bf6c 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -9,7 +9,6 @@ #include #if _STL_COMPILER_PREPROCESSOR -#define __cpp_lib_syncbuf #ifndef __cpp_lib_syncbuf #pragma message("The contents of are available only with C++20 syncbuf support.") #else // ^^^ !defined(__cpp_lib_syncbuf) / defined(__cpp_lib_syncbuf) vvv diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 309bacffaa9..a8ca95d3262 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -5,7 +5,6 @@ #include -#ifdef __cpp_lib_syncbuf #pragma warning(disable : 4074) #pragma init_seg(compiler) @@ -40,4 +39,3 @@ extern "C" _CRTIMP2 void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noe } _STD_END -#endif From 5f159192dd1969adb2d2ecee26a0e91ed03d2c28 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Tue, 12 Jan 2021 19:02:20 +0000 Subject: [PATCH 11/60] Update included headers --- stl/src/syncstream.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index a8ca95d3262..da4e9a21c5a 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -3,7 +3,8 @@ // initialize syncstream mutex map -#include +#include +#include #pragma warning(disable : 4074) #pragma init_seg(compiler) From 31169f6c5caa4a40c7a7b3953ef32222d3930f6e Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Tue, 12 Jan 2021 19:04:15 +0000 Subject: [PATCH 12/60] clang-format --- stl/src/syncstream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index da4e9a21c5a..ec12c1dfd53 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -3,8 +3,8 @@ // initialize syncstream mutex map -#include #include +#include #pragma warning(disable : 4074) #pragma init_seg(compiler) From 103e07e8f1ed8d2a63eb5faa7ad1d8386b9f9f85 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Tue, 12 Jan 2021 20:53:21 +0000 Subject: [PATCH 13/60] Remove unnecessary header, adjust variable names Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/syncstream | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 26d3009bf6c..798efbb0a7b 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -12,7 +12,6 @@ #ifndef __cpp_lib_syncbuf #pragma message("The contents of are available only with C++20 syncbuf support.") #else // ^^^ !defined(__cpp_lib_syncbuf) / defined(__cpp_lib_syncbuf) vvv -#include #include #include #include @@ -200,13 +199,13 @@ private: if (_Getal() == _Right._Getal()) { _Move_assign(_Right, _Equal_allocators{}); } else { - const size_type _Right_Buf_size = _Right._Get_buffer_size(); - const size_type _Right_Data_size = _Right._Get_data_size(); + const size_type _Right_buf_size = _Right._Get_buffer_size(); + const size_type _Right_data_size = _Right._Get_data_size(); - const pointer _New_ptr = _Al.allocate(_Right_Buf_size); - _Traits::copy(_New_ptr, pointer_traits::pointer_to(*_Right.pbase()), _Right_Data_size); + const pointer _New_ptr = _Al.allocate(_Right_buf_size); + _Traits::copy(_New_ptr, pointer_traits::pointer_to(*_Right.pbase()), _Right_data_size); - streambuf_type::setp(_New_ptr, _New_ptr + _Right_Data_size, _New_ptr + _Right_Buf_size); + streambuf_type::setp(_New_ptr, _New_ptr + _Right_data_size, _New_ptr + _Right_buf_size); streambuf_type::_Plocale = _STD move(_Right._Plocale); _Right._Tidy(); From 9e05bb280de24253e8398342621fab45321110d2 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Tue, 12 Jan 2021 21:05:44 +0000 Subject: [PATCH 14/60] Add basic_osyncstream class --- stl/inc/syncstream | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 798efbb0a7b..692080ff55b 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -13,6 +13,7 @@ #pragma message("The contents of are available only with C++20 syncbuf support.") #else // ^^^ !defined(__cpp_lib_syncbuf) / defined(__cpp_lib_syncbuf) vvv #include +#include #include #include @@ -269,6 +270,63 @@ void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, basic_syncbuf<_Elem, _Tr _Left.swap(_Right); } +template +class basic_osyncstream : public basic_ostream<_Elem, _Traits> { +public: + using char_type = _Elem; + 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<_Elem, _Traits>; + using syncbuf_type = basic_syncbuf<_Elem, _Traits, _Alloc>; + + using _Mybase = basic_ostream<_Elem, _Traits>; + + basic_osyncstream(streambuf_type* _Strbuf, const _Alloc& _Al) + : _Mybase(_STD addressof(_Sync_buf)), _Sync_buf(_Strbuf, _Al) {} + + explicit basic_osyncstream(streambuf_type* _Strbuf) : basic_osyncstream(_Strbuf, _Alloc{}) {} + + basic_osyncstream(basic_ostream<_Elem, _Traits>& _Str, const _Alloc& _Al) : basic_osyncstream(_Str.rdbuf(), _Al) {} + + explicit basic_osyncstream(basic_ostream<_Elem, _Traits>& _Str) : basic_osyncstream(_Str, _Alloc{}) {} + + basic_osyncstream(basic_osyncstream&& _Right) noexcept + : _Mybase(_STD move(_Right)), _Sync_buf(_STD move(_Right._Sync_buf)) { + _Mybase::set_rdbuf(_STD addressof(_Sync_buf)); + } + + ~basic_osyncstream() { + _Emit(); + } + + basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default; + + void emit() { + if (!_Sync_buf.emit()) { + _Mybase::setstate(ios::badbit); + } + } + _NODISCARD streambuf_type* get_wrapped() const noexcept { + return _Sync_buf.get_wrapped(); + } + _NODISCARD syncbuf_type* rdbuf() const noexcept { + return const_cast(_STD addressof(_Sync_buf)); + } + +private: + void _Emit() { + _TRY_BEGIN + return emit(); + _CATCH_ALL + _CATCH_END + } + + syncbuf_type _Sync_buf; +}; + _STD_END #pragma pop_macro("emit") From 7889ef0833b6cd4dad6f93199a2721f2214ecd9d Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Wed, 13 Jan 2021 19:14:58 +0000 Subject: [PATCH 15/60] Apply code review changes - Add missing guards - Name changes Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/iosfwd | 14 ++++++++++---- stl/inc/syncstream | 28 +++++++++------------------- stl/src/syncstream.cpp | 20 +++++++++----------- 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/stl/inc/iosfwd b/stl/inc/iosfwd index fcfac770605..f34805c7bbc 100644 --- a/stl/inc/iosfwd +++ b/stl/inc/iosfwd @@ -200,10 +200,12 @@ template > class basic_ofstream; template > class basic_fstream; +#if _HAS_CXX20 template , class _Alloc = allocator<_Elem>> class basic_syncbuf; template , class _Alloc = allocator<_Elem>> class basic_osyncstream; +#endif // _HAS_CXX20 #if defined(_DLL_CPPLIB) template @@ -228,8 +230,10 @@ using filebuf = basic_filebuf>; using ifstream = basic_ifstream>; using ofstream = basic_ofstream>; using fstream = basic_fstream>; -using syncbuf = basic_syncbuf; -using osyncstream = basic_osyncstream; +#if _HAS_CXX20 +using syncbuf = basic_syncbuf; +using osyncstream = basic_osyncstream; +#endif // _HAS_CXX20 // wchar_t TYPEDEFS using wios = basic_ios>; @@ -245,8 +249,10 @@ using wfilebuf = basic_filebuf>; using wifstream = basic_ifstream>; using wofstream = basic_ofstream>; using wfstream = basic_fstream>; -using wsyncbuf = basic_syncbuf; -using wosyncstream = basic_osyncstream; +#if _HAS_CXX20 +using wsyncbuf = basic_syncbuf; +using wosyncstream = basic_osyncstream; +#endif // _HAS_CXX20 #if defined(_CRTBLD) // unsigned short TYPEDEFS diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 692080ff55b..06082abc232 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -8,10 +8,9 @@ #define _SYNCSTREAM_ #include #if _STL_COMPILER_PREPROCESSOR - -#ifndef __cpp_lib_syncbuf -#pragma message("The contents of are available only with C++20 syncbuf support.") -#else // ^^^ !defined(__cpp_lib_syncbuf) / defined(__cpp_lib_syncbuf) vvv +#if !_HAS_CXX20 +#pragma message("The contents of are available only with C++20 or later.") +#else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv #include #include #include @@ -29,8 +28,8 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN extern "C" shared_mutex& __cdecl _Get_mutex_for_instance(void* _Ptr); -extern "C" void __cdecl _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept; -extern "C" void __cdecl _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept; +extern "C" void __cdecl _Acquire_mutex_for_instance(void* _Ptr) noexcept; +extern "C" void __cdecl _Release_mutex_for_instance(void* _Ptr) noexcept; // CLASS TEMPLATE basic_syncbuf template @@ -51,7 +50,7 @@ public: basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { if (_Wrapped != nullptr) { - _Add_mutex_for_instance_or_increment(_Wrapped); + _Acquire_mutex_for_instance(_Wrapped); } _Init(); } @@ -172,7 +171,7 @@ private: streambuf_type::setp(nullptr, nullptr, nullptr); if (_Wrapped != nullptr) { - _Delete_mutex_for_instance_or_decrement(_Wrapped); + _Release_mutex_for_instance(_Wrapped); _Wrapped = nullptr; } } @@ -298,9 +297,7 @@ public: _Mybase::set_rdbuf(_STD addressof(_Sync_buf)); } - ~basic_osyncstream() { - _Emit(); - } + ~basic_osyncstream() = default; basic_osyncstream& operator=(basic_osyncstream&&) noexcept = default; @@ -317,13 +314,6 @@ public: } private: - void _Emit() { - _TRY_BEGIN - return emit(); - _CATCH_ALL - _CATCH_END - } - syncbuf_type _Sync_buf; }; @@ -334,6 +324,6 @@ _STD_END _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) -#endif // __cpp_lib_syncbuf +#endif // _HAS_CXX20 #endif // _STL_COMPILER_PREPROCESSOR #endif // _SYNCSTREAM_ diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index ec12c1dfd53..6fc2abb50c1 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -19,22 +19,20 @@ static map _Mutex_map{}; static shared_mutex _Mutex{}; extern "C" _CRTIMP2 shared_mutex& _Get_mutex_for_instance(void* _Ptr) { - scoped_lock _Guard(_Mutex); - auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); - _ASSERT_EXPR(_Instance_mutex_itr != _Mutex_map.end(), "No mutex exists for given instance!"); - return _Instance_mutex_itr->second._Mutex; + shared_lock _Guard(_Mutex); + auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); + _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); + return _Instance_mutex_iter->second._Mutex; } -extern "C" _CRTIMP2 void _Add_mutex_for_instance_or_increment(void* _Ptr) noexcept { +extern "C" _CRTIMP2 void _Acquire_mutex_for_instance(void* _Ptr) noexcept { scoped_lock _Guard(_Mutex); _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; } -extern "C" _CRTIMP2 void _Delete_mutex_for_instance_or_decrement(void* _Ptr) noexcept { +extern "C" _CRTIMP2 void _Release_mutex_for_instance(void* _Ptr) noexcept { scoped_lock _Guard(_Mutex); - auto _Instance_mutex_itr = _Mutex_map.find(_Ptr); - if (_Instance_mutex_itr == _Mutex_map.end()) { - return; - } - if (--_Instance_mutex_itr->second._Ref_count == 0) { + auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); + _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); + if (--_Instance_mutex_iter->second._Ref_count == 0) { _Mutex_map.erase(_Ptr); } } From df7aea1b375ac9883f568b042a27991b3571fc35 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Wed, 13 Jan 2021 19:37:23 +0000 Subject: [PATCH 16/60] Add custom CRT Allocator --- stl/src/syncstream.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 6fc2abb50c1..b0bc73d07fb 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -3,6 +3,7 @@ // initialize syncstream mutex map +#include #include #include @@ -15,8 +16,33 @@ struct _Mutex_count_pair { shared_mutex _Mutex; uint64_t _Ref_count = 0; }; -static map _Mutex_map{}; -static shared_mutex _Mutex{}; + +template +class _Crt_allocator { +public: + using value_type = _Ty; + using size_type = size_t; + using difference_type = ptrdiff_t; + using propagate_on_container_move_assignment = true_type; + using is_always_equal = true_type; + + constexpr _Crt_allocator() noexcept {} + + constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; + template + constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} + + _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + return static_cast<_Ty*>(_calloc_crt(_Count, sizeof(_Ty))); + } + + void deallocate(_Ty* const _Ptr, const size_t) { + _free_crt(_Ptr); + } +}; + +static map, _Crt_allocator>> _Mutex_map; +static shared_mutex _Mutex; extern "C" _CRTIMP2 shared_mutex& _Get_mutex_for_instance(void* _Ptr) { shared_lock _Guard(_Mutex); From fa3c112dfa7982831811abc94291528a0f082b67 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Wed, 13 Jan 2021 21:07:54 +0000 Subject: [PATCH 17/60] Apply code review changes - Remove unnecessary helper functions - Add self assignment check - Trial to C-linkage return type warning Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Co-Authored-By: Michael Schellenberger Costa --- stl/inc/syncstream | 30 ++++++++++++------------------ stl/src/syncstream.cpp | 6 +++--- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 06082abc232..a6aa150a05e 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -27,7 +27,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -extern "C" shared_mutex& __cdecl _Get_mutex_for_instance(void* _Ptr); +extern "C" shared_mutex* __cdecl _Get_mutex_for_instance(void* _Ptr); extern "C" void __cdecl _Acquire_mutex_for_instance(void* _Ptr) noexcept; extern "C" void __cdecl _Release_mutex_for_instance(void* _Ptr) noexcept; @@ -49,7 +49,7 @@ public: explicit basic_syncbuf(streambuf_type* _Strbuf) : basic_syncbuf(_Strbuf, _Alloc{}) {} basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { - if (_Wrapped != nullptr) { + if (_Wrapped) { _Acquire_mutex_for_instance(_Wrapped); } _Init(); @@ -67,19 +67,21 @@ public: basic_syncbuf& operator=(basic_syncbuf&& _Right) { _Emit(); - _Move_assign(_STD move(_Right), _Choose_pocma<_Alloc>{}); + if (this != _STD addressof(_Right)) { + _Move_assign(_STD move(_Right), _Choose_pocma<_Alloc>{}); + } return *this; } void swap(basic_syncbuf& _Right) noexcept { if (this != _STD addressof(_Right)) { - _Pocs(_Getal(), _Right._Getal()); + _Pocs(_Al, _Right._Al); _Swap_except_al(_Right); } } bool emit() { - if (_Wrapped == nullptr) { + if (!_Wrapped) { return false; } @@ -87,7 +89,7 @@ public: const size_type _Data_size = _Get_data_size(); const pointer _Begin_seq_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); if (_Data_size > 0 || _Record_sync) { - scoped_lock _Guard(_Get_mutex_for_instance(_Wrapped)); + scoped_lock _Guard(*_Get_mutex_for_instance(_Wrapped)); if (_Data_size > 0 && _Data_size != _Wrapped->sputn(_Begin_seq_ptr, _Data_size)) { _Result = false; @@ -110,7 +112,7 @@ public: } _NODISCARD allocator_type get_allocator() const noexcept { - return _Getal(); + return _Al; } void set_emit_on_sync(bool _Val) noexcept { @@ -185,18 +187,18 @@ private: void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) noexcept { _Tidy(); - _Pocma(_Getal(), _Right._Getal()); + _Pocma(_Al, _Right._Al); this->_Swap_except_al(_Right); } void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) noexcept { _Tidy(); - _Pocma(_Getal(), _Right._Getal()); + _Pocma(_Al, _Right._Al); this->_Swap_except_al(_Right); } void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { - if (_Getal() == _Right._Getal()) { + if (_Al == _Right._Al) { _Move_assign(_Right, _Equal_allocators{}); } else { const size_type _Right_buf_size = _Right._Get_buffer_size(); @@ -250,14 +252,6 @@ private: return static_cast(streambuf_type::epptr() - streambuf_type::pbase()); } - _NODISCARD constexpr _Alloc& _Getal() noexcept { - return _Al; - } - - _NODISCARD constexpr const _Alloc& _Getal() const noexcept { - return _Al; - } - streambuf_type* _Wrapped{nullptr}; allocator_type _Al{}; bool _Emit_on_sync{false}; diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index b0bc73d07fb..04b116ee707 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -26,7 +26,7 @@ class _Crt_allocator { using propagate_on_container_move_assignment = true_type; using is_always_equal = true_type; - constexpr _Crt_allocator() noexcept {} + constexpr _Crt_allocator() noexcept = default; constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; template @@ -44,11 +44,11 @@ class _Crt_allocator { static map, _Crt_allocator>> _Mutex_map; static shared_mutex _Mutex; -extern "C" _CRTIMP2 shared_mutex& _Get_mutex_for_instance(void* _Ptr) { +extern "C" _CRTIMP2 shared_mutex* _Get_mutex_for_instance(void* _Ptr) { shared_lock _Guard(_Mutex); auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); - return _Instance_mutex_iter->second._Mutex; + return _STD addressof(_Instance_mutex_iter->second._Mutex); } extern "C" _CRTIMP2 void _Acquire_mutex_for_instance(void* _Ptr) noexcept { scoped_lock _Guard(_Mutex); From 2a3ab4e5beff6282cae8d2bfb93b75a1a2b2b874 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Wed, 13 Jan 2021 21:58:08 +0000 Subject: [PATCH 18/60] Adjust noexcept flag and throw on null allocation Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/syncstream | 6 +++--- stl/src/syncstream.cpp | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index a6aa150a05e..279ee35c09d 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -27,8 +27,8 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -extern "C" shared_mutex* __cdecl _Get_mutex_for_instance(void* _Ptr); -extern "C" void __cdecl _Acquire_mutex_for_instance(void* _Ptr) noexcept; +extern "C" shared_mutex* __cdecl _Get_mutex_for_instance(void* _Ptr) noexcept; +extern "C" void __cdecl _Acquire_mutex_for_instance(void* _Ptr); extern "C" void __cdecl _Release_mutex_for_instance(void* _Ptr) noexcept; // CLASS TEMPLATE basic_syncbuf @@ -115,7 +115,7 @@ public: return _Al; } - void set_emit_on_sync(bool _Val) noexcept { + void set_emit_on_sync(const bool _Val) noexcept { _Emit_on_sync = _Val; } diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 04b116ee707..89467c6b005 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -33,10 +33,14 @@ class _Crt_allocator { constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast<_Ty*>(_calloc_crt(_Count, sizeof(_Ty))); + auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); + if (!_Ptr) { + throw bad_alloc{}; + } + return static_cast<_Ty*>(_Ptr); } - void deallocate(_Ty* const _Ptr, const size_t) { + void deallocate(_Ty* const _Ptr, const size_t) noexcept { _free_crt(_Ptr); } }; @@ -44,13 +48,13 @@ class _Crt_allocator { static map, _Crt_allocator>> _Mutex_map; static shared_mutex _Mutex; -extern "C" _CRTIMP2 shared_mutex* _Get_mutex_for_instance(void* _Ptr) { +extern "C" _CRTIMP2 shared_mutex* _Get_mutex_for_instance(void* _Ptr) noexcept { shared_lock _Guard(_Mutex); auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); return _STD addressof(_Instance_mutex_iter->second._Mutex); } -extern "C" _CRTIMP2 void _Acquire_mutex_for_instance(void* _Ptr) noexcept { +extern "C" _CRTIMP2 void _Acquire_mutex_for_instance(void* _Ptr) { scoped_lock _Guard(_Mutex); _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; } From cd299907dbb95c79565308936e8b2b5cd7613419 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Wed, 13 Jan 2021 22:13:40 +0000 Subject: [PATCH 19/60] Implement intermediate class for basic_syncbuf - Required to implement P0753R2 --- stl/inc/iosfwd | 2 ++ stl/inc/syncstream | 55 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/stl/inc/iosfwd b/stl/inc/iosfwd index f34805c7bbc..0aec6258a95 100644 --- a/stl/inc/iosfwd +++ b/stl/inc/iosfwd @@ -201,6 +201,8 @@ class basic_ofstream; template > class basic_fstream; #if _HAS_CXX20 +template > +class _Basic_syncbuf_impl; template , class _Alloc = allocator<_Elem>> class basic_syncbuf; template , class _Alloc = allocator<_Elem>> diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 279ee35c09d..0b64fccf8c9 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -31,9 +31,38 @@ extern "C" shared_mutex* __cdecl _Get_mutex_for_instance(void* _Ptr) noexcept; extern "C" void __cdecl _Acquire_mutex_for_instance(void* _Ptr); extern "C" void __cdecl _Release_mutex_for_instance(void* _Ptr) noexcept; +// CLASS TEMPLATE _Basic_osyncbuf_impl +template +class _Basic_syncbuf_impl : public basic_streambuf<_Elem, _Traits> { +public: + void _Set_emit_on_sync(const bool _Val) noexcept { + _Emit_on_sync = _Val; + } + + virtual bool _Do_emit() = 0; + +protected: + using _Mysb = basic_streambuf<_Elem, _Traits>; + + _Basic_syncbuf_impl() = default; + + _Basic_syncbuf_impl(_Basic_syncbuf_impl&& _Right) { + swap(_Right); + } + + void swap(_Basic_syncbuf_impl& _Right) { + if (this != _STD addressof(_Right)) { + _Mysb::swap(_Right); + _STD swap(_Right._Emit_on_sync, _Emit_on_sync); + } + } + + bool _Emit_on_sync{false}; +}; + // CLASS TEMPLATE basic_syncbuf template -class basic_syncbuf : public basic_streambuf<_Elem, _Traits> { +class basic_syncbuf : public _Basic_syncbuf_impl<_Elem, _Traits> { public: using int_type = typename _Traits::int_type; using pos_type = typename _Traits::pos_type; @@ -41,6 +70,7 @@ public: using allocator_type = _Alloc; using streambuf_type = basic_streambuf<_Elem, _Traits>; + using _Mybase = _Basic_syncbuf_impl<_Elem, _Traits>; using pointer = typename allocator_traits<_Alloc>::pointer; using size_type = typename allocator_traits<_Alloc>::size_type; @@ -91,7 +121,7 @@ public: if (_Data_size > 0 || _Record_sync) { scoped_lock _Guard(*_Get_mutex_for_instance(_Wrapped)); - if (_Data_size > 0 && _Data_size != _Wrapped->sputn(_Begin_seq_ptr, _Data_size)) { + if (_Data_size > 0 && _Data_size != static_cast(_Wrapped->sputn(_Begin_seq_ptr, _Data_size))) { _Result = false; } @@ -116,14 +146,14 @@ public: } void set_emit_on_sync(const bool _Val) noexcept { - _Emit_on_sync = _Val; + _Mybase::_Set_emit_on_sync(_Val); } protected: int sync() override { _Record_sync = true; - if (_Emit_on_sync) { + if (_Mybase::_Emit_on_sync) { if (!emit()) { return -1; } @@ -210,17 +240,24 @@ private: streambuf_type::setp(_New_ptr, _New_ptr + _Right_data_size, _New_ptr + _Right_buf_size); streambuf_type::_Plocale = _STD move(_Right._Plocale); + _STD swap(_Mybase::_Emit_on_sync, _Right._Emit_on_sync); + _STD swap(_Record_sync, _Right._Record_sync); + _STD swap(_Wrapped, _Right._Wrapped); + _Right._Tidy(); } } void _Swap_except_al(basic_syncbuf& _Right) noexcept { - streambuf_type::swap(_Right); - _STD swap(_Emit_on_sync, _Right._Emit_on_sync); + _Mybase::swap(_Right); _STD swap(_Record_sync, _Right._Record_sync); _STD swap(_Wrapped, _Right._Wrapped); } + bool _Do_emit() override { + return emit(); + } + bool _Emit() noexcept { _TRY_BEGIN return emit(); @@ -254,7 +291,6 @@ private: streambuf_type* _Wrapped{nullptr}; allocator_type _Al{}; - bool _Emit_on_sync{false}; bool _Record_sync{false}; }; @@ -282,9 +318,10 @@ public: explicit basic_osyncstream(streambuf_type* _Strbuf) : basic_osyncstream(_Strbuf, _Alloc{}) {} - basic_osyncstream(basic_ostream<_Elem, _Traits>& _Str, const _Alloc& _Al) : basic_osyncstream(_Str.rdbuf(), _Al) {} + basic_osyncstream(basic_ostream<_Elem, _Traits>& _Ostr, const _Alloc& _Al) + : basic_osyncstream(_Ostr.rdbuf(), _Al) {} - explicit basic_osyncstream(basic_ostream<_Elem, _Traits>& _Str) : basic_osyncstream(_Str, _Alloc{}) {} + explicit basic_osyncstream(basic_ostream<_Elem, _Traits>& _Ostr) : basic_osyncstream(_Ostr, _Alloc{}) {} basic_osyncstream(basic_osyncstream&& _Right) noexcept : _Mybase(_STD move(_Right)), _Sync_buf(_STD move(_Right._Sync_buf)) { From 472b80019b30023a30c01cbcb654ba0401485f42 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Wed, 13 Jan 2021 22:30:46 +0000 Subject: [PATCH 20/60] Implement osyncstream manipulators --- stl/inc/ostream | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/stl/inc/ostream b/stl/inc/ostream index 671f8d0e0bd..6a90c870094 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -993,6 +993,38 @@ basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL flush(basic_ostream<_Elem, _Tr return _Ostr; } +#if _HAS_CXX20 +template +basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { + auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + _Sync_buf_ptr->_Set_emit_on_sync(true); + } + return _Ostr; +} + +template +basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { + auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + _Sync_buf_ptr->_Set_emit_on_sync(false); + } + return _Ostr; +} + +template +basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>& _Ostr) { + _Ostr.flush(); + auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + if (_Sync_buf_ptr) { + if (!_Sync_buf_ptr->_Do_emit()) { + _Ostr.setstate(ios::badbit); + } + } + return _Ostr; +} +#endif // _HAS_CXX20 + // INSERTER FOR error_category template basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, From 5bdb646bdaaa583414f4425fb52ba205b524bbaa Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Thu, 14 Jan 2021 14:13:19 +0000 Subject: [PATCH 21/60] Apply code review changes - Variable name change - Function optimizations Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/ostream | 4 ++-- stl/inc/syncstream | 45 +++++++++++++++++---------------------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/stl/inc/ostream b/stl/inc/ostream index 6a90c870094..f0f7df17675 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -998,7 +998,7 @@ template basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); if (_Sync_buf_ptr) { - _Sync_buf_ptr->_Set_emit_on_sync(true); + _Sync_buf_ptr->set_emit_on_sync(true); } return _Ostr; } @@ -1007,7 +1007,7 @@ template basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); if (_Sync_buf_ptr) { - _Sync_buf_ptr->_Set_emit_on_sync(false); + _Sync_buf_ptr->set_emit_on_sync(false); } return _Ostr; } diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 0b64fccf8c9..dec628fda34 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -35,7 +35,7 @@ extern "C" void __cdecl _Release_mutex_for_instance(void* _Ptr) noexcept; template class _Basic_syncbuf_impl : public basic_streambuf<_Elem, _Traits> { public: - void _Set_emit_on_sync(const bool _Val) noexcept { + void set_emit_on_sync(const bool _Val) noexcept { _Emit_on_sync = _Val; } @@ -51,13 +51,13 @@ protected: } void swap(_Basic_syncbuf_impl& _Right) { - if (this != _STD addressof(_Right)) { - _Mysb::swap(_Right); - _STD swap(_Right._Emit_on_sync, _Emit_on_sync); - } + _Mysb::swap(_Right); + _STD swap(_Emit_on_sync, _Right._Emit_on_sync); + _STD swap(_Sync_recorded, _Right._Sync_recorded); } bool _Emit_on_sync{false}; + bool _Sync_recorded{false}; }; // CLASS TEMPLATE basic_syncbuf @@ -74,6 +74,8 @@ public: using pointer = typename allocator_traits<_Alloc>::pointer; using size_type = typename allocator_traits<_Alloc>::size_type; + using _Mybase::set_emit_on_sync; + basic_syncbuf() = default; explicit basic_syncbuf(streambuf_type* _Strbuf) : basic_syncbuf(_Strbuf, _Alloc{}) {} @@ -85,9 +87,8 @@ public: _Init(); } - basic_syncbuf(basic_syncbuf&& _Right) { - _Al = _STD move(_Right._Al); - _Assign_rv(_STD move(_Right)); + basic_syncbuf(basic_syncbuf&& _Right) : _Al(_STD move(_Right._Al)) { + _Swap_except_al(_Right); } ~basic_syncbuf() { @@ -118,20 +119,20 @@ public: bool _Result = true; const size_type _Data_size = _Get_data_size(); const pointer _Begin_seq_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); - if (_Data_size > 0 || _Record_sync) { + if (_Data_size > 0 || _Mybase::_Sync_recorded) { scoped_lock _Guard(*_Get_mutex_for_instance(_Wrapped)); if (_Data_size > 0 && _Data_size != static_cast(_Wrapped->sputn(_Begin_seq_ptr, _Data_size))) { _Result = false; } - if (_Record_sync) { + if (_Mybase::_Sync_recorded) { if (_Wrapped->pubsync() == -1) { _Result = false; } } } - _Record_sync = false; + _Mybase::_Sync_recorded = false; streambuf_type::setp( _Begin_seq_ptr, pointer_traits::pointer_to(*streambuf_type::epptr())); // reset written data return _Result; @@ -145,13 +146,9 @@ public: return _Al; } - void set_emit_on_sync(const bool _Val) noexcept { - _Mybase::_Set_emit_on_sync(_Val); - } - protected: int sync() override { - _Record_sync = true; + _Mybase::_Sync_recorded = true; if (_Mybase::_Emit_on_sync) { if (!emit()) { @@ -208,23 +205,16 @@ private: } } - void _Assign_rv(basic_syncbuf&& _Right) { - if (this != _STD addressof(_Right)) { - _Tidy(); - this->_Swap_except_al(_Right); - } - } - void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) noexcept { _Tidy(); _Pocma(_Al, _Right._Al); - this->_Swap_except_al(_Right); + _Swap_except_al(_Right); } void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) noexcept { _Tidy(); _Pocma(_Al, _Right._Al); - this->_Swap_except_al(_Right); + _Swap_except_al(_Right); } void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { @@ -241,7 +231,7 @@ private: streambuf_type::_Plocale = _STD move(_Right._Plocale); _STD swap(_Mybase::_Emit_on_sync, _Right._Emit_on_sync); - _STD swap(_Record_sync, _Right._Record_sync); + _STD swap(_Mybase::_Sync_recorded, _Right._Sync_recorded); _STD swap(_Wrapped, _Right._Wrapped); _Right._Tidy(); @@ -250,7 +240,6 @@ private: void _Swap_except_al(basic_syncbuf& _Right) noexcept { _Mybase::swap(_Right); - _STD swap(_Record_sync, _Right._Record_sync); _STD swap(_Wrapped, _Right._Wrapped); } @@ -291,7 +280,6 @@ private: streambuf_type* _Wrapped{nullptr}; allocator_type _Al{}; - bool _Record_sync{false}; }; template @@ -299,6 +287,7 @@ void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, basic_syncbuf<_Elem, _Tr _Left.swap(_Right); } +// CLASS TEMPLATE basic_osyncstream template class basic_osyncstream : public basic_ostream<_Elem, _Traits> { public: From f7aa45d181a66774117c8da59079b77a5cfb08dc Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Thu, 14 Jan 2021 21:58:40 +0000 Subject: [PATCH 22/60] Properly move assign equal _No_propagate_allocators --- stl/inc/syncstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index dec628fda34..756d9b281f7 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -219,7 +219,7 @@ private: void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { if (_Al == _Right._Al) { - _Move_assign(_Right, _Equal_allocators{}); + _Move_assign(_STD move(_Right), _Equal_allocators{}); } else { const size_type _Right_buf_size = _Right._Get_buffer_size(); const size_type _Right_data_size = _Right._Get_data_size(); From 0a1ce0edea4708d57370772f6c236b74c87f4ad4 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Fri, 15 Jan 2021 20:37:20 +0000 Subject: [PATCH 23/60] Ensure dealing _Unfancy pointers with streambuf --- stl/inc/syncstream | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 756d9b281f7..6743cd357e7 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -116,9 +116,9 @@ public: return false; } - bool _Result = true; - const size_type _Data_size = _Get_data_size(); - const pointer _Begin_seq_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); + bool _Result = true; + const size_type _Data_size = _Get_data_size(); + _Elem* const _Begin_seq_ptr = streambuf_type::pbase(); if (_Data_size > 0 || _Mybase::_Sync_recorded) { scoped_lock _Guard(*_Get_mutex_for_instance(_Wrapped)); @@ -133,8 +133,7 @@ public: } } _Mybase::_Sync_recorded = false; - streambuf_type::setp( - _Begin_seq_ptr, pointer_traits::pointer_to(*streambuf_type::epptr())); // reset written data + streambuf_type::setp(_Begin_seq_ptr, streambuf_type::epptr()); // reset written data return _Result; } @@ -174,10 +173,10 @@ protected: } const size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); - const pointer _Old_ptr = pointer_traits::pointer_to(*streambuf_type::pbase()); + const _Elem* const _Old_ptr = streambuf_type::pbase(); const size_type _Old_data_size = _Get_data_size(); - const pointer _New_ptr = _Al.allocate(_New_capacity); + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_New_capacity)); _Traits::copy(_New_ptr, _Old_ptr, _Old_data_size); streambuf_type::setp(_New_ptr, _New_ptr + _Old_data_size, _New_ptr + _New_capacity); @@ -190,13 +189,15 @@ private: static constexpr size_type _Min_size = 32; // constant for minimum buffer size void _Init() { - const pointer _New_ptr = _Al.allocate(_Min_size); + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_Min_size)); streambuf_type::setp(_New_ptr, _New_ptr + _Min_size); } void _Tidy() noexcept { const size_type _Buf_size = _Get_buffer_size(); - _Al.deallocate(pointer_traits::pointer_to(*streambuf_type::pbase()), _Buf_size); + if (0 < _Buf_size) { + _Al.deallocate(pointer_traits::pointer_to(*streambuf_type::pbase()), _Buf_size); + } streambuf_type::setp(nullptr, nullptr, nullptr); if (_Wrapped != nullptr) { @@ -221,11 +222,13 @@ private: if (_Al == _Right._Al) { _Move_assign(_STD move(_Right), _Equal_allocators{}); } else { + _Tidy(); + const size_type _Right_buf_size = _Right._Get_buffer_size(); const size_type _Right_data_size = _Right._Get_data_size(); - const pointer _New_ptr = _Al.allocate(_Right_buf_size); - _Traits::copy(_New_ptr, pointer_traits::pointer_to(*_Right.pbase()), _Right_data_size); + _Elem* const _New_ptr = _Unfancy(_Al.allocate(_Right_buf_size)); + _Traits::copy(_New_ptr, _Right.pbase(), _Right_data_size); streambuf_type::setp(_New_ptr, _New_ptr + _Right_data_size, _New_ptr + _Right_buf_size); streambuf_type::_Plocale = _STD move(_Right._Plocale); From 12ac2f942ea5f1370789b60a9cb43bd05091b6cc Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Fri, 15 Jan 2021 21:43:58 +0000 Subject: [PATCH 24/60] Add tests to basic_syncbuf and feature test macro --- stl/inc/yvals_core.h | 3 + .../env.lst | 4 + .../test.cpp | 287 ++++++++++++++++++ .../test.hpp | 219 +++++++++++++ .../test.cpp | 8 +- .../test.compile.pass.cpp | 14 + 6 files changed, 534 insertions(+), 1 deletion(-) create mode 100644 tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst create mode 100644 tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp create mode 100644 tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index e37d8daf036..9fa4ad33e88 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -133,6 +133,7 @@ // _HAS_CXX20 directly controls: // P0019R8 atomic_ref // P0020R6 atomic, atomic, atomic +// P0053R7 C++ Synchronized Buffered Ostream // P0122R7 // P0202R3 constexpr For And exchange() // P0318R1 unwrap_reference, unwrap_ref_decay @@ -164,6 +165,7 @@ // P0660R10 And jthread // P0674R1 make_shared() For Arrays // P0718R2 atomic>, atomic> +// P0753R2 Manipulators for C++ Synchronized Buffered Ostream // P0758R1 is_nothrow_convertible // P0768R1 Library Support For The Spaceship Comparison Operator <=> // P0769R2 shift_left(), shift_right() @@ -1221,6 +1223,7 @@ #define __cpp_lib_span 202002L #define __cpp_lib_ssize 201902L #define __cpp_lib_starts_ends_with 201711L +#define __cpp_lib_syncbuf 201803L #ifdef __cpp_lib_concepts // TRANSITION, GH-395 #define __cpp_lib_three_way_comparison 201711L diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp new file mode 100644 index 00000000000..87ec2808ba8 --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -0,0 +1,287 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include "test.hpp" + +#include + +using namespace std; + +static_assert(is_default_constructible_v); +static_assert(is_constructible_v); +static_assert(is_constructible_v); +static_assert(is_move_constructible_v); +static_assert(is_move_assignable_v); +static_assert(is_destructible_v); + +template +class test_syncbuf : public basic_syncbuf { +public: + using size_type = typename Alloc::size_type; + using value_type = typename Alloc::value_type; + using _Mybase = basic_syncbuf; + using streambuf_type = typename _Mybase::streambuf_type; + + using _Mybase::epptr; + using _Mybase::pbase; + using _Mybase::pptr; + + test_syncbuf() = default; + + explicit test_syncbuf(streambuf_type* _Strbuf) : _Mybase(_Strbuf) {} + + test_syncbuf(streambuf_type* _Strbuf, const Alloc& _Al) : _Mybase(_Strbuf, _Al) {} + + test_syncbuf(test_syncbuf&&) = default; + test_syncbuf& operator=(test_syncbuf&&) = default; + ~test_syncbuf() = default; + + auto Test_get_buffer_size() const noexcept { + return static_cast(epptr() - pbase()); + } + auto Test_get_data_size() const noexcept { + return static_cast(pptr() - pbase()); + } + + test_syncbuf(const test_syncbuf&) = delete; + test_syncbuf& operator=(const test_syncbuf&) = delete; +}; + +template +void test_syncbuf_member_functions(string_buffer* buf = nullptr) { + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + Alloc alloc{}; + + Syncbuf aSyncbuf{buf, alloc}; + + // test construction post-conditions + assert(aSyncbuf.get_wrapped() == buf); + assert(aSyncbuf.get_allocator() == alloc); + assert(aSyncbuf.Test_get_data_size() == 0); + assert(aSyncbuf.Test_get_buffer_size() == _Min_syncbuf_size); + + // check emit post-conditions with no input + if (buf) { + assert(aSyncbuf.emit() == true); + } else { + assert(aSyncbuf.emit() == false); + } + + OStream os{&aSyncbuf}; + + os << "A small string\n"; + assert(aSyncbuf.Test_get_data_size() == 15); + if (buf) { + assert(aSyncbuf.emit() == true); + assert(buf->str == "A small string\n"); + buf->str.clear(); + } else { + assert(os.rdstate() == ios::goodbit); + assert(aSyncbuf.emit() == false); + } + + os << "A string holds more than 32 characters"; // requires one re-allocation + if (buf) { + assert(aSyncbuf.Test_get_data_size() == 38); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string holds more than 32 characters"); + buf->str.clear(); + } else { + assert(aSyncbuf.Test_get_data_size() == _Min_syncbuf_size); // if _Wrapped is nullptr, re-allocation will not + // occur and will return a _Traits::eof bit + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == false); + } + + os << "A string that will definitely overflow the small_size_allocator"; // requires more than one re-allocation + if (buf) { + if constexpr (is_base_of_v) { // fail to allocate enough memory + assert(aSyncbuf.Test_get_data_size() == _Min_size_allocation); + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string that will definitely overflow the small_s"); + } else { + assert(aSyncbuf.Test_get_data_size() == 63); + assert(os.rdstate() == ios::goodbit); + assert(aSyncbuf.emit() == true); + assert(buf->str == "A string that will definitely overflow the small_size_allocator"); + } + buf->str.clear(); + } else { + assert(aSyncbuf.Test_get_data_size() == _Min_syncbuf_size); + assert(os.rdstate() == ios::badbit); + os.setstate(ios::goodbit); + assert(aSyncbuf.emit() == false); + } +} + +template +void test_syncbuf_synchronization(string_buffer* buf) { + assert(buf); // meaningless to run with nullptr + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + { + Syncbuf buf1{buf}; + OStream os1{&buf1}; + os1 << "Last element "; + { + Syncbuf buf2{buf}; + buf2.set_emit_on_sync(false); + OStream os2{&buf2}; + os2 << "Second element "; + int syncResult = buf2.pubsync(); // trigger a sync + assert(syncResult == 0); + { + Syncbuf buf3{buf}; + OStream{&buf3} << "First element to be presented!\n"; + } + os2 << "to be presented!\n"; + } + os1 << "to be presented!\n"; + } + assert( + buf->str == "First element to be presented!\nSecond element to be presented!\nLast element to be presented!\n"); + buf->str.clear(); + { + Syncbuf buf1{buf}; + OStream os1{&buf1}; + os1 << "Last element "; + { + Syncbuf buf2{buf}; + buf2.set_emit_on_sync(true); + OStream os2{&buf2}; + os2 << "First element to by emitted by sync!\n"; + int syncResult = buf2.pubsync(); // trigger a sync + if constexpr (ThrowOnSync) { + assert(syncResult == -1); + } else { + assert(syncResult == 0); + } + { + Syncbuf buf3{buf}; + OStream{&buf3} << "Second element to be presented!\n"; + } + os2 << "Third element to be presented!\n"; + } + os1 << "to be presented!\n"; + } + assert(buf->str + == "First element to by emitted by sync!\nSecond element to be presented!\nThird element to be " + "presented!\nLast element to be presented!\n"); + buf->str.clear(); +} + +template +void test_syncbuf_move_swap_operations(string_buffer* buf) { + + using value_type = typename Alloc::value_type; + using Syncbuf = test_syncbuf, Alloc>; + using OStream = basic_ostream>; + + { // test move constructor + Syncbuf buf1{buf}; + OStream(&buf1) << "Some input"; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + Syncbuf buf2{move(buf1)}; + assert(buf->str == ""); + + // move constructor post-conditions + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == nullptr); + assert(buf1.pbase() == buf1.pptr()); + assert(buf1.Test_get_buffer_size() == 0); + assert(buf1.Test_get_data_size() == 0); + } + assert(buf->str == "Some input"); + buf->str.clear(); + { // test move assignment + Syncbuf buf1{buf}; + OStream{&buf1} << "Some input"; + Syncbuf buf2{nullptr}; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + buf2 = move(buf1); + + // move assignment post-conditions + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == nullptr); + assert(buf1.Test_get_buffer_size() == 0); + assert(buf1.Test_get_data_size() == 0); + + if constexpr (allocator_traits::propagate_on_container_move_assignment::value + && is_same_v::is_always_equal, true_type>) { + assert(buf1.get_allocator() == buf2.get_allocator()); + } else if constexpr (is_same_v::is_always_equal, true_type>) { + assert(buf1.get_allocator() == buf2.get_allocator()); + } else { + assert(buf1.get_allocator() != buf2.get_allocator()); + } + } + assert(buf->str == "Some input"); + buf->str.clear(); + { // test swap + Syncbuf buf1{buf}; + OStream{&buf1} << "Some input that requires re-allocation"; + Syncbuf buf2{nullptr}; + auto buf1WrappedObject = buf1.get_wrapped(); + auto buf1BufferSize = buf1.Test_get_buffer_size(); + auto buf1DataSize = buf1.Test_get_data_size(); + auto buf2WrappedObject = buf2.get_wrapped(); + auto buf2BufferSize = buf2.Test_get_buffer_size(); + auto buf2DataSize = buf2.Test_get_data_size(); + if constexpr (allocator_traits::propagate_on_container_swap::value + || is_same_v::is_always_equal, true_type>) { + buf1.swap(buf2); + assert(buf2.get_wrapped() == buf1WrappedObject); + assert(buf2.Test_get_buffer_size() == buf1BufferSize); + assert(buf2.Test_get_data_size() == buf1DataSize); + assert(buf1.get_wrapped() == buf2WrappedObject); + assert(buf1.Test_get_buffer_size() == buf2BufferSize); + assert(buf1.Test_get_data_size() == buf2DataSize); + } + } + assert(buf->str == "Some input that requires re-allocation"); + buf->str.clear(); +} + +int main() { + string_buffer char_buffer{}; + string_buffer no_sync_char_buffer{}; + + test_syncbuf_member_functions>(); + test_syncbuf_member_functions>(); + test_syncbuf_member_functions>(); + test_syncbuf_member_functions>(); + + test_syncbuf_member_functions>(&char_buffer); + test_syncbuf_member_functions>(&char_buffer); + test_syncbuf_member_functions>(&char_buffer); + test_syncbuf_member_functions>(&char_buffer); + + test_syncbuf_synchronization>(&char_buffer); + test_syncbuf_synchronization>(&no_sync_char_buffer); + test_syncbuf_synchronization>(&char_buffer); + test_syncbuf_synchronization>(&no_sync_char_buffer); + + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); +} diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp new file mode 100644 index 00000000000..7f0896adfbe --- /dev/null +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -0,0 +1,219 @@ +#include +#include +#include +#include + +constexpr size_t _Min_size_allocation = 50; +constexpr size_t _Min_syncbuf_size = 32; + +template +class string_buffer : public basic_streambuf> { // represents the wrapped object in syncbuf +public: + string_buffer() = default; + ~string_buffer() = default; + + streamsize xsputn(const Ty* _Ptr, streamsize _Count) override { + str.append(_Ptr, _Count); + return _Count; + } + + int sync() override { + if constexpr (ThrowOnSync) { + return -1; + } else { + return 0; + } + } + + string str; +}; + +class small_size_allocation { +public: + using size_type = size_t; + + _NODISCARD size_type max_size() const noexcept { + return _Min_size_allocation; + } +}; + +template +class fancy_ptr { +private: + uint64_t ptr = 0; + +public: + fancy_ptr() = default; + fancy_ptr(std::nullptr_t) {} + explicit fancy_ptr(Ty* _ptr) : ptr(static_cast(reinterpret_cast(_ptr))) {} + template + explicit fancy_ptr(const fancy_ptr& right) : ptr(right.ptr) {} + + Ty& operator*() const { + return *reinterpret_cast(static_cast(ptr)); + } + + template + static fancy_ptr pointer_to(Other& _ptr) noexcept { + return fancy_ptr(addressof(_ptr)); + } +}; + +template +class fancy_ptr_allocator { +public: + using value_type = Ty; + using size_type = size_t; + using pointer = fancy_ptr; + using propagate_on_container_move_assignment = true_type; + using propagate_on_container_swap = true_type; + + constexpr fancy_ptr_allocator() noexcept = default; + + _NODISCARD pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + auto _Ptr = ::operator new(_Count * sizeof(value_type)); + return static_cast(static_cast(_Ptr)); + } + + void deallocate(pointer const _Ptr, const size_t _Count) noexcept { + ::operator delete(&*_Ptr, _Count); + } +}; + +template +_NODISCARD bool operator==(const fancy_ptr_allocator&, const fancy_ptr_allocator&) noexcept { + return true; +} + +template +class small_size_fancy_ptr_allocator : public small_size_allocation { +public: + using value_type = Ty; + using pointer = fancy_ptr; + using propagate_on_container_move_assignment = true_type; + using propagate_on_container_swap = true_type; + + constexpr small_size_fancy_ptr_allocator() noexcept = default; + + _NODISCARD pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + auto _Ptr = ::operator new(_Count * sizeof(value_type)); + return static_cast(static_cast(_Ptr)); + } + + void deallocate(pointer const _Ptr, const size_t _Count) noexcept { + ::operator delete(&*_Ptr, _Count); + } +}; + +template +_NODISCARD bool operator==( + const small_size_fancy_ptr_allocator&, const small_size_fancy_ptr_allocator&) noexcept { + return true; +} + +template +class small_size_allocator : public small_size_allocation { +public: + using value_type = Ty; + using pointer = Ty*; + using propagate_on_container_move_assignment = true_type; + using propagate_on_container_swap = true_type; + + constexpr small_size_allocator() noexcept = default; + + _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + return static_cast(::operator new(_Count * sizeof(value_type))); + } + + void deallocate(pointer const _Ptr, const size_t _Count) noexcept { + ::operator delete(_Ptr, _Count); + } +}; + +template +_NODISCARD bool operator==(const small_size_allocator&, const small_size_allocator&) noexcept { + return true; +} + +template +class non_move_assignable_non_equal_allocator { +public: + using value_type = Ty; + using size_type = size_t; + using pointer = Ty*; + using propagate_on_container_move_assignment = false_type; + using propagate_on_container_swap = true_type; + using is_always_equal = false_type; + + constexpr non_move_assignable_non_equal_allocator() noexcept = default; + + _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + return static_cast(::operator new(_Count * sizeof(value_type))); + } + + void deallocate(pointer const _Ptr, const size_t _Count) noexcept { + ::operator delete(_Ptr, _Count); + } + + _NODISCARD size_type max_size() const noexcept { + return 50; + } +}; + +template +_NODISCARD bool operator==(const non_move_assignable_non_equal_allocator&, + const non_move_assignable_non_equal_allocator&) noexcept { + return false; +} + +template +class non_move_assignable_equal_allocator { +public: + using value_type = Ty; + using size_type = size_t; + using pointer = Ty*; + using propagate_on_container_move_assignment = false_type; + using propagate_on_container_swap = true_type; + + constexpr non_move_assignable_equal_allocator() noexcept = default; + + _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + return static_cast(::operator new(_Count * sizeof(value_type))); + } + + void deallocate(pointer const _Ptr, const size_t _Count) noexcept { + ::operator delete(_Ptr, _Count); + } +}; + +template +_NODISCARD bool operator==( + const non_move_assignable_equal_allocator&, const non_move_assignable_equal_allocator&) noexcept { + return true; +} + +template +class non_swapable_equal_allocator { +public: + using value_type = Ty; + using size_type = size_t; + using pointer = Ty*; + using propagate_on_container_move_assignment = true_type; + using propagate_on_container_swap = false_type; + + constexpr non_swapable_equal_allocator() noexcept = default; + + _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + return static_cast(::operator new(_Count * sizeof(value_type))); + } + + void deallocate(pointer const _Ptr, const size_t _Count) noexcept { + ::operator delete(_Ptr, _Count); + } +}; + +template +_NODISCARD bool operator==( + const non_swapable_equal_allocator&, const non_swapable_equal_allocator&) noexcept { + return true; +} diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index ee3bad3492f..f1c4b1d11f9 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -804,7 +804,13 @@ int main() { { puts("Testing ."); - puts("(TRANSITION, not yet implemented.)"); + syncbuf sync_buf{nullptr}; + assert(sync_buf.get_wrapped() == nullptr); + assert(sync_buf.get_allocator() == std::allocator{}); + assert(sync_buf.emit() == false); + osyncstream sync_str{cout}; + sync_str << "Testing P1502R1_standard_library_header_units.\n"; + assert(sync_str.rdbuf()->get_wrapped() == cout.rdbuf()); } { diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 9a6cd10c972..55b1b36db53 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1332,6 +1332,20 @@ STATIC_ASSERT(__cpp_lib_starts_ends_with == 201711L); #endif #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_syncbuf +#error __cpp_lib_syncbuf is not defined +#elif __cpp_lib_syncbuf != 201803L +#error __cpp_lib_syncbuf is not 201803L +#else +STATIC_ASSERT(__cpp_lib_syncbuf == 201803L); +#endif +#else +#ifdef __cpp_lib_syncbuf +#error __cpp_lib_syncbuf is defined +#endif +#endif + #ifndef __cpp_lib_string_udls #error __cpp_lib_string_udls is not defined #elif __cpp_lib_string_udls != 201304L From acbf0bf4b036bece5e9ab6b4a19f1a80fa2a1e3b Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Fri, 15 Jan 2021 21:57:50 +0000 Subject: [PATCH 25/60] organize include headers --- .../tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp | 2 -- .../tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp index 87ec2808ba8..ec7ffd0686d 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -3,8 +3,6 @@ #include "test.hpp" -#include - using namespace std; static_assert(is_default_constructible_v); diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index 7f0896adfbe..4b46984c741 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -1,8 +1,12 @@ #include +#include #include +#include #include #include +using namespace std; + constexpr size_t _Min_size_allocation = 50; constexpr size_t _Min_syncbuf_size = 32; From 95e50c47bf8434b5ac83305e4a56c246b8590e79 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Fri, 15 Jan 2021 22:14:01 +0000 Subject: [PATCH 26/60] Fix tests --- stl/inc/syncstream | 5 ++++- .../P0053R7_cpp_synchronized_buffered_ostream/test.cpp | 6 +++--- .../P0053R7_cpp_synchronized_buffered_ostream/test.hpp | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 6743cd357e7..8e1680c86b2 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -122,7 +122,10 @@ public: if (_Data_size > 0 || _Mybase::_Sync_recorded) { scoped_lock _Guard(*_Get_mutex_for_instance(_Wrapped)); - if (_Data_size > 0 && _Data_size != static_cast(_Wrapped->sputn(_Begin_seq_ptr, _Data_size))) { + if (_Data_size > 0 + && _Data_size + != static_cast( + _Wrapped->sputn(_Begin_seq_ptr, static_cast(_Data_size)))) { _Result = false; } diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp index ec7ffd0686d..31df6ff2c4e 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -223,9 +223,9 @@ void test_syncbuf_move_swap_operations(string_buffer assert(buf1.Test_get_data_size() == 0); if constexpr (allocator_traits::propagate_on_container_move_assignment::value - && is_same_v::is_always_equal, true_type>) { + && is_same_v::is_always_equal, true_type>) { assert(buf1.get_allocator() == buf2.get_allocator()); - } else if constexpr (is_same_v::is_always_equal, true_type>) { + } else if constexpr (is_same_v::is_always_equal, true_type>) { assert(buf1.get_allocator() == buf2.get_allocator()); } else { assert(buf1.get_allocator() != buf2.get_allocator()); @@ -244,7 +244,7 @@ void test_syncbuf_move_swap_operations(string_buffer auto buf2BufferSize = buf2.Test_get_buffer_size(); auto buf2DataSize = buf2.Test_get_data_size(); if constexpr (allocator_traits::propagate_on_container_swap::value - || is_same_v::is_always_equal, true_type>) { + || is_same_v::is_always_equal, true_type>) { buf1.swap(buf2); assert(buf2.get_wrapped() == buf1WrappedObject); assert(buf2.Test_get_buffer_size() == buf1BufferSize); diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index 4b46984c741..66aec358232 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -17,7 +17,7 @@ class string_buffer : public basic_streambuf> { // represent ~string_buffer() = default; streamsize xsputn(const Ty* _Ptr, streamsize _Count) override { - str.append(_Ptr, _Count); + str.append(_Ptr, static_cast(_Count)); return _Count; } From e355673d41a666e91564bf6b2a227e6602067fa3 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Fri, 15 Jan 2021 22:27:32 +0000 Subject: [PATCH 27/60] Fix tests again - Comment out the probable source of unexpected failure --- .../tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp index 31df6ff2c4e..d6df9542ac6 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -279,7 +279,7 @@ int main() { test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); - test_syncbuf_move_swap_operations>(&char_buffer); + // test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); } From 5a085af3208e325c270de67b826ce128e7ed6a11 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Fri, 15 Jan 2021 22:50:41 +0000 Subject: [PATCH 28/60] Add the new test to the list --- tests/std/test.lst | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/test.lst b/tests/std/test.lst index 014d2c798d4..6186ed4ec17 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -207,6 +207,7 @@ tests\P0024R2_parallel_algorithms_transform_inclusive_scan tests\P0024R2_parallel_algorithms_transform_reduce tests\P0035R4_over_aligned_allocation tests\P0040R3_extending_memory_management_tools +tests\P0053R7_cpp_synchronized_buffered_ostream tests\P0067R5_charconv tests\P0083R3_splicing_maps_and_sets tests\P0088R3_variant From e4bbea18b8f87dcf0653792fef0da020ff055d94 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Sat, 16 Jan 2021 00:17:45 +0000 Subject: [PATCH 29/60] Add tests for basic_osyncstream --- .../test.cpp | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp index d6df9542ac6..cca1d6090fc 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -258,10 +258,109 @@ void test_syncbuf_move_swap_operations(string_buffer buf->str.clear(); } +template +void test_osyncstream(string_buffer* buf = nullptr) { + using value_type = typename Alloc::value_type; + using OSyncStream = basic_osyncstream, Alloc>; + + { + OSyncStream oss{buf}; + + // test construction post-conditions + assert(oss.get_wrapped() == buf); + assert(oss.rdbuf()->get_wrapped() == buf); + + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + + oss << "A small string\n"; + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + if (buf) { + assert(buf->str == "A small string\n"); + buf->str.clear(); + } + + oss << "A string holds more than 32 characters"; // requires one re-allocation + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + if (buf) { + assert(buf->str == "A string holds more than 32 characters"); + buf->str.clear(); + } + + oss << "A string that will definitely overflow the small_size_allocator"; // requires more than one + // re-allocation + if constexpr (is_base_of_v) { // fail to allocate enough memory + assert(oss.rdstate() == ios::badbit); + oss.clear(); + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + assert(buf ? buf->str == "A string that will definitely overflow the small_s" : true); + } else { + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + oss.emit(); + assert(oss.rdstate() == (buf ? ios::goodbit : ios::badbit)); + assert(buf ? buf->str == "A string that will definitely overflow the small_size_allocator" : true); + } + } + if (buf) { + buf->str.clear(); + } + + { // move construction + OSyncStream oss{buf}; + oss << "Some input"; + auto ossWrapped = oss.get_wrapped(); + OSyncStream oss1{move(oss)}; + + assert(oss1.get_wrapped() == ossWrapped); + assert(oss.get_wrapped() == nullptr); + } + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + + { + OSyncStream oss{buf}; + oss << "Some input"; + auto ossWrapped = oss.get_wrapped(); + + OSyncStream oss1{buf}; + oss1 << "An input to emit first\n"; + + oss1 = move(oss); + + assert(oss1.get_wrapped() == ossWrapped); + assert(oss.get_wrapped() == nullptr); + if (buf) { + assert(buf->str == "An input to emit first\n"); + buf->str.clear(); + } + } + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + + { // test synchronization + OSyncStream oss(buf); + oss << "Last "; + { OSyncStream(oss.get_wrapped()) << "First Input!\n"; } + oss << "Input!" << '\n'; + } + if (buf) { + assert(buf->str == "First Input!\nLast Input!\n"); + buf->str.clear(); + } +} + int main() { string_buffer char_buffer{}; string_buffer no_sync_char_buffer{}; + // Testing basic_syncbuf test_syncbuf_member_functions>(); test_syncbuf_member_functions>(); test_syncbuf_member_functions>(); @@ -282,4 +381,15 @@ int main() { // test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); + + // Testing basic_osyncstream + test_osyncstream>(); + test_osyncstream>(); + test_osyncstream>(); + test_osyncstream>(); + + test_osyncstream>(&char_buffer); + test_osyncstream>(&char_buffer); + test_osyncstream>(&char_buffer); + test_osyncstream>(&char_buffer); } From 3554be3dcc9d67f405cb776fa9962763d3a8e73b Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Sat, 16 Jan 2021 21:52:06 +0000 Subject: [PATCH 30/60] Add osyncstream manipulators tests and enable a disabled test --- tests/std/test.lst | 1 + .../test.cpp | 2 +- .../env.lst | 4 + .../test.cpp | 112 ++++++++++++++++++ 4 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst create mode 100644 tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index 6186ed4ec17..20da9f6c1e4 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -251,6 +251,7 @@ tests\P0660R10_stop_token tests\P0660R10_stop_token_death tests\P0674R1_make_shared_for_arrays tests\P0718R2_atomic_smart_ptrs +tests\P0753R2_manipulators_for_cpp_synchronized_buffered_ostream tests\P0758R1_is_nothrow_convertible tests\P0768R1_spaceship_cpos tests\P0768R1_spaceship_operator diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp index cca1d6090fc..47c84cd7cff 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -378,7 +378,7 @@ int main() { test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); - // test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp new file mode 100644 index 00000000000..8701da086e6 --- /dev/null +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp @@ -0,0 +1,112 @@ +#include +#include +#include +#include + +using namespace std; + +template +class string_buffer : public basic_streambuf> { // represents the wrapped object in syncbuf +public: + string_buffer() = default; + ~string_buffer() = default; + + streamsize xsputn(const Ty* _Ptr, streamsize _Count) override { + str.append(_Ptr, static_cast(_Count)); + return _Count; + } + + int sync() override { + if constexpr (ThrowOnSync) { + return -1; + } else { + return 0; + } + } + + string str; +}; + +template +class Test_Basic_syncbuf : public _Basic_syncbuf_impl { + using Mybase = _Basic_syncbuf_impl; + +public: + Test_Basic_syncbuf() = default; + Test_Basic_syncbuf(Test_Basic_syncbuf&& _Right) = default; + + using Mybase::_Emit_on_sync; +}; + +template +requires(is_base_of_v, + Ty>) void test_osyncstream_manipulators(string_buffer* buf = nullptr) { + using char_type = typename Ty::char_type; + using traits_type = typename Ty::traits_type; + + Ty os{buf}; + os << "Some input"; + + assert(addressof(emit_on_flush(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + auto* aSyncbuf = reinterpret_cast*>(os.rdbuf()); + assert(aSyncbuf->_Emit_on_sync == true); + if (buf) { + assert(buf->str == ""); + } + } + + assert(addressof(flush_emit(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + if constexpr (ThrowOnSync) { + assert(os.rdstate() == ios::badbit); + } else { + assert(os.rdstate() == (buf ? ios::goodbit : ios::badbit)); + } + if (buf) { + assert(buf->str == "Some input"); + buf->str.clear(); + } + os.clear(); + os << "Another input"; + } + + assert(addressof(noemit_on_flush(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + auto* aSyncbuf = reinterpret_cast*>(os.rdbuf()); + assert(aSyncbuf->_Emit_on_sync == false); + } + + assert(addressof(flush_emit(os)) == addressof(os)); + if constexpr (is_base_of_v, Ty>) { + if constexpr (ThrowOnSync) { + assert(os.rdstate() == ios::badbit); + } else { + assert(os.rdstate() == (buf ? ios::goodbit : ios::badbit)); + } + if (buf) { + assert(buf->str == "Another input"); + } + os.clear(); + } + + if (buf) { + buf->str.clear(); + } +} + +int main() { + string_buffer char_buffer{}; + string_buffer no_sync_char_buffer{}; + + test_osyncstream_manipulators>(); + test_osyncstream_manipulators, allocator>, allocator>(); + + test_osyncstream_manipulators>(&char_buffer); + test_osyncstream_manipulators, allocator>, allocator>( + &char_buffer); + + test_osyncstream_manipulators>(&no_sync_char_buffer); + test_osyncstream_manipulators, allocator>, allocator>( + &no_sync_char_buffer); +} \ No newline at end of file From 3cccfc3f5708d5a6024c952836ddbf683eac84f9 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Sat, 16 Jan 2021 21:56:53 +0000 Subject: [PATCH 31/60] clang-format --- .../test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp index 8701da086e6..61925b3e26f 100644 --- a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp @@ -109,4 +109,4 @@ int main() { test_osyncstream_manipulators>(&no_sync_char_buffer); test_osyncstream_manipulators, allocator>, allocator>( &no_sync_char_buffer); -} \ No newline at end of file +} From f873fdda75dafb84dd911ea519f475b0b60a6747 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Sat, 16 Jan 2021 22:07:11 +0000 Subject: [PATCH 32/60] Fix typo --- .../test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp index 61925b3e26f..c742556b8fb 100644 --- a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp @@ -1,7 +1,7 @@ #include #include #include -#include +#include using namespace std; From 1f7e98d6f7ae0a59925fb765a8a2b348468fd84e Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Mon, 18 Jan 2021 14:43:17 +0000 Subject: [PATCH 33/60] Add syncstream header to header-units --- stl/inc/header-units.json | 1 + 1 file changed, 1 insertion(+) diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index cb1ef9efeb5..66cb2996698 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -96,6 +96,7 @@ "string", "string_view", "strstream", + "syncstream", "system_error", "thread", "tuple", From 245202444cca8587fa479f77b21d48678cde5f75 Mon Sep 17 00:00:00 2001 From: MichaelRizkalla Date: Sat, 23 Jan 2021 15:28:04 +0000 Subject: [PATCH 34/60] Guard functions that require runtime information checking --- stl/inc/ostream | 9 +++++++++ .../test.cpp | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/stl/inc/ostream b/stl/inc/ostream index f0f7df17675..cc2dc3479be 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -994,6 +994,7 @@ basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL flush(basic_ostream<_Elem, _Tr } #if _HAS_CXX20 +#ifdef _CPPRTTI template basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); @@ -1023,6 +1024,14 @@ basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>& _Ostr) } return _Ostr; } +#else // _CPPRTTI +template +basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +template +basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +template +basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>&) = delete; // requires /GR option +#endif // _CPPRTTI #endif // _HAS_CXX20 // INSERTER FOR error_category diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp index c742556b8fb..d47e7b550ba 100644 --- a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp @@ -98,7 +98,7 @@ requires(is_base_of_v char_buffer{}; string_buffer no_sync_char_buffer{}; - +#ifdef _CPPRTTI test_osyncstream_manipulators>(); test_osyncstream_manipulators, allocator>, allocator>(); @@ -109,4 +109,5 @@ int main() { test_osyncstream_manipulators>(&no_sync_char_buffer); test_osyncstream_manipulators, allocator>, allocator>( &no_sync_char_buffer); +#endif } From 35918c53b43ec7665b7b517b5f3adc4a858b7de4 Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Wed, 27 Jan 2021 19:49:07 +0000 Subject: [PATCH 35/60] Fix bugs Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Co-Authored-By: Berrysoft <37586447+Berrysoft@users.noreply.github.com> --- stl/inc/syncstream | 2 +- .../test.hpp | 24 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 8e1680c86b2..38d437e545e 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -234,7 +234,7 @@ private: _Traits::copy(_New_ptr, _Right.pbase(), _Right_data_size); streambuf_type::setp(_New_ptr, _New_ptr + _Right_data_size, _New_ptr + _Right_buf_size); - streambuf_type::_Plocale = _STD move(_Right._Plocale); + _STD swap(streambuf_type::_Plocale, _Right._Plocale); _STD swap(_Mybase::_Emit_on_sync, _Right._Emit_on_sync); _STD swap(_Mybase::_Sync_recorded, _Right._Sync_recorded); diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index 66aec358232..e30d119d26b 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -75,12 +75,12 @@ class fancy_ptr_allocator { constexpr fancy_ptr_allocator() noexcept = default; _NODISCARD pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - auto _Ptr = ::operator new(_Count * sizeof(value_type)); + auto _Ptr = allocator{}.allocate(_Count); return static_cast(static_cast(_Ptr)); } void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - ::operator delete(&*_Ptr, _Count); + allocator{}.deallocate(&*_Ptr, _Count); } }; @@ -100,12 +100,12 @@ class small_size_fancy_ptr_allocator : public small_size_allocation { constexpr small_size_fancy_ptr_allocator() noexcept = default; _NODISCARD pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - auto _Ptr = ::operator new(_Count * sizeof(value_type)); + auto _Ptr = allocator{}.allocate(_Count); return static_cast(static_cast(_Ptr)); } void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - ::operator delete(&*_Ptr, _Count); + allocator{}.deallocate(&*_Ptr, _Count); } }; @@ -126,11 +126,11 @@ class small_size_allocator : public small_size_allocation { constexpr small_size_allocator() noexcept = default; _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(::operator new(_Count * sizeof(value_type))); + return static_cast(allocator{}.allocate(_Count)); } void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - ::operator delete(_Ptr, _Count); + allocator{}.deallocate(_Ptr, _Count); } }; @@ -152,11 +152,11 @@ class non_move_assignable_non_equal_allocator { constexpr non_move_assignable_non_equal_allocator() noexcept = default; _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(::operator new(_Count * sizeof(value_type))); + return static_cast(allocator{}.allocate(_Count)); } void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - ::operator delete(_Ptr, _Count); + allocator{}.deallocate(_Ptr, _Count); } _NODISCARD size_type max_size() const noexcept { @@ -182,11 +182,11 @@ class non_move_assignable_equal_allocator { constexpr non_move_assignable_equal_allocator() noexcept = default; _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(::operator new(_Count * sizeof(value_type))); + return static_cast(allocator{}.allocate(_Count)); } void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - ::operator delete(_Ptr, _Count); + allocator{}.deallocate(_Ptr, _Count); } }; @@ -208,11 +208,11 @@ class non_swapable_equal_allocator { constexpr non_swapable_equal_allocator() noexcept = default; _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(::operator new(_Count * sizeof(value_type))); + return static_cast(allocator{}.allocate(_Count)); } void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - ::operator delete(_Ptr, _Count); + allocator{}.deallocate(_Ptr, _Count); } }; From d1ffbe687ac94f46e53f2a4d93e33a30181f07f4 Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Fri, 29 Jan 2021 19:20:18 +0000 Subject: [PATCH 36/60] Apply some code review changes Co-Authored-By: Stephan T. Lavavej --- stl/inc/ostream | 6 +- stl/inc/syncstream | 58 +++++++++---------- stl/inc/yvals_core.h | 4 +- stl/src/syncstream.cpp | 6 +- .../test.cpp | 2 +- .../test.compile.pass.cpp | 28 ++++----- 6 files changed, 52 insertions(+), 52 deletions(-) diff --git a/stl/inc/ostream b/stl/inc/ostream index cc2dc3479be..75de90733fc 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -997,7 +997,7 @@ basic_ostream<_Elem, _Traits>& __CLRCALL_OR_CDECL flush(basic_ostream<_Elem, _Tr #ifdef _CPPRTTI template basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { - auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); if (_Sync_buf_ptr) { _Sync_buf_ptr->set_emit_on_sync(true); } @@ -1006,7 +1006,7 @@ basic_ostream<_Elem, _Traits>& emit_on_flush(basic_ostream<_Elem, _Traits>& _Ost template basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>& _Ostr) { - auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); if (_Sync_buf_ptr) { _Sync_buf_ptr->set_emit_on_sync(false); } @@ -1016,7 +1016,7 @@ basic_ostream<_Elem, _Traits>& noemit_on_flush(basic_ostream<_Elem, _Traits>& _O template basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>& _Ostr) { _Ostr.flush(); - auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); + const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); if (_Sync_buf_ptr) { if (!_Sync_buf_ptr->_Do_emit()) { _Ostr.setstate(ios::badbit); diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 38d437e545e..f953e338457 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -27,11 +27,11 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN -extern "C" shared_mutex* __cdecl _Get_mutex_for_instance(void* _Ptr) noexcept; -extern "C" void __cdecl _Acquire_mutex_for_instance(void* _Ptr); -extern "C" void __cdecl _Release_mutex_for_instance(void* _Ptr) noexcept; +extern "C" _NODISCARD shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept; +extern "C" void __stdcall _Acquire_mutex_for_instance(void* _Ptr); +extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept; -// CLASS TEMPLATE _Basic_osyncbuf_impl +// CLASS TEMPLATE _Basic_syncbuf_impl template class _Basic_syncbuf_impl : public basic_streambuf<_Elem, _Traits> { public: @@ -47,10 +47,10 @@ protected: _Basic_syncbuf_impl() = default; _Basic_syncbuf_impl(_Basic_syncbuf_impl&& _Right) { - swap(_Right); + _Swap(_Right); } - void swap(_Basic_syncbuf_impl& _Right) { + void _Swap(_Basic_syncbuf_impl& _Right) noexcept { _Mysb::swap(_Right); _STD swap(_Emit_on_sync, _Right._Emit_on_sync); _STD swap(_Sync_recorded, _Right._Sync_recorded); @@ -70,9 +70,9 @@ public: using allocator_type = _Alloc; using streambuf_type = basic_streambuf<_Elem, _Traits>; - using _Mybase = _Basic_syncbuf_impl<_Elem, _Traits>; - using pointer = typename allocator_traits<_Alloc>::pointer; - using size_type = typename allocator_traits<_Alloc>::size_type; + using _Mybase = _Basic_syncbuf_impl<_Elem, _Traits>; + using _Pointer = typename allocator_traits<_Alloc>::pointer; + using _Size_type = typename allocator_traits<_Alloc>::size_type; using _Mybase::set_emit_on_sync; @@ -117,14 +117,14 @@ public: } bool _Result = true; - const size_type _Data_size = _Get_data_size(); + const _Size_type _Data_size = _Get_data_size(); _Elem* const _Begin_seq_ptr = streambuf_type::pbase(); if (_Data_size > 0 || _Mybase::_Sync_recorded) { scoped_lock _Guard(*_Get_mutex_for_instance(_Wrapped)); if (_Data_size > 0 && _Data_size - != static_cast( + != static_cast<_Size_type>( _Wrapped->sputn(_Begin_seq_ptr, static_cast(_Data_size)))) { _Result = false; } @@ -169,15 +169,15 @@ protected: return _Traits::not_eof(_Current_elem); } - const size_type _Buf_size = _Get_buffer_size(); - const size_type _Max_allocation = allocator_traits<_Alloc>::max_size(_Al); + const _Size_type _Buf_size = _Get_buffer_size(); + const _Size_type _Max_allocation = allocator_traits<_Alloc>::max_size(_Al); if (_Buf_size == _Max_allocation) { return _Traits::eof(); } - const size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); - const _Elem* const _Old_ptr = streambuf_type::pbase(); - const size_type _Old_data_size = _Get_data_size(); + const _Size_type _New_capacity = _Calculate_growth(_Buf_size, _Buf_size + 1, _Max_allocation); + const _Elem* const _Old_ptr = streambuf_type::pbase(); + const _Size_type _Old_data_size = _Get_data_size(); _Elem* const _New_ptr = _Unfancy(_Al.allocate(_New_capacity)); _Traits::copy(_New_ptr, _Old_ptr, _Old_data_size); @@ -189,7 +189,7 @@ protected: } private: - static constexpr size_type _Min_size = 32; // constant for minimum buffer size + static constexpr _Size_type _Min_size = 32; // constant for minimum buffer size void _Init() { _Elem* const _New_ptr = _Unfancy(_Al.allocate(_Min_size)); @@ -197,9 +197,9 @@ private: } void _Tidy() noexcept { - const size_type _Buf_size = _Get_buffer_size(); + const _Size_type _Buf_size = _Get_buffer_size(); if (0 < _Buf_size) { - _Al.deallocate(pointer_traits::pointer_to(*streambuf_type::pbase()), _Buf_size); + _Al.deallocate(pointer_traits<_Pointer>::pointer_to(*streambuf_type::pbase()), _Buf_size); } streambuf_type::setp(nullptr, nullptr, nullptr); @@ -227,8 +227,8 @@ private: } else { _Tidy(); - const size_type _Right_buf_size = _Right._Get_buffer_size(); - const size_type _Right_data_size = _Right._Get_data_size(); + const _Size_type _Right_buf_size = _Right._Get_buffer_size(); + const _Size_type _Right_data_size = _Right._Get_data_size(); _Elem* const _New_ptr = _Unfancy(_Al.allocate(_Right_buf_size)); _Traits::copy(_New_ptr, _Right.pbase(), _Right_data_size); @@ -245,7 +245,7 @@ private: } void _Swap_except_al(basic_syncbuf& _Right) noexcept { - _Mybase::swap(_Right); + _Mybase::_Swap(_Right); _STD swap(_Wrapped, _Right._Wrapped); } @@ -261,13 +261,13 @@ private: _CATCH_END } - _NODISCARD constexpr size_type _Calculate_growth( - const size_type _Oldsize, const size_type _Newsize, const size_type _Maxsize) const { + _NODISCARD constexpr _Size_type _Calculate_growth( + const _Size_type _Oldsize, const _Size_type _Newsize, const _Size_type _Maxsize) const { if (_Oldsize > _Maxsize - _Oldsize / 2) { return _Maxsize; // geometric growth would overflow } - const size_type _Geometric = _Oldsize + _Oldsize / 2; + const _Size_type _Geometric = _Oldsize + _Oldsize / 2; if (_Geometric < _Newsize) { return _Newsize; // geometric growth would be insufficient @@ -276,12 +276,12 @@ private: return _Geometric; // geometric growth is sufficient } - _NODISCARD constexpr size_type _Get_data_size() const noexcept { - return static_cast(streambuf_type::pptr() - streambuf_type::pbase()); + _NODISCARD constexpr _Size_type _Get_data_size() const noexcept { + return static_cast<_Size_type>(streambuf_type::pptr() - streambuf_type::pbase()); } - _NODISCARD constexpr size_type _Get_buffer_size() const noexcept { - return static_cast(streambuf_type::epptr() - streambuf_type::pbase()); + _NODISCARD constexpr _Size_type _Get_buffer_size() const noexcept { + return static_cast<_Size_type>(streambuf_type::epptr() - streambuf_type::pbase()); } streambuf_type* _Wrapped{nullptr}; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 9fa4ad33e88..2e96f909995 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -133,7 +133,7 @@ // _HAS_CXX20 directly controls: // P0019R8 atomic_ref // P0020R6 atomic, atomic, atomic -// P0053R7 C++ Synchronized Buffered Ostream +// P0053R7 // P0122R7 // P0202R3 constexpr For And exchange() // P0318R1 unwrap_reference, unwrap_ref_decay @@ -165,7 +165,7 @@ // P0660R10 And jthread // P0674R1 make_shared() For Arrays // P0718R2 atomic>, atomic> -// P0753R2 Manipulators for C++ Synchronized Buffered Ostream +// P0753R2 osyncstream Manipulators // P0758R1 is_nothrow_convertible // P0768R1 Library Support For The Spaceship Comparison Operator <=> // P0769R2 shift_left(), shift_right() diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 89467c6b005..5250df46f47 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -48,17 +48,17 @@ class _Crt_allocator { static map, _Crt_allocator>> _Mutex_map; static shared_mutex _Mutex; -extern "C" _CRTIMP2 shared_mutex* _Get_mutex_for_instance(void* _Ptr) noexcept { +extern "C" _NODISCARD _CRTIMP2 shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept { shared_lock _Guard(_Mutex); auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); return _STD addressof(_Instance_mutex_iter->second._Mutex); } -extern "C" _CRTIMP2 void _Acquire_mutex_for_instance(void* _Ptr) { +extern "C" _CRTIMP2 void __stdcall _Acquire_mutex_for_instance(void* _Ptr) { scoped_lock _Guard(_Mutex); _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; } -extern "C" _CRTIMP2 void _Release_mutex_for_instance(void* _Ptr) noexcept { +extern "C" _CRTIMP2 void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept { scoped_lock _Guard(_Mutex); auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index f1c4b1d11f9..b9b8190964a 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -806,7 +806,7 @@ int main() { puts("Testing ."); syncbuf sync_buf{nullptr}; assert(sync_buf.get_wrapped() == nullptr); - assert(sync_buf.get_allocator() == std::allocator{}); + assert(sync_buf.get_allocator() == allocator{}); assert(sync_buf.emit() == false); osyncstream sync_str{cout}; sync_str << "Testing P1502R1_standard_library_header_units.\n"; diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 55b1b36db53..b01305e9dc9 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1332,20 +1332,6 @@ STATIC_ASSERT(__cpp_lib_starts_ends_with == 201711L); #endif #endif -#if _HAS_CXX20 -#ifndef __cpp_lib_syncbuf -#error __cpp_lib_syncbuf is not defined -#elif __cpp_lib_syncbuf != 201803L -#error __cpp_lib_syncbuf is not 201803L -#else -STATIC_ASSERT(__cpp_lib_syncbuf == 201803L); -#endif -#else -#ifdef __cpp_lib_syncbuf -#error __cpp_lib_syncbuf is defined -#endif -#endif - #ifndef __cpp_lib_string_udls #error __cpp_lib_string_udls is not defined #elif __cpp_lib_string_udls != 201304L @@ -1368,6 +1354,20 @@ STATIC_ASSERT(__cpp_lib_string_view == 201803L); #endif #endif +#if _HAS_CXX20 +#ifndef __cpp_lib_syncbuf +#error __cpp_lib_syncbuf is not defined +#elif __cpp_lib_syncbuf != 201803L +#error __cpp_lib_syncbuf is not 201803L +#else +STATIC_ASSERT(__cpp_lib_syncbuf == 201803L); +#endif +#else +#ifdef __cpp_lib_syncbuf +#error __cpp_lib_syncbuf is defined +#endif +#endif + #if _HAS_CXX20 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_three_way_comparison #error __cpp_lib_three_way_comparison is not defined From 430944c96cc91a354d5ae3cdfb5eaba3d88876c5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 4 Feb 2021 21:22:06 -0800 Subject: [PATCH 37/60] Code review feedback. --- stl/inc/syncstream | 22 +++++++++++----------- stl/src/syncstream.cpp | 20 +++++++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index f953e338457..9b8d882612f 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -149,7 +149,7 @@ public: } protected: - int sync() override { + virtual int sync() override { _Mybase::_Sync_recorded = true; if (_Mybase::_Emit_on_sync) { @@ -160,8 +160,8 @@ protected: return 0; } - int_type overflow(int_type _Current_elem) override { - if (_Wrapped == nullptr) { + virtual int_type overflow(int_type _Current_elem) override { + if (!_Wrapped) { return _Traits::eof(); } const bool _Chk_eof = _Traits::eq_int_type(_Current_elem, _Traits::eof()); @@ -199,11 +199,11 @@ private: void _Tidy() noexcept { const _Size_type _Buf_size = _Get_buffer_size(); if (0 < _Buf_size) { - _Al.deallocate(pointer_traits<_Pointer>::pointer_to(*streambuf_type::pbase()), _Buf_size); + _Al.deallocate(_Refancy<_Pointer>(streambuf_type::pbase()), _Buf_size); } streambuf_type::setp(nullptr, nullptr, nullptr); - if (_Wrapped != nullptr) { + if (_Wrapped) { _Release_mutex_for_instance(_Wrapped); _Wrapped = nullptr; } @@ -249,7 +249,7 @@ private: _STD swap(_Wrapped, _Right._Wrapped); } - bool _Do_emit() override { + virtual bool _Do_emit() override { return emit(); } @@ -261,8 +261,8 @@ private: _CATCH_END } - _NODISCARD constexpr _Size_type _Calculate_growth( - const _Size_type _Oldsize, const _Size_type _Newsize, const _Size_type _Maxsize) const { + _NODISCARD static constexpr _Size_type _Calculate_growth( + const _Size_type _Oldsize, const _Size_type _Newsize, const _Size_type _Maxsize) { if (_Oldsize > _Maxsize - _Oldsize / 2) { return _Maxsize; // geometric growth would overflow } @@ -276,11 +276,11 @@ private: return _Geometric; // geometric growth is sufficient } - _NODISCARD constexpr _Size_type _Get_data_size() const noexcept { + _NODISCARD _Size_type _Get_data_size() const noexcept { return static_cast<_Size_type>(streambuf_type::pptr() - streambuf_type::pbase()); } - _NODISCARD constexpr _Size_type _Get_buffer_size() const noexcept { + _NODISCARD _Size_type _Get_buffer_size() const noexcept { return static_cast<_Size_type>(streambuf_type::epptr() - streambuf_type::pbase()); } @@ -289,7 +289,7 @@ private: }; template -void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, basic_syncbuf<_Elem, _Traits, _Alloc>& _Right) { +void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, basic_syncbuf<_Elem, _Traits, _Alloc>& _Right) noexcept { _Left.swap(_Right); } diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 5250df46f47..b3b5fe95196 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -21,8 +21,6 @@ template class _Crt_allocator { public: using value_type = _Ty; - using size_type = size_t; - using difference_type = ptrdiff_t; using propagate_on_container_move_assignment = true_type; using is_always_equal = true_type; @@ -33,14 +31,14 @@ class _Crt_allocator { constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); + const auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); if (!_Ptr) { throw bad_alloc{}; } return static_cast<_Ty*>(_Ptr); } - void deallocate(_Ty* const _Ptr, const size_t) noexcept { + void deallocate(_Ty* const _Ptr, size_t) noexcept { _free_crt(_Ptr); } }; @@ -52,18 +50,22 @@ extern "C" _NODISCARD _CRTIMP2 shared_mutex* __stdcall _Get_mutex_for_instance(v shared_lock _Guard(_Mutex); auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); - return _STD addressof(_Instance_mutex_iter->second._Mutex); + return &_Instance_mutex_iter->second._Mutex; } + extern "C" _CRTIMP2 void __stdcall _Acquire_mutex_for_instance(void* _Ptr) { scoped_lock _Guard(_Mutex); - _Mutex_map.try_emplace(_Ptr).first->second._Ref_count++; + auto& _Refs = _Mutex_map.try_emplace(_Ptr).first->second._Ref_count; + ++_Refs; } + extern "C" _CRTIMP2 void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept { scoped_lock _Guard(_Mutex); - auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); + const auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); - if (--_Instance_mutex_iter->second._Ref_count == 0) { - _Mutex_map.erase(_Ptr); + auto& _Refs = _Instance_mutex_iter->second._Ref_count; + if (--_Refs == 0) { + _Mutex_map.erase(_Instance_mutex_iter); } } From c2e37053815a2a546680be81e5d5502ad335c1d1 Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Fri, 5 Feb 2021 19:49:09 +0000 Subject: [PATCH 38/60] change flush_emit to follow the standard wording - Adjust the tests accordingly --- stl/inc/ostream | 4 +--- .../test.cpp | 6 +----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/stl/inc/ostream b/stl/inc/ostream index 75de90733fc..61766bf311e 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -1018,9 +1018,7 @@ basic_ostream<_Elem, _Traits>& flush_emit(basic_ostream<_Elem, _Traits>& _Ostr) _Ostr.flush(); const auto _Sync_buf_ptr = dynamic_cast<_Basic_syncbuf_impl<_Elem, _Traits>*>(_Ostr.rdbuf()); if (_Sync_buf_ptr) { - if (!_Sync_buf_ptr->_Do_emit()) { - _Ostr.setstate(ios::badbit); - } + _Sync_buf_ptr->_Do_emit(); } return _Ostr; } diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp index d47e7b550ba..d352344438c 100644 --- a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp @@ -79,11 +79,7 @@ requires(is_base_of_v, Ty>) { - if constexpr (ThrowOnSync) { - assert(os.rdstate() == ios::badbit); - } else { - assert(os.rdstate() == (buf ? ios::goodbit : ios::badbit)); - } + assert(os.rdstate() == ios::goodbit); if (buf) { assert(buf->str == "Another input"); } From 8101d4b3e3ee8be14025701038b37f73a0f85586 Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Fri, 5 Feb 2021 20:23:48 +0000 Subject: [PATCH 39/60] Workaround EHsc compiler flag with throwing extern C --- stl/inc/syncstream | 11 +++++++++-- stl/src/syncstream.cpp | 13 +++++++++---- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 9b8d882612f..c868c5dfe00 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -28,9 +28,16 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN extern "C" _NODISCARD shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept; -extern "C" void __stdcall _Acquire_mutex_for_instance(void* _Ptr); +extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance_impl(void* _Ptr) noexcept; extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept; +template < class _Enabler> +inline void _Acquire_mutex_for_instance(void* _Ptr) { + if (!_Acquire_mutex_for_instance_impl(_Ptr)) { + throw std::bad_alloc{}; + } +} + // CLASS TEMPLATE _Basic_syncbuf_impl template class _Basic_syncbuf_impl : public basic_streambuf<_Elem, _Traits> { @@ -82,7 +89,7 @@ public: basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { if (_Wrapped) { - _Acquire_mutex_for_instance(_Wrapped); + _Acquire_mutex_for_instance(_Wrapped); } _Init(); } diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index b3b5fe95196..429a539eaa5 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -53,10 +53,15 @@ extern "C" _NODISCARD _CRTIMP2 shared_mutex* __stdcall _Get_mutex_for_instance(v return &_Instance_mutex_iter->second._Mutex; } -extern "C" _CRTIMP2 void __stdcall _Acquire_mutex_for_instance(void* _Ptr) { - scoped_lock _Guard(_Mutex); - auto& _Refs = _Mutex_map.try_emplace(_Ptr).first->second._Ref_count; - ++_Refs; +extern "C" _NODISCARD _CRTIMP2 bool __stdcall _Acquire_mutex_for_instance_impl(void* _Ptr) noexcept { + try { + scoped_lock _Guard(_Mutex); + auto& _Refs = _Mutex_map.try_emplace(_Ptr).first->second._Ref_count; + ++_Refs; + return true; + } catch (...) { + return false; + } } extern "C" _CRTIMP2 void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept { From 5b13b442808817fe6eb5ecac95543bd5d527e5e0 Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Fri, 5 Feb 2021 20:28:52 +0000 Subject: [PATCH 40/60] clang-format --- stl/inc/syncstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index c868c5dfe00..0c1bdfa761c 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -31,7 +31,7 @@ extern "C" _NODISCARD shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance_impl(void* _Ptr) noexcept; extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept; -template < class _Enabler> +template inline void _Acquire_mutex_for_instance(void* _Ptr) { if (!_Acquire_mutex_for_instance_impl(_Ptr)) { throw std::bad_alloc{}; From bc1360867777177e41b882caaed2e2c8ad9d3871 Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Fri, 5 Feb 2021 22:07:30 +0000 Subject: [PATCH 41/60] Inline _Acquire_mutex_for_instance Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- stl/inc/syncstream | 13 ++++--------- stl/src/syncstream.cpp | 2 +- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 0c1bdfa761c..c11d03a822d 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -28,16 +28,9 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN extern "C" _NODISCARD shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept; -extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance_impl(void* _Ptr) noexcept; +extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noexcept; extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept; -template -inline void _Acquire_mutex_for_instance(void* _Ptr) { - if (!_Acquire_mutex_for_instance_impl(_Ptr)) { - throw std::bad_alloc{}; - } -} - // CLASS TEMPLATE _Basic_syncbuf_impl template class _Basic_syncbuf_impl : public basic_streambuf<_Elem, _Traits> { @@ -89,7 +82,9 @@ public: basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { if (_Wrapped) { - _Acquire_mutex_for_instance(_Wrapped); + if (!_Acquire_mutex_for_instance(_Wrapped)) { + throw std::bad_alloc{}; + } } _Init(); } diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 429a539eaa5..bfd1ff14d51 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -53,7 +53,7 @@ extern "C" _NODISCARD _CRTIMP2 shared_mutex* __stdcall _Get_mutex_for_instance(v return &_Instance_mutex_iter->second._Mutex; } -extern "C" _NODISCARD _CRTIMP2 bool __stdcall _Acquire_mutex_for_instance_impl(void* _Ptr) noexcept { +extern "C" _NODISCARD _CRTIMP2 bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noexcept { try { scoped_lock _Guard(_Mutex); auto& _Refs = _Mutex_map.try_emplace(_Ptr).first->second._Ref_count; From 19a747c1bee177fa927e053911bf72f9b0729b6f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 5 Feb 2021 19:56:38 -0800 Subject: [PATCH 42/60] Disable header units test coverage. --- tests/std/tests/P1502R1_standard_library_header_units/test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index e0a961e6bd0..fa593c11b05 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -810,6 +810,7 @@ int main() { { puts("Testing ."); +#if 0 // TRANSITION, VSO-1275701 (runtime crash with dynamic atexit destructor for syncstreams) syncbuf sync_buf{nullptr}; assert(sync_buf.get_wrapped() == nullptr); assert(sync_buf.get_allocator() == allocator{}); @@ -817,6 +818,7 @@ int main() { osyncstream sync_str{cout}; sync_str << "Testing P1502R1_standard_library_header_units.\n"; assert(sync_str.rdbuf()->get_wrapped() == cout.rdbuf()); +#endif // ^^^ no workaround ^^^ } { From e7fc1c94f921157b74fbb841f9bcc2194cd11504 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 5 Feb 2021 20:26:53 -0800 Subject: [PATCH 43/60] Use _Xbad_alloc() to throw. --- stl/inc/syncstream | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index c11d03a822d..560fb076374 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -83,7 +83,7 @@ public: basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { if (_Wrapped) { if (!_Acquire_mutex_for_instance(_Wrapped)) { - throw std::bad_alloc{}; + _Xbad_alloc(); } } _Init(); From 7f703101adb7a9e10541154b68f9cca90fb1e9e5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 5 Feb 2021 20:32:39 -0800 Subject: [PATCH 44/60] Don't use _CRTIMP2 for import-lib functions. (Also applies to the satellite DLL which uses msvcp_atomic_wait.src.) --- stl/src/syncstream.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index bfd1ff14d51..7b628beff2c 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -46,14 +46,14 @@ class _Crt_allocator { static map, _Crt_allocator>> _Mutex_map; static shared_mutex _Mutex; -extern "C" _NODISCARD _CRTIMP2 shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept { +extern "C" _NODISCARD shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept { shared_lock _Guard(_Mutex); auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); return &_Instance_mutex_iter->second._Mutex; } -extern "C" _NODISCARD _CRTIMP2 bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noexcept { +extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noexcept { try { scoped_lock _Guard(_Mutex); auto& _Refs = _Mutex_map.try_emplace(_Ptr).first->second._Ref_count; @@ -64,7 +64,7 @@ extern "C" _NODISCARD _CRTIMP2 bool __stdcall _Acquire_mutex_for_instance(void* } } -extern "C" _CRTIMP2 void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept { +extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept { scoped_lock _Guard(_Mutex); const auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); From 019e25fc60611b7cdfa0dbbe5827dbb10573546b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 6 Feb 2021 00:37:07 -0800 Subject: [PATCH 45/60] Rename _Lookup_map and _Lookup_mutex. --- stl/src/syncstream.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 7b628beff2c..5fe4a8ec912 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -43,20 +43,23 @@ class _Crt_allocator { } }; -static map, _Crt_allocator>> _Mutex_map; -static shared_mutex _Mutex; +using _Map_alloc = _Crt_allocator>; +using _Map_type = map, _Map_alloc>; + +static _Map_type _Lookup_map; +static shared_mutex _Lookup_mutex; extern "C" _NODISCARD shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept { - shared_lock _Guard(_Mutex); - auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); - _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); + shared_lock _Guard(_Lookup_mutex); + auto _Instance_mutex_iter = _Lookup_map.find(_Ptr); + _ASSERT_EXPR(_Instance_mutex_iter != _Lookup_map.end(), "No mutex exists for given instance!"); return &_Instance_mutex_iter->second._Mutex; } extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noexcept { try { - scoped_lock _Guard(_Mutex); - auto& _Refs = _Mutex_map.try_emplace(_Ptr).first->second._Ref_count; + scoped_lock _Guard(_Lookup_mutex); + auto& _Refs = _Lookup_map.try_emplace(_Ptr).first->second._Ref_count; ++_Refs; return true; } catch (...) { @@ -65,12 +68,12 @@ extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noe } extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept { - scoped_lock _Guard(_Mutex); - const auto _Instance_mutex_iter = _Mutex_map.find(_Ptr); - _ASSERT_EXPR(_Instance_mutex_iter != _Mutex_map.end(), "No mutex exists for given instance!"); + scoped_lock _Guard(_Lookup_mutex); + const auto _Instance_mutex_iter = _Lookup_map.find(_Ptr); + _ASSERT_EXPR(_Instance_mutex_iter != _Lookup_map.end(), "No mutex exists for given instance!"); auto& _Refs = _Instance_mutex_iter->second._Ref_count; if (--_Refs == 0) { - _Mutex_map.erase(_Instance_mutex_iter); + _Lookup_map.erase(_Instance_mutex_iter); } } From 37fab1efb487aaa63f4d4ab98d3f446432bec52a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 6 Feb 2021 00:43:11 -0800 Subject: [PATCH 46/60] Use size_t for refcounts instead of uint64_t. --- stl/src/syncstream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 5fe4a8ec912..9888587d8df 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -14,7 +14,7 @@ _STD_BEGIN // OBJECT DECLARATIONS struct _Mutex_count_pair { shared_mutex _Mutex; - uint64_t _Ref_count = 0; + size_t _Ref_count = 0; }; template From 9af659807fabe2784b590a6c443160f10e02e7d7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Fri, 5 Feb 2021 21:47:55 -0800 Subject: [PATCH 47/60] Move code into the atomic_wait satellite DLL. --- stl/CMakeLists.txt | 4 +- stl/inc/syncstream | 16 +++--- stl/src/msvcp_atomic_wait.src | 3 ++ stl/src/syncstream.cpp | 94 +++++++++++++++++++---------------- 4 files changed, 66 insertions(+), 51 deletions(-) diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index ba00e5db432..298f1439d03 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -249,7 +249,6 @@ set(IMPLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/locale0_implib.cpp ${CMAKE_CURRENT_LIST_DIR}/src/nothrow.cpp ${CMAKE_CURRENT_LIST_DIR}/src/sharedmutex.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/syncstream.cpp ${CMAKE_CURRENT_LIST_DIR}/src/syserror_import_lib.cpp ${CMAKE_CURRENT_LIST_DIR}/src/vector_algorithms.cpp ${CMAKE_CURRENT_LIST_DIR}/src/xonce2.cpp @@ -398,6 +397,7 @@ set(SOURCES_SATELLITE_2 set(SOURCES_SATELLITE_ATOMIC_WAIT ${CMAKE_CURRENT_LIST_DIR}/src/atomic_wait.cpp ${CMAKE_CURRENT_LIST_DIR}/src/parallel_algorithms.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/syncstream.cpp ) set(SOURCES_SATELLITE_CODECVT_IDS @@ -497,7 +497,7 @@ function(add_stl_dlls D_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIO file(WRITE "${_ATOMIC_WAIT_DEF_NAME}" "${_ATOMIC_WAIT_DEF_CONTENTS}") add_library(msvcp${D_SUFFIX}_atomic_wait SHARED "${_ATOMIC_WAIT_DEF_NAME}") - target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib") + target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects msvcp${D_SUFFIX}_implib_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_NAME "msvcp140_atomic_wait${D_SUFFIX}${VCLIBS_SUFFIX}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES OUTPUT_NAME "${_ATOMIC_WAIT_OUTPUT_NAME}") diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 560fb076374..13cda8deeed 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -25,11 +25,13 @@ _STL_DISABLE_CLANG_WARNINGS #undef new #undef emit -_STD_BEGIN +_EXTERN_C +_NODISCARD void* __stdcall __std_get_shared_mutex_for_instance(void* _Ptr) noexcept; +_NODISCARD bool __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept; +void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept; +_END_EXTERN_C -extern "C" _NODISCARD shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept; -extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noexcept; -extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept; +_STD_BEGIN // CLASS TEMPLATE _Basic_syncbuf_impl template @@ -82,7 +84,7 @@ public: basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { if (_Wrapped) { - if (!_Acquire_mutex_for_instance(_Wrapped)) { + if (!__std_acquire_shared_mutex_for_instance(_Wrapped)) { _Xbad_alloc(); } } @@ -122,7 +124,7 @@ public: const _Size_type _Data_size = _Get_data_size(); _Elem* const _Begin_seq_ptr = streambuf_type::pbase(); if (_Data_size > 0 || _Mybase::_Sync_recorded) { - scoped_lock _Guard(*_Get_mutex_for_instance(_Wrapped)); + scoped_lock _Guard(*static_cast(__std_get_shared_mutex_for_instance(_Wrapped))); if (_Data_size > 0 && _Data_size @@ -206,7 +208,7 @@ private: streambuf_type::setp(nullptr, nullptr, nullptr); if (_Wrapped) { - _Release_mutex_for_instance(_Wrapped); + __std_release_shared_mutex_for_instance(_Wrapped); _Wrapped = nullptr; } } diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index d8f2d843b5a..3a3e10653a1 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -6,6 +6,7 @@ LIBRARY LIBRARYNAME EXPORTS + __std_acquire_shared_mutex_for_instance __std_atomic_compare_exchange_128 __std_atomic_get_mutex __std_atomic_has_cmpxchg16b @@ -23,6 +24,8 @@ EXPORTS __std_create_threadpool_work __std_execution_wait_on_uchar __std_execution_wake_by_address_all + __std_get_shared_mutex_for_instance __std_parallel_algorithms_hw_threads + __std_release_shared_mutex_for_instance __std_submit_threadpool_work __std_wait_for_threadpool_work_callbacks diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 9888587d8df..470a546b8fd 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -3,62 +3,72 @@ // initialize syncstream mutex map +#include #include #include +#include +#include #include +#include +#include #pragma warning(disable : 4074) #pragma init_seg(compiler) -_STD_BEGIN -// OBJECT DECLARATIONS -struct _Mutex_count_pair { - shared_mutex _Mutex; - size_t _Ref_count = 0; -}; - -template -class _Crt_allocator { -public: - using value_type = _Ty; - using propagate_on_container_move_assignment = true_type; - using is_always_equal = true_type; - - constexpr _Crt_allocator() noexcept = default; - - constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; - template - constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} - - _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - const auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); - if (!_Ptr) { - throw bad_alloc{}; +namespace { + // OBJECT DECLARATIONS + struct _Mutex_count_pair { + _STD shared_mutex _Mutex; + size_t _Ref_count = 0; + }; + + template + class _Crt_allocator { + public: + using value_type = _Ty; + using propagate_on_container_move_assignment = _STD true_type; + using is_always_equal = _STD true_type; + + constexpr _Crt_allocator() noexcept = default; + + constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; + template + constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} + + _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + const auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); + if (!_Ptr) { + throw _STD bad_alloc{}; + } + return static_cast<_Ty*>(_Ptr); } - return static_cast<_Ty*>(_Ptr); - } - void deallocate(_Ty* const _Ptr, size_t) noexcept { - _free_crt(_Ptr); - } -}; + void deallocate(_Ty* const _Ptr, size_t) noexcept { + _free_crt(_Ptr); + } + }; + + using _Map_alloc = _Crt_allocator<_STD pair>; + using _Map_type = _STD map, _Map_alloc>; -using _Map_alloc = _Crt_allocator>; -using _Map_type = map, _Map_alloc>; + _Map_type _Lookup_map; + _STD shared_mutex _Lookup_mutex; +} // unnamed namespace -static _Map_type _Lookup_map; -static shared_mutex _Lookup_mutex; +_EXTERN_C -extern "C" _NODISCARD shared_mutex* __stdcall _Get_mutex_for_instance(void* _Ptr) noexcept { - shared_lock _Guard(_Lookup_mutex); +_NODISCARD void* __stdcall __std_get_shared_mutex_for_instance(void* _Ptr) noexcept { + _STD shared_lock _Guard(_Lookup_mutex); auto _Instance_mutex_iter = _Lookup_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Lookup_map.end(), "No mutex exists for given instance!"); - return &_Instance_mutex_iter->second._Mutex; + auto _Ret = &_Instance_mutex_iter->second._Mutex; + static_assert(_STD is_same_v); + return _Ret; // caller will restore type } -extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noexcept { +_NODISCARD bool __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept { try { - scoped_lock _Guard(_Lookup_mutex); + _STD scoped_lock _Guard(_Lookup_mutex); auto& _Refs = _Lookup_map.try_emplace(_Ptr).first->second._Ref_count; ++_Refs; return true; @@ -67,8 +77,8 @@ extern "C" _NODISCARD bool __stdcall _Acquire_mutex_for_instance(void* _Ptr) noe } } -extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept { - scoped_lock _Guard(_Lookup_mutex); +void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept { + _STD scoped_lock _Guard(_Lookup_mutex); const auto _Instance_mutex_iter = _Lookup_map.find(_Ptr); _ASSERT_EXPR(_Instance_mutex_iter != _Lookup_map.end(), "No mutex exists for given instance!"); auto& _Refs = _Instance_mutex_iter->second._Ref_count; @@ -77,4 +87,4 @@ extern "C" void __stdcall _Release_mutex_for_instance(void* _Ptr) noexcept { } } -_STD_END +_END_EXTERN_C From 8b89ab0d741ec01e367a8279ca2ce03a6954f340 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 6 Feb 2021 01:29:04 -0800 Subject: [PATCH 48/60] Increase efficiency by caching the mutex pointer. --- stl/inc/syncstream | 12 ++++++++---- stl/src/msvcp_atomic_wait.src | 1 - stl/src/syncstream.cpp | 18 +++++------------- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 13cda8deeed..ac2ec596b04 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -26,8 +26,7 @@ _STL_DISABLE_CLANG_WARNINGS #undef emit _EXTERN_C -_NODISCARD void* __stdcall __std_get_shared_mutex_for_instance(void* _Ptr) noexcept; -_NODISCARD bool __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept; +_NODISCARD void* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept; void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept; _END_EXTERN_C @@ -84,7 +83,8 @@ public: basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { if (_Wrapped) { - if (!__std_acquire_shared_mutex_for_instance(_Wrapped)) { + _Mutex = static_cast(__std_acquire_shared_mutex_for_instance(_Wrapped)); + if (!_Mutex) { _Xbad_alloc(); } } @@ -124,7 +124,7 @@ public: const _Size_type _Data_size = _Get_data_size(); _Elem* const _Begin_seq_ptr = streambuf_type::pbase(); if (_Data_size > 0 || _Mybase::_Sync_recorded) { - scoped_lock _Guard(*static_cast(__std_get_shared_mutex_for_instance(_Wrapped))); + scoped_lock _Guard(*_Mutex); if (_Data_size > 0 && _Data_size @@ -210,6 +210,7 @@ private: if (_Wrapped) { __std_release_shared_mutex_for_instance(_Wrapped); _Wrapped = nullptr; + _Mutex = nullptr; } } @@ -243,6 +244,7 @@ private: _STD swap(_Mybase::_Emit_on_sync, _Right._Emit_on_sync); _STD swap(_Mybase::_Sync_recorded, _Right._Sync_recorded); _STD swap(_Wrapped, _Right._Wrapped); + _STD swap(_Mutex, _Right._Mutex); _Right._Tidy(); } @@ -251,6 +253,7 @@ private: void _Swap_except_al(basic_syncbuf& _Right) noexcept { _Mybase::_Swap(_Right); _STD swap(_Wrapped, _Right._Wrapped); + _STD swap(_Mutex, _Right._Mutex); } virtual bool _Do_emit() override { @@ -289,6 +292,7 @@ private: } streambuf_type* _Wrapped{nullptr}; + shared_mutex* _Mutex{nullptr}; allocator_type _Al{}; }; diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index 3a3e10653a1..240916023ea 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -24,7 +24,6 @@ EXPORTS __std_create_threadpool_work __std_execution_wait_on_uchar __std_execution_wake_by_address_all - __std_get_shared_mutex_for_instance __std_parallel_algorithms_hw_threads __std_release_shared_mutex_for_instance __std_submit_threadpool_work diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 470a546b8fd..b9ed5fc73ad 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -57,23 +57,15 @@ namespace { _EXTERN_C -_NODISCARD void* __stdcall __std_get_shared_mutex_for_instance(void* _Ptr) noexcept { - _STD shared_lock _Guard(_Lookup_mutex); - auto _Instance_mutex_iter = _Lookup_map.find(_Ptr); - _ASSERT_EXPR(_Instance_mutex_iter != _Lookup_map.end(), "No mutex exists for given instance!"); - auto _Ret = &_Instance_mutex_iter->second._Mutex; - static_assert(_STD is_same_v); - return _Ret; // caller will restore type -} - -_NODISCARD bool __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept { +_NODISCARD void* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept { try { _STD scoped_lock _Guard(_Lookup_mutex); - auto& _Refs = _Lookup_map.try_emplace(_Ptr).first->second._Ref_count; + auto& [_Mutex, _Refs] = _Lookup_map.try_emplace(_Ptr).first->second; ++_Refs; - return true; + static_assert(_STD is_same_v); + return &_Mutex; // caller will restore type } catch (...) { - return false; + return nullptr; } } From b42dfe95f3947387aac1424609cb64d6580adcaa Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Sun, 7 Feb 2021 01:44:10 +0000 Subject: [PATCH 49/60] Refactor duplicate test code --- .../test.hpp | 69 ++++++------------- 1 file changed, 20 insertions(+), 49 deletions(-) diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index e30d119d26b..b79295f000a 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -41,6 +41,21 @@ class small_size_allocation { } }; +template +class allocator_base { +public: + using value_type = Ty; + using pointer = Ty*; + + _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + return static_cast(allocator{}.allocate(_Count)); + } + + void deallocate(pointer const _Ptr, const size_t _Count) noexcept { + allocator{}.deallocate(_Ptr, _Count); + } +}; + template class fancy_ptr { private: @@ -67,8 +82,8 @@ template class fancy_ptr_allocator { public: using value_type = Ty; - using size_type = size_t; using pointer = fancy_ptr; + using size_type = size_t; using propagate_on_container_move_assignment = true_type; using propagate_on_container_swap = true_type; @@ -116,22 +131,12 @@ _NODISCARD bool operator==( } template -class small_size_allocator : public small_size_allocation { +class small_size_allocator : public allocator_base, public small_size_allocation { public: - using value_type = Ty; - using pointer = Ty*; using propagate_on_container_move_assignment = true_type; using propagate_on_container_swap = true_type; constexpr small_size_allocator() noexcept = default; - - _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(allocator{}.allocate(_Count)); - } - - void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - allocator{}.deallocate(_Ptr, _Count); - } }; template @@ -140,28 +145,14 @@ _NODISCARD bool operator==(const small_size_allocator&, const small_size_all } template -class non_move_assignable_non_equal_allocator { +class non_move_assignable_non_equal_allocator : public allocator_base { public: - using value_type = Ty; using size_type = size_t; - using pointer = Ty*; using propagate_on_container_move_assignment = false_type; using propagate_on_container_swap = true_type; using is_always_equal = false_type; constexpr non_move_assignable_non_equal_allocator() noexcept = default; - - _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(allocator{}.allocate(_Count)); - } - - void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - allocator{}.deallocate(_Ptr, _Count); - } - - _NODISCARD size_type max_size() const noexcept { - return 50; - } }; template @@ -171,23 +162,13 @@ _NODISCARD bool operator==(const non_move_assignable_non_equal_allocator&, } template -class non_move_assignable_equal_allocator { +class non_move_assignable_equal_allocator : public allocator_base { public: - using value_type = Ty; using size_type = size_t; - using pointer = Ty*; using propagate_on_container_move_assignment = false_type; using propagate_on_container_swap = true_type; constexpr non_move_assignable_equal_allocator() noexcept = default; - - _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(allocator{}.allocate(_Count)); - } - - void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - allocator{}.deallocate(_Ptr, _Count); - } }; template @@ -197,23 +178,13 @@ _NODISCARD bool operator==( } template -class non_swapable_equal_allocator { +class non_swapable_equal_allocator : public allocator_base { public: - using value_type = Ty; using size_type = size_t; - using pointer = Ty*; using propagate_on_container_move_assignment = true_type; using propagate_on_container_swap = false_type; constexpr non_swapable_equal_allocator() noexcept = default; - - _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(allocator{}.allocate(_Count)); - } - - void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - allocator{}.deallocate(_Ptr, _Count); - } }; template From e7ed8758d3ec72eb87408554c4fb6928441286d8 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 6 Feb 2021 18:32:22 -0800 Subject: [PATCH 50/60] Use _Compressed_pair. --- stl/inc/syncstream | 45 ++++++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index ac2ec596b04..2ed144a3bc6 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -81,9 +81,11 @@ public: explicit basic_syncbuf(streambuf_type* _Strbuf) : basic_syncbuf(_Strbuf, _Alloc{}) {} - basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) : _Wrapped(_Strbuf), _Al(_Al_) { + basic_syncbuf(streambuf_type* _Strbuf, const _Alloc& _Al_) + : _Wrapped(_Strbuf), _Mypair{_One_then_variadic_args_t{}, _Al_, nullptr} { if (_Wrapped) { - _Mutex = static_cast(__std_acquire_shared_mutex_for_instance(_Wrapped)); + auto& _Mutex = _Get_mutex(); + _Mutex = static_cast(__std_acquire_shared_mutex_for_instance(_Wrapped)); if (!_Mutex) { _Xbad_alloc(); } @@ -91,7 +93,7 @@ public: _Init(); } - basic_syncbuf(basic_syncbuf&& _Right) : _Al(_STD move(_Right._Al)) { + basic_syncbuf(basic_syncbuf&& _Right) : _Mypair{_One_then_variadic_args_t{}, _STD move(_Right._Getal()), nullptr} { _Swap_except_al(_Right); } @@ -110,7 +112,7 @@ public: void swap(basic_syncbuf& _Right) noexcept { if (this != _STD addressof(_Right)) { - _Pocs(_Al, _Right._Al); + _Pocs(_Getal(), _Right._Getal()); _Swap_except_al(_Right); } } @@ -124,7 +126,7 @@ public: const _Size_type _Data_size = _Get_data_size(); _Elem* const _Begin_seq_ptr = streambuf_type::pbase(); if (_Data_size > 0 || _Mybase::_Sync_recorded) { - scoped_lock _Guard(*_Mutex); + scoped_lock _Guard(*_Get_mutex()); if (_Data_size > 0 && _Data_size @@ -149,7 +151,7 @@ public: } _NODISCARD allocator_type get_allocator() const noexcept { - return _Al; + return _Mypair._Get_first(); } protected: @@ -173,6 +175,7 @@ protected: return _Traits::not_eof(_Current_elem); } + auto& _Al = _Getal(); const _Size_type _Buf_size = _Get_buffer_size(); const _Size_type _Max_allocation = allocator_traits<_Alloc>::max_size(_Al); if (_Buf_size == _Max_allocation) { @@ -196,38 +199,39 @@ private: static constexpr _Size_type _Min_size = 32; // constant for minimum buffer size void _Init() { - _Elem* const _New_ptr = _Unfancy(_Al.allocate(_Min_size)); + _Elem* const _New_ptr = _Unfancy(_Getal().allocate(_Min_size)); streambuf_type::setp(_New_ptr, _New_ptr + _Min_size); } void _Tidy() noexcept { const _Size_type _Buf_size = _Get_buffer_size(); if (0 < _Buf_size) { - _Al.deallocate(_Refancy<_Pointer>(streambuf_type::pbase()), _Buf_size); + _Getal().deallocate(_Refancy<_Pointer>(streambuf_type::pbase()), _Buf_size); } streambuf_type::setp(nullptr, nullptr, nullptr); if (_Wrapped) { __std_release_shared_mutex_for_instance(_Wrapped); - _Wrapped = nullptr; - _Mutex = nullptr; + _Wrapped = nullptr; + _Get_mutex() = nullptr; } } void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) noexcept { _Tidy(); - _Pocma(_Al, _Right._Al); + _Pocma(_Getal(), _Right._Getal()); _Swap_except_al(_Right); } void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) noexcept { _Tidy(); - _Pocma(_Al, _Right._Al); + _Pocma(_Getal(), _Right._Getal()); _Swap_except_al(_Right); } void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { - if (_Al == _Right._Al) { + auto& _Al = _Getal(); + if (_Al == _Right._Getal()) { _Move_assign(_STD move(_Right), _Equal_allocators{}); } else { _Tidy(); @@ -244,7 +248,7 @@ private: _STD swap(_Mybase::_Emit_on_sync, _Right._Emit_on_sync); _STD swap(_Mybase::_Sync_recorded, _Right._Sync_recorded); _STD swap(_Wrapped, _Right._Wrapped); - _STD swap(_Mutex, _Right._Mutex); + _STD swap(_Get_mutex(), _Right._Get_mutex()); _Right._Tidy(); } @@ -253,7 +257,7 @@ private: void _Swap_except_al(basic_syncbuf& _Right) noexcept { _Mybase::_Swap(_Right); _STD swap(_Wrapped, _Right._Wrapped); - _STD swap(_Mutex, _Right._Mutex); + _STD swap(_Get_mutex(), _Right._Get_mutex()); } virtual bool _Do_emit() override { @@ -291,9 +295,16 @@ private: return static_cast<_Size_type>(streambuf_type::epptr() - streambuf_type::pbase()); } + _NODISCARD _Alloc& _Getal() noexcept { + return _Mypair._Get_first(); + } + + _NODISCARD shared_mutex*& _Get_mutex() noexcept { + return _Mypair._Myval2; + } + streambuf_type* _Wrapped{nullptr}; - shared_mutex* _Mutex{nullptr}; - allocator_type _Al{}; + _Compressed_pair<_Alloc, shared_mutex*> _Mypair{_Zero_then_variadic_args_t{}, nullptr}; }; template From ec2a3a5ca6b33eabe7b5d1e5ddc80cb6b920f732 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 6 Feb 2021 18:59:10 -0800 Subject: [PATCH 51/60] Code review feedback for tests. --- stl/inc/syncstream | 6 ++ .../test.cpp | 41 ++++---- .../test.hpp | 93 ++++++++++++------- .../test.cpp | 39 ++++---- 4 files changed, 105 insertions(+), 74 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 2ed144a3bc6..1c23880a25f 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -42,6 +42,12 @@ public: virtual bool _Do_emit() = 0; +#ifdef _ENABLE_STL_INTERNAL_CHECK + _NODISCARD bool _Stl_internal_check_get_emit_on_sync() const noexcept { + return _Emit_on_sync; + } +#endif // _ENABLE_STL_INTERNAL_CHECK + protected: using _Mysb = basic_streambuf<_Elem, _Traits>; diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp index 47c84cd7cff..a0944ed4eef 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -6,10 +6,11 @@ using namespace std; static_assert(is_default_constructible_v); -static_assert(is_constructible_v); -static_assert(is_constructible_v); +static_assert(is_constructible_v); +static_assert(is_constructible_v); static_assert(is_move_constructible_v); static_assert(is_move_assignable_v); +static_assert(is_swappable_v); static_assert(is_destructible_v); template @@ -17,18 +18,18 @@ class test_syncbuf : public basic_syncbuf { public: using size_type = typename Alloc::size_type; using value_type = typename Alloc::value_type; - using _Mybase = basic_syncbuf; - using streambuf_type = typename _Mybase::streambuf_type; + using Mybase = basic_syncbuf; + using streambuf_type = typename Mybase::streambuf_type; - using _Mybase::epptr; - using _Mybase::pbase; - using _Mybase::pptr; + using Mybase::epptr; + using Mybase::pbase; + using Mybase::pptr; test_syncbuf() = default; - explicit test_syncbuf(streambuf_type* _Strbuf) : _Mybase(_Strbuf) {} + explicit test_syncbuf(streambuf_type* strbuf) : Mybase(strbuf) {} - test_syncbuf(streambuf_type* _Strbuf, const Alloc& _Al) : _Mybase(_Strbuf, _Al) {} + test_syncbuf(streambuf_type* strbuf, const Alloc& al) : Mybase(strbuf, al) {} test_syncbuf(test_syncbuf&&) = default; test_syncbuf& operator=(test_syncbuf&&) = default; @@ -60,7 +61,7 @@ void test_syncbuf_member_functions(string_buffer* bu assert(aSyncbuf.get_wrapped() == buf); assert(aSyncbuf.get_allocator() == alloc); assert(aSyncbuf.Test_get_data_size() == 0); - assert(aSyncbuf.Test_get_buffer_size() == _Min_syncbuf_size); + assert(aSyncbuf.Test_get_buffer_size() == Min_syncbuf_size); // check emit post-conditions with no input if (buf) { @@ -89,8 +90,8 @@ void test_syncbuf_member_functions(string_buffer* bu assert(buf->str == "A string holds more than 32 characters"); buf->str.clear(); } else { - assert(aSyncbuf.Test_get_data_size() == _Min_syncbuf_size); // if _Wrapped is nullptr, re-allocation will not - // occur and will return a _Traits::eof bit + assert(aSyncbuf.Test_get_data_size() == Min_syncbuf_size); // if _Wrapped is nullptr, re-allocation will not + // occur and will return a _Traits::eof bit assert(os.rdstate() == ios::badbit); os.setstate(ios::goodbit); assert(aSyncbuf.emit() == false); @@ -99,7 +100,7 @@ void test_syncbuf_member_functions(string_buffer* bu os << "A string that will definitely overflow the small_size_allocator"; // requires more than one re-allocation if (buf) { if constexpr (is_base_of_v) { // fail to allocate enough memory - assert(aSyncbuf.Test_get_data_size() == _Min_size_allocation); + assert(aSyncbuf.Test_get_data_size() == Min_size_allocation); assert(os.rdstate() == ios::badbit); os.setstate(ios::goodbit); assert(aSyncbuf.emit() == true); @@ -112,7 +113,7 @@ void test_syncbuf_member_functions(string_buffer* bu } buf->str.clear(); } else { - assert(aSyncbuf.Test_get_data_size() == _Min_syncbuf_size); + assert(aSyncbuf.Test_get_data_size() == Min_syncbuf_size); assert(os.rdstate() == ios::badbit); os.setstate(ios::goodbit); assert(aSyncbuf.emit() == false); @@ -157,7 +158,7 @@ void test_syncbuf_synchronization(string_bufferstr - == "First element to by emitted by sync!\nSecond element to be presented!\nThird element to be " + == "First element to be emitted by sync!\nSecond element to be presented!\nThird element to be " "presented!\nLast element to be presented!\n"); buf->str.clear(); } @@ -223,9 +224,9 @@ void test_syncbuf_move_swap_operations(string_buffer assert(buf1.Test_get_data_size() == 0); if constexpr (allocator_traits::propagate_on_container_move_assignment::value - && is_same_v::is_always_equal, true_type>) { + && allocator_traits::is_always_equal::value) { assert(buf1.get_allocator() == buf2.get_allocator()); - } else if constexpr (is_same_v::is_always_equal, true_type>) { + } else if constexpr (allocator_traits::is_always_equal::value) { assert(buf1.get_allocator() == buf2.get_allocator()); } else { assert(buf1.get_allocator() != buf2.get_allocator()); @@ -244,7 +245,7 @@ void test_syncbuf_move_swap_operations(string_buffer auto buf2BufferSize = buf2.Test_get_buffer_size(); auto buf2DataSize = buf2.Test_get_data_size(); if constexpr (allocator_traits::propagate_on_container_swap::value - || is_same_v::is_always_equal, true_type>) { + || allocator_traits::is_always_equal::value) { buf1.swap(buf2); assert(buf2.get_wrapped() == buf1WrappedObject); assert(buf2.Test_get_buffer_size() == buf1BufferSize); @@ -380,7 +381,7 @@ int main() { test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); - test_syncbuf_move_swap_operations>(&char_buffer); + test_syncbuf_move_swap_operations>(&char_buffer); // Testing basic_osyncstream test_osyncstream>(); diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index b79295f000a..fe979346588 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -1,5 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + #include +#include +#include +#include #include +#include #include #include #include @@ -7,8 +16,8 @@ using namespace std; -constexpr size_t _Min_size_allocation = 50; -constexpr size_t _Min_syncbuf_size = 32; +constexpr size_t Min_size_allocation = 50; +constexpr size_t Min_syncbuf_size = 32; template class string_buffer : public basic_streambuf> { // represents the wrapped object in syncbuf @@ -16,9 +25,9 @@ class string_buffer : public basic_streambuf> { // represent string_buffer() = default; ~string_buffer() = default; - streamsize xsputn(const Ty* _Ptr, streamsize _Count) override { - str.append(_Ptr, static_cast(_Count)); - return _Count; + streamsize xsputn(const Ty* ptr, streamsize n) override { + str.append(ptr, static_cast(n)); + return n; } int sync() override { @@ -36,8 +45,8 @@ class small_size_allocation { public: using size_type = size_t; - _NODISCARD size_type max_size() const noexcept { - return _Min_size_allocation; + [[nodiscard]] size_type max_size() const noexcept { + return Min_size_allocation; } }; @@ -47,12 +56,12 @@ class allocator_base { using value_type = Ty; using pointer = Ty*; - _NODISCARD __declspec(allocator) pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - return static_cast(allocator{}.allocate(_Count)); + [[nodiscard]] pointer allocate(const size_t n) { + return allocator{}.allocate(n); } - void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - allocator{}.deallocate(_Ptr, _Count); + void deallocate(const pointer ptr, const size_t n) noexcept { + allocator{}.deallocate(ptr, n); } }; @@ -63,7 +72,7 @@ class fancy_ptr { public: fancy_ptr() = default; - fancy_ptr(std::nullptr_t) {} + fancy_ptr(nullptr_t) {} explicit fancy_ptr(Ty* _ptr) : ptr(static_cast(reinterpret_cast(_ptr))) {} template explicit fancy_ptr(const fancy_ptr& right) : ptr(right.ptr) {} @@ -73,8 +82,8 @@ class fancy_ptr { } template - static fancy_ptr pointer_to(Other& _ptr) noexcept { - return fancy_ptr(addressof(_ptr)); + static fancy_ptr pointer_to(Other& obj) noexcept { + return fancy_ptr(addressof(obj)); } }; @@ -89,18 +98,21 @@ class fancy_ptr_allocator { constexpr fancy_ptr_allocator() noexcept = default; - _NODISCARD pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - auto _Ptr = allocator{}.allocate(_Count); - return static_cast(static_cast(_Ptr)); + template + constexpr fancy_ptr_allocator(const fancy_ptr_allocator&) noexcept {} + + [[nodiscard]] pointer allocate(const size_t n) { + auto ptr = allocator{}.allocate(n); + return static_cast(ptr); } - void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - allocator{}.deallocate(&*_Ptr, _Count); + void deallocate(const pointer ptr, const size_t n) noexcept { + allocator{}.deallocate(&*ptr, n); } }; template -_NODISCARD bool operator==(const fancy_ptr_allocator&, const fancy_ptr_allocator&) noexcept { +[[nodiscard]] bool operator==(const fancy_ptr_allocator&, const fancy_ptr_allocator&) noexcept { return true; } @@ -114,18 +126,21 @@ class small_size_fancy_ptr_allocator : public small_size_allocation { constexpr small_size_fancy_ptr_allocator() noexcept = default; - _NODISCARD pointer allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - auto _Ptr = allocator{}.allocate(_Count); - return static_cast(static_cast(_Ptr)); + template + constexpr small_size_fancy_ptr_allocator(const small_size_fancy_ptr_allocator&) noexcept {} + + [[nodiscard]] pointer allocate(const size_t n) { + auto ptr = allocator{}.allocate(n); + return static_cast(ptr); } - void deallocate(pointer const _Ptr, const size_t _Count) noexcept { - allocator{}.deallocate(&*_Ptr, _Count); + void deallocate(const pointer ptr, const size_t n) noexcept { + allocator{}.deallocate(&*ptr, n); } }; template -_NODISCARD bool operator==( +[[nodiscard]] bool operator==( const small_size_fancy_ptr_allocator&, const small_size_fancy_ptr_allocator&) noexcept { return true; } @@ -137,10 +152,13 @@ class small_size_allocator : public allocator_base, public small_size_alloca using propagate_on_container_swap = true_type; constexpr small_size_allocator() noexcept = default; + + template + constexpr small_size_allocator(const small_size_allocator&) noexcept {} }; template -_NODISCARD bool operator==(const small_size_allocator&, const small_size_allocator&) noexcept { +[[nodiscard]] bool operator==(const small_size_allocator&, const small_size_allocator&) noexcept { return true; } @@ -153,10 +171,13 @@ class non_move_assignable_non_equal_allocator : public allocator_base { using is_always_equal = false_type; constexpr non_move_assignable_non_equal_allocator() noexcept = default; + + template + constexpr non_move_assignable_non_equal_allocator(const non_move_assignable_non_equal_allocator&) noexcept {} }; template -_NODISCARD bool operator==(const non_move_assignable_non_equal_allocator&, +[[nodiscard]] bool operator==(const non_move_assignable_non_equal_allocator&, const non_move_assignable_non_equal_allocator&) noexcept { return false; } @@ -169,26 +190,32 @@ class non_move_assignable_equal_allocator : public allocator_base { using propagate_on_container_swap = true_type; constexpr non_move_assignable_equal_allocator() noexcept = default; + + template + constexpr non_move_assignable_equal_allocator(const non_move_assignable_equal_allocator&) noexcept {} }; template -_NODISCARD bool operator==( +[[nodiscard]] bool operator==( const non_move_assignable_equal_allocator&, const non_move_assignable_equal_allocator&) noexcept { return true; } template -class non_swapable_equal_allocator : public allocator_base { +class non_swappable_equal_allocator : public allocator_base { public: using size_type = size_t; using propagate_on_container_move_assignment = true_type; using propagate_on_container_swap = false_type; - constexpr non_swapable_equal_allocator() noexcept = default; + constexpr non_swappable_equal_allocator() noexcept = default; + + template + constexpr non_swappable_equal_allocator(const non_swappable_equal_allocator&) noexcept {} }; template -_NODISCARD bool operator==( - const non_swapable_equal_allocator&, const non_swapable_equal_allocator&) noexcept { +[[nodiscard]] bool operator==( + const non_swappable_equal_allocator&, const non_swappable_equal_allocator&) noexcept { return true; } diff --git a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp index d352344438c..f58fde52393 100644 --- a/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0753R2_manipulators_for_cpp_synchronized_buffered_ostream/test.cpp @@ -1,4 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + #include +#include +#include +#include #include #include #include @@ -11,9 +17,9 @@ class string_buffer : public basic_streambuf> { // represent string_buffer() = default; ~string_buffer() = default; - streamsize xsputn(const Ty* _Ptr, streamsize _Count) override { - str.append(_Ptr, static_cast(_Count)); - return _Count; + streamsize xsputn(const Ty* ptr, streamsize n) override { + str.append(ptr, static_cast(n)); + return n; } int sync() override { @@ -27,30 +33,21 @@ class string_buffer : public basic_streambuf> { // represent string str; }; -template -class Test_Basic_syncbuf : public _Basic_syncbuf_impl { - using Mybase = _Basic_syncbuf_impl; - -public: - Test_Basic_syncbuf() = default; - Test_Basic_syncbuf(Test_Basic_syncbuf&& _Right) = default; - - using Mybase::_Emit_on_sync; -}; template -requires(is_base_of_v, - Ty>) void test_osyncstream_manipulators(string_buffer* buf = nullptr) { +void test_osyncstream_manipulators(string_buffer* buf = nullptr) { using char_type = typename Ty::char_type; using traits_type = typename Ty::traits_type; + static_assert(is_base_of_v, Ty>); + Ty os{buf}; os << "Some input"; assert(addressof(emit_on_flush(os)) == addressof(os)); if constexpr (is_base_of_v, Ty>) { - auto* aSyncbuf = reinterpret_cast*>(os.rdbuf()); - assert(aSyncbuf->_Emit_on_sync == true); + auto* aSyncbuf = static_cast*>(os.rdbuf()); + assert(aSyncbuf->_Stl_internal_check_get_emit_on_sync() == true); if (buf) { assert(buf->str == ""); } @@ -63,6 +60,7 @@ requires(is_base_of_vstr == "Some input"); buf->str.clear(); @@ -73,8 +71,8 @@ requires(is_base_of_v, Ty>) { - auto* aSyncbuf = reinterpret_cast*>(os.rdbuf()); - assert(aSyncbuf->_Emit_on_sync == false); + auto* aSyncbuf = static_cast*>(os.rdbuf()); + assert(aSyncbuf->_Stl_internal_check_get_emit_on_sync() == false); } assert(addressof(flush_emit(os)) == addressof(os)); @@ -94,7 +92,7 @@ requires(is_base_of_v char_buffer{}; string_buffer no_sync_char_buffer{}; -#ifdef _CPPRTTI + test_osyncstream_manipulators>(); test_osyncstream_manipulators, allocator>, allocator>(); @@ -105,5 +103,4 @@ int main() { test_osyncstream_manipulators>(&no_sync_char_buffer); test_osyncstream_manipulators, allocator>, allocator>( &no_sync_char_buffer); -#endif } From bbdfc77979989fa799a802ea04ecda8f9c25dbbf Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 8 Feb 2021 01:29:16 -0800 Subject: [PATCH 52/60] Return shared_mutex*, comment that this isn't really flat. --- stl/inc/syncstream | 4 ++-- stl/src/syncstream.cpp | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 1c23880a25f..3d4ab991b53 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -26,7 +26,7 @@ _STL_DISABLE_CLANG_WARNINGS #undef emit _EXTERN_C -_NODISCARD void* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept; +_NODISCARD _STD shared_mutex* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept; void __stdcall __std_release_shared_mutex_for_instance(void* _Ptr) noexcept; _END_EXTERN_C @@ -91,7 +91,7 @@ public: : _Wrapped(_Strbuf), _Mypair{_One_then_variadic_args_t{}, _Al_, nullptr} { if (_Wrapped) { auto& _Mutex = _Get_mutex(); - _Mutex = static_cast(__std_acquire_shared_mutex_for_instance(_Wrapped)); + _Mutex = __std_acquire_shared_mutex_for_instance(_Wrapped); if (!_Mutex) { _Xbad_alloc(); } diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index b9ed5fc73ad..ee9db7eb350 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -57,13 +57,14 @@ namespace { _EXTERN_C -_NODISCARD void* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept { +// TRANSITION, ABI: This returns a pointer to a C++ type. +// A flat C interface would return an opaque handle and would provide separate functions for locking and unlocking. +_NODISCARD _STD shared_mutex* __stdcall __std_acquire_shared_mutex_for_instance(void* _Ptr) noexcept { try { _STD scoped_lock _Guard(_Lookup_mutex); auto& [_Mutex, _Refs] = _Lookup_map.try_emplace(_Ptr).first->second; ++_Refs; - static_assert(_STD is_same_v); - return &_Mutex; // caller will restore type + return &_Mutex; } catch (...) { return nullptr; } From 2763e9fa67d60e07450ee7838b353b4ae1a37373 Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Mon, 8 Feb 2021 10:21:51 +0000 Subject: [PATCH 53/60] move fancy pointer test to VSO_0000000_fancy_pointers --- .../test.cpp | 11 --- .../test.hpp | 80 ------------------- .../test.compile.pass.cpp | 6 ++ 3 files changed, 6 insertions(+), 91 deletions(-) diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp index a0944ed4eef..06da2ce3a88 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.cpp @@ -364,21 +364,14 @@ int main() { // Testing basic_syncbuf test_syncbuf_member_functions>(); test_syncbuf_member_functions>(); - test_syncbuf_member_functions>(); - test_syncbuf_member_functions>(); test_syncbuf_member_functions>(&char_buffer); test_syncbuf_member_functions>(&char_buffer); - test_syncbuf_member_functions>(&char_buffer); - test_syncbuf_member_functions>(&char_buffer); test_syncbuf_synchronization>(&char_buffer); test_syncbuf_synchronization>(&no_sync_char_buffer); - test_syncbuf_synchronization>(&char_buffer); - test_syncbuf_synchronization>(&no_sync_char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); - test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); test_syncbuf_move_swap_operations>(&char_buffer); @@ -386,11 +379,7 @@ int main() { // Testing basic_osyncstream test_osyncstream>(); test_osyncstream>(); - test_osyncstream>(); - test_osyncstream>(); test_osyncstream>(&char_buffer); test_osyncstream>(&char_buffer); - test_osyncstream>(&char_buffer); - test_osyncstream>(&char_buffer); } diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index fe979346588..687cce4f5c3 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -65,86 +65,6 @@ class allocator_base { } }; -template -class fancy_ptr { -private: - uint64_t ptr = 0; - -public: - fancy_ptr() = default; - fancy_ptr(nullptr_t) {} - explicit fancy_ptr(Ty* _ptr) : ptr(static_cast(reinterpret_cast(_ptr))) {} - template - explicit fancy_ptr(const fancy_ptr& right) : ptr(right.ptr) {} - - Ty& operator*() const { - return *reinterpret_cast(static_cast(ptr)); - } - - template - static fancy_ptr pointer_to(Other& obj) noexcept { - return fancy_ptr(addressof(obj)); - } -}; - -template -class fancy_ptr_allocator { -public: - using value_type = Ty; - using pointer = fancy_ptr; - using size_type = size_t; - using propagate_on_container_move_assignment = true_type; - using propagate_on_container_swap = true_type; - - constexpr fancy_ptr_allocator() noexcept = default; - - template - constexpr fancy_ptr_allocator(const fancy_ptr_allocator&) noexcept {} - - [[nodiscard]] pointer allocate(const size_t n) { - auto ptr = allocator{}.allocate(n); - return static_cast(ptr); - } - - void deallocate(const pointer ptr, const size_t n) noexcept { - allocator{}.deallocate(&*ptr, n); - } -}; - -template -[[nodiscard]] bool operator==(const fancy_ptr_allocator&, const fancy_ptr_allocator&) noexcept { - return true; -} - -template -class small_size_fancy_ptr_allocator : public small_size_allocation { -public: - using value_type = Ty; - using pointer = fancy_ptr; - using propagate_on_container_move_assignment = true_type; - using propagate_on_container_swap = true_type; - - constexpr small_size_fancy_ptr_allocator() noexcept = default; - - template - constexpr small_size_fancy_ptr_allocator(const small_size_fancy_ptr_allocator&) noexcept {} - - [[nodiscard]] pointer allocate(const size_t n) { - auto ptr = allocator{}.allocate(n); - return static_cast(ptr); - } - - void deallocate(const pointer ptr, const size_t n) noexcept { - allocator{}.deallocate(&*ptr, n); - } -}; - -template -[[nodiscard]] bool operator==( - const small_size_fancy_ptr_allocator&, const small_size_fancy_ptr_allocator&) noexcept { - return true; -} - template class small_size_allocator : public allocator_base, public small_size_allocation { public: diff --git a/tests/std/tests/VSO_0000000_fancy_pointers/test.compile.pass.cpp b/tests/std/tests/VSO_0000000_fancy_pointers/test.compile.pass.cpp index 60f0b53fbc0..2a6a9b4b90e 100644 --- a/tests/std/tests/VSO_0000000_fancy_pointers/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0000000_fancy_pointers/test.compile.pass.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -431,6 +432,11 @@ template class std::unordered_multiset, fancy_all template class std::basic_string, fancy_allocator>; template class std::basic_stringbuf, fancy_allocator>; +#if _HAS_CXX20 +template class std::basic_syncbuf, fancy_allocator>; +template class std::basic_osyncstream, fancy_allocator>; +#endif + void instantiate() { random_iterators_test>>(); fwd_iterators_test>>(); From eeb28aa096651cbf96c74981d0af0000e20528d8 Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Mon, 8 Feb 2021 13:29:02 +0000 Subject: [PATCH 54/60] Attempt to achieve a conformant allocator - it does not protect against overflow, but for test purposes it should be (fine?) Co-Authored-By: Stephan T. Lavavej --- .../test.hpp | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index 687cce4f5c3..b5f183c1170 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -82,23 +82,42 @@ template return true; } +class non_move_assignable_non_equal_allocator_id { +public: + non_move_assignable_non_equal_allocator_id() : id(id_gen++) {} + constexpr non_move_assignable_non_equal_allocator_id(size_t _id) : id(_id) {} + ~non_move_assignable_non_equal_allocator_id() = default; + + size_t id; + +private: + static size_t id_gen; +}; +size_t non_move_assignable_non_equal_allocator_id::id_gen = 0; + template -class non_move_assignable_non_equal_allocator : public allocator_base { +class non_move_assignable_non_equal_allocator : public non_move_assignable_non_equal_allocator_id, + public allocator_base { public: using size_type = size_t; using propagate_on_container_move_assignment = false_type; using propagate_on_container_swap = true_type; using is_always_equal = false_type; - constexpr non_move_assignable_non_equal_allocator() noexcept = default; + non_move_assignable_non_equal_allocator() noexcept = default; template - constexpr non_move_assignable_non_equal_allocator(const non_move_assignable_non_equal_allocator&) noexcept {} + constexpr non_move_assignable_non_equal_allocator( + const non_move_assignable_non_equal_allocator& rhs) noexcept + : non_move_assignable_non_equal_allocator_id{rhs.id} {} }; template -[[nodiscard]] bool operator==(const non_move_assignable_non_equal_allocator&, - const non_move_assignable_non_equal_allocator&) noexcept { +[[nodiscard]] bool operator==(const non_move_assignable_non_equal_allocator& lhs, + const non_move_assignable_non_equal_allocator& rhs) noexcept { + if (lhs.id == rhs.id) { + return true; + } return false; } From a0faf59f9604f64fefbcbebd4f635df2a96348de Mon Sep 17 00:00:00 2001 From: Michael Rizkalla Date: Mon, 8 Feb 2021 13:50:06 +0000 Subject: [PATCH 55/60] Update test Co-Authored-By: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> --- .../P0053R7_cpp_synchronized_buffered_ostream/test.hpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index b5f183c1170..94524559d79 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -91,9 +91,8 @@ class non_move_assignable_non_equal_allocator_id { size_t id; private: - static size_t id_gen; + inline static size_t id_gen = 0; }; -size_t non_move_assignable_non_equal_allocator_id::id_gen = 0; template class non_move_assignable_non_equal_allocator : public non_move_assignable_non_equal_allocator_id, @@ -115,10 +114,7 @@ class non_move_assignable_non_equal_allocator : public non_move_assignable_non_e template [[nodiscard]] bool operator==(const non_move_assignable_non_equal_allocator& lhs, const non_move_assignable_non_equal_allocator& rhs) noexcept { - if (lhs.id == rhs.id) { - return true; - } - return false; + return lhs.id == rhs.id; } template From a9db2caaf780553f38f03c2028ffd1358a28086a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 8 Feb 2021 18:39:56 -0800 Subject: [PATCH 56/60] Add explicit. --- .../tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp index 94524559d79..beffa7170de 100644 --- a/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp +++ b/tests/std/tests/P0053R7_cpp_synchronized_buffered_ostream/test.hpp @@ -85,7 +85,7 @@ template class non_move_assignable_non_equal_allocator_id { public: non_move_assignable_non_equal_allocator_id() : id(id_gen++) {} - constexpr non_move_assignable_non_equal_allocator_id(size_t _id) : id(_id) {} + constexpr explicit non_move_assignable_non_equal_allocator_id(size_t _id) : id(_id) {} ~non_move_assignable_non_equal_allocator_id() = default; size_t id; From 33b19d0273ada99970388345132076bfe2df6c6a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 8 Feb 2021 20:44:45 -0800 Subject: [PATCH 57/60] stl/msbuild changes. --- .../stl_atomic_wait/stl_atomic_wait.files.settings.targets | 1 + stl/msbuild/stl_base/stl.files.settings.targets | 1 + 2 files changed, 2 insertions(+) diff --git a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets index e7cc52c398d..05ebeab8862 100644 --- a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets +++ b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets @@ -8,6 +8,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception nativecpp diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 5fd1b263103..0cea788aee0 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -16,6 +16,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\memory_resource.cpp; $(CrtRoot)\github\stl\src\parallel_algorithms.cpp; $(CrtRoot)\github\stl\src\special_math.cpp; + $(CrtRoot)\github\stl\src\syncstream.cpp; $(CrtRoot)\github\stl\src\ulocale.cpp; "> nativecpp From 627641fbc0a71ac9a34122c98310de99bb966a65 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 16 Feb 2021 14:36:21 -0800 Subject: [PATCH 58/60] stl/msbuild: Drop special_math.cpp comment. This comment is increasingly confusing - it was added when the BuildFiles below was moved to the top, which is the important part. special_math.cpp doesn't need to be literally first. We aren't going to arbitrarily reorder these lists, and in the long term, our CMake build system will replace our legacy msbuild system. --- stl/msbuild/stl_base/stl.files.settings.targets | 4 ---- 1 file changed, 4 deletions(-) diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 0cea788aee0..b83c74b1e4f 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -6,10 +6,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception --> - - Date: Wed, 17 Feb 2021 15:40:07 -0800 Subject: [PATCH 59/60] Use _Init_locks. --- stl/src/syncstream.cpp | 1 + tests/std/tests/P1502R1_standard_library_header_units/test.cpp | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index ee9db7eb350..77e7df373e9 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -14,6 +14,7 @@ #pragma warning(disable : 4074) #pragma init_seg(compiler) +static std::_Init_locks initlocks; namespace { // OBJECT DECLARATIONS diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index fa593c11b05..e0a961e6bd0 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -810,7 +810,6 @@ int main() { { puts("Testing ."); -#if 0 // TRANSITION, VSO-1275701 (runtime crash with dynamic atexit destructor for syncstreams) syncbuf sync_buf{nullptr}; assert(sync_buf.get_wrapped() == nullptr); assert(sync_buf.get_allocator() == allocator{}); @@ -818,7 +817,6 @@ int main() { osyncstream sync_str{cout}; sync_str << "Testing P1502R1_standard_library_header_units.\n"; assert(sync_str.rdbuf()->get_wrapped() == cout.rdbuf()); -#endif // ^^^ no workaround ^^^ } { From d70684fa891e75aca21071c1c2c891196ee4c8ae Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 18 Feb 2021 21:32:40 -0800 Subject: [PATCH 60/60] Remove noexcept from move/swap machinery, mention LWG-3498. --- stl/inc/syncstream | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/stl/inc/syncstream b/stl/inc/syncstream index 3d4ab991b53..434a1cd7a7f 100644 --- a/stl/inc/syncstream +++ b/stl/inc/syncstream @@ -57,7 +57,7 @@ protected: _Swap(_Right); } - void _Swap(_Basic_syncbuf_impl& _Right) noexcept { + void _Swap(_Basic_syncbuf_impl& _Right) { // see LWG-3498 regarding noexcept _Mysb::swap(_Right); _STD swap(_Emit_on_sync, _Right._Emit_on_sync); _STD swap(_Sync_recorded, _Right._Sync_recorded); @@ -108,15 +108,15 @@ public: _Tidy(); } - basic_syncbuf& operator=(basic_syncbuf&& _Right) { - _Emit(); + basic_syncbuf& operator=(basic_syncbuf&& _Right) { // see LWG-3498 regarding noexcept + emit(); if (this != _STD addressof(_Right)) { _Move_assign(_STD move(_Right), _Choose_pocma<_Alloc>{}); } return *this; } - void swap(basic_syncbuf& _Right) noexcept { + void swap(basic_syncbuf& _Right) { // see LWG-3498 regarding noexcept if (this != _STD addressof(_Right)) { _Pocs(_Getal(), _Right._Getal()); _Swap_except_al(_Right); @@ -223,19 +223,19 @@ private: } } - void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) noexcept { + void _Move_assign(basic_syncbuf&& _Right, _Equal_allocators) { // see LWG-3498 regarding noexcept _Tidy(); _Pocma(_Getal(), _Right._Getal()); _Swap_except_al(_Right); } - void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) noexcept { + void _Move_assign(basic_syncbuf&& _Right, _Propagate_allocators) { // see LWG-3498 regarding noexcept _Tidy(); _Pocma(_Getal(), _Right._Getal()); _Swap_except_al(_Right); } - void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { + void _Move_assign(basic_syncbuf&& _Right, _No_propagate_allocators) { // see LWG-3498 regarding noexcept auto& _Al = _Getal(); if (_Al == _Right._Getal()) { _Move_assign(_STD move(_Right), _Equal_allocators{}); @@ -260,7 +260,7 @@ private: } } - void _Swap_except_al(basic_syncbuf& _Right) noexcept { + void _Swap_except_al(basic_syncbuf& _Right) { // see LWG-3498 regarding noexcept _Mybase::_Swap(_Right); _STD swap(_Wrapped, _Right._Wrapped); _STD swap(_Get_mutex(), _Right._Get_mutex()); @@ -314,7 +314,8 @@ private: }; template -void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, basic_syncbuf<_Elem, _Traits, _Alloc>& _Right) noexcept { +void swap(basic_syncbuf<_Elem, _Traits, _Alloc>& _Left, + basic_syncbuf<_Elem, _Traits, _Alloc>& _Right) { // see LWG-3498 regarding noexcept _Left.swap(_Right); }