forked from boostorg/system
Rework conversion to std::error_category to not allocate (closes #78)
This commit is contained in:
@@ -48,6 +48,11 @@ class std_category;
|
|||||||
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
|
||||||
#endif
|
#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
|
class BOOST_SYMBOL_VISIBLE error_category
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
@@ -76,13 +81,21 @@ private:
|
|||||||
|
|
||||||
boost::ulong_long_type id_;
|
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)
|
#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR)
|
||||||
|
|
||||||
mutable std::atomic< boost::system::detail::std_category* > ps_;
|
mutable std::atomic< unsigned > sc_init_;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
boost::system::detail::std_category* ps_;
|
unsigned sc_init_;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -103,11 +116,11 @@ protected:
|
|||||||
|
|
||||||
#endif
|
#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 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR)
|
#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR)
|
||||||
|
|
||||||
|
void init_stdcat() const;
|
||||||
|
|
||||||
# if defined(__SUNPRO_CC) // trailing __global is not supported
|
# if defined(__SUNPRO_CC) // trailing __global is not supported
|
||||||
operator std::error_category const & () const;
|
operator std::error_category const & () const;
|
||||||
# else
|
# else
|
||||||
operator std::error_category const & () const BOOST_SYMBOL_VISIBLE;
|
operator std::error_category const & () const BOOST_SYMBOL_VISIBLE;
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if defined(BOOST_MSVC)
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
#if ( defined( BOOST_GCC ) && BOOST_GCC >= 40600 ) || defined( BOOST_CLANG )
|
#if ( defined( BOOST_GCC ) && BOOST_GCC >= 40600 ) || defined( BOOST_CLANG )
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
|
@@ -98,12 +98,47 @@ inline char const * error_category::message( int ev, char * buffer, std::size_t
|
|||||||
#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR)
|
#if defined(BOOST_SYSTEM_HAS_SYSTEM_ERROR)
|
||||||
|
|
||||||
#include <boost/system/detail/std_category_impl.hpp>
|
#include <boost/system/detail/std_category_impl.hpp>
|
||||||
|
#include <mutex>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
namespace boost
|
namespace boost
|
||||||
{
|
{
|
||||||
namespace system
|
namespace system
|
||||||
{
|
{
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
|
||||||
|
template<class = void> struct stdcat_mx_holder
|
||||||
|
{
|
||||||
|
static std::mutex mx_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T> std::mutex stdcat_mx_holder<T>::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<std::mutex> lk( boost::system::detail::stdcat_mx_holder<>::mx_ );
|
||||||
|
|
||||||
|
if( sc_init_.load( std::memory_order_acquire ) == 0 )
|
||||||
|
{
|
||||||
|
::new( static_cast<void*>( 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
|
inline error_category::operator std::error_category const & () const
|
||||||
{
|
{
|
||||||
if( id_ == detail::generic_category_id )
|
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
|
// This condition must be the same as the one in error_condition.hpp
|
||||||
#if defined(BOOST_SYSTEM_AVOID_STD_GENERIC_CATEGORY)
|
#if defined(BOOST_SYSTEM_AVOID_STD_GENERIC_CATEGORY)
|
||||||
|
|
||||||
static const boost::system::detail::std_category generic_instance( this, 0x1F4D3 );
|
static const boost::system::detail::std_category generic_instance( this, 0x1F4D3 );
|
||||||
return generic_instance;
|
return generic_instance;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
return std::generic_category();
|
return std::generic_category();
|
||||||
|
|
||||||
#endif
|
#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
|
// This condition must be the same as the one in error_code.hpp
|
||||||
#if defined(BOOST_SYSTEM_AVOID_STD_SYSTEM_CATEGORY)
|
#if defined(BOOST_SYSTEM_AVOID_STD_SYSTEM_CATEGORY)
|
||||||
|
|
||||||
static const boost::system::detail::std_category system_instance( this, 0x1F4D7 );
|
static const boost::system::detail::std_category system_instance( this, 0x1F4D7 );
|
||||||
return system_instance;
|
return system_instance;
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
return std::system_category();
|
return std::system_category();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
detail::std_category* p = ps_.load( std::memory_order_acquire );
|
if( sc_init_.load( std::memory_order_acquire ) == 0 )
|
||||||
|
|
||||||
if( p != 0 )
|
|
||||||
{
|
{
|
||||||
return *p;
|
init_stdcat();
|
||||||
}
|
}
|
||||||
|
|
||||||
// One `std_category` object is allocated for every
|
return *reinterpret_cast<boost::system::detail::std_category const*>( stdcat_ );
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace system
|
} // namespace system
|
||||||
|
Reference in New Issue
Block a user