diff --git a/include/boost/system/detail/error_category.hpp b/include/boost/system/detail/error_category.hpp index a205d81ca..6185423ed 100644 --- a/include/boost/system/detail/error_category.hpp +++ b/include/boost/system/detail/error_category.hpp @@ -48,6 +48,11 @@ class std_category; #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" #endif +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable: 4351) // new behavior: elements of array will be default initialized +#endif + class BOOST_SYMBOL_VISIBLE error_category { private: @@ -76,13 +81,21 @@ class BOOST_SYMBOL_VISIBLE error_category boost::ulong_long_type id_; + static std::size_t const stdcat_size_ = 4 * sizeof( void const* ); + + union + { + mutable unsigned char stdcat_[ stdcat_size_ ]; + void const* stdcat_align_; + }; + #if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) - mutable std::atomic< boost::system::detail::std_category* > ps_; + mutable std::atomic< unsigned > sc_init_; #else - boost::system::detail::std_category* ps_; + unsigned sc_init_; #endif @@ -103,11 +116,11 @@ class BOOST_SYMBOL_VISIBLE error_category #endif - BOOST_SYSTEM_CONSTEXPR error_category() BOOST_NOEXCEPT: id_( 0 ), ps_() + BOOST_SYSTEM_CONSTEXPR error_category() BOOST_NOEXCEPT: id_( 0 ), stdcat_(), sc_init_() { } - explicit BOOST_SYSTEM_CONSTEXPR error_category( boost::ulong_long_type id ) BOOST_NOEXCEPT: id_( id ), ps_() + explicit BOOST_SYSTEM_CONSTEXPR error_category( boost::ulong_long_type id ) BOOST_NOEXCEPT: id_( id ), stdcat_(), sc_init_() { } @@ -158,14 +171,22 @@ class BOOST_SYMBOL_VISIBLE error_category } #if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) + + void init_stdcat() const; + # if defined(__SUNPRO_CC) // trailing __global is not supported operator std::error_category const & () const; # else operator std::error_category const & () const BOOST_SYMBOL_VISIBLE; # endif + #endif }; +#if defined(BOOST_MSVC) +#pragma warning(pop) +#endif + #if ( defined( BOOST_GCC ) && BOOST_GCC >= 40600 ) || defined( BOOST_CLANG ) #pragma GCC diagnostic pop #endif diff --git a/include/boost/system/detail/error_category_impl.hpp b/include/boost/system/detail/error_category_impl.hpp index 3512ddced..db3fbc96a 100644 --- a/include/boost/system/detail/error_category_impl.hpp +++ b/include/boost/system/detail/error_category_impl.hpp @@ -98,12 +98,47 @@ inline char const * error_category::message( int ev, char * buffer, std::size_t #if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR) #include +#include +#include namespace boost { namespace system { +namespace detail +{ + +template struct stdcat_mx_holder +{ + static std::mutex mx_; +}; + +template std::mutex stdcat_mx_holder::mx_; + +} // namespace detail + +inline BOOST_NOINLINE void error_category::init_stdcat() const +{ + static_assert( sizeof( stdcat_ ) >= sizeof( boost::system::detail::std_category ), "sizeof(stdcat_) is not enough for std_category" ); + +#if defined(BOOST_MSVC) && BOOST_MSVC < 1900 + // no alignof +#else + + static_assert( alignof( decltype(stdcat_align_) ) >= alignof( boost::system::detail::std_category ), "alignof(stdcat_) is not enough for std_category" ); + +#endif + + std::lock_guard lk( boost::system::detail::stdcat_mx_holder<>::mx_ ); + + if( sc_init_.load( std::memory_order_acquire ) == 0 ) + { + ::new( static_cast( stdcat_ ) ) boost::system::detail::std_category( this, 0 ); + sc_init_.store( 1, std::memory_order_release ); + } +} + inline error_category::operator std::error_category const & () const { if( id_ == detail::generic_category_id ) @@ -111,12 +146,12 @@ inline error_category::operator std::error_category const & () const // This condition must be the same as the one in error_condition.hpp #if defined(BOOST_SYSTEM_AVOID_STD_GENERIC_CATEGORY) - static const boost::system::detail::std_category generic_instance( this, 0x1F4D3 ); - return generic_instance; + static const boost::system::detail::std_category generic_instance( this, 0x1F4D3 ); + return generic_instance; #else - return std::generic_category(); + return std::generic_category(); #endif } @@ -126,47 +161,22 @@ inline error_category::operator std::error_category const & () const // This condition must be the same as the one in error_code.hpp #if defined(BOOST_SYSTEM_AVOID_STD_SYSTEM_CATEGORY) - static const boost::system::detail::std_category system_instance( this, 0x1F4D7 ); - return system_instance; + static const boost::system::detail::std_category system_instance( this, 0x1F4D7 ); + return system_instance; #else - return std::system_category(); + return std::system_category(); #endif } - detail::std_category* p = ps_.load( std::memory_order_acquire ); - - if( p != 0 ) + if( sc_init_.load( std::memory_order_acquire ) == 0 ) { - return *p; + init_stdcat(); } - // One `std_category` object is allocated for every - // user-defined `error_category` that is converted to - // `std::error_category`. - // - // This one-time allocation will show up on leak checkers. - // That's unavoidable. There is no way to deallocate the - // `std_category` object because first, `error_category` - // is a literal type (so it can't have a destructor) and - // second, `error_category` needs to be usable during program - // shutdown. - // - // https://github.com/boostorg/system/issues/78 - - detail::std_category* q = new detail::std_category( this, 0 ); - - if( ps_.compare_exchange_strong( p, q, std::memory_order_release, std::memory_order_acquire ) ) - { - return *q; - } - else - { - delete q; - return *p; - } + return *reinterpret_cast( stdcat_ ); } } // namespace system