2
0
mirror of https://github.com/boostorg/leaf.git synced 2026-01-19 04:22:08 +00:00

- Refactored TLS implementation, including Win32 support across DLL bondaries

- on_error now allocates dynamic storage (if enabled) before the stack unwind
This commit is contained in:
Emil Dotchevski
2025-12-15 00:35:41 -05:00
parent cd9eb89798
commit f8bc875500
14 changed files with 128 additions and 182 deletions

View File

@@ -31,30 +31,32 @@ namespace tls
// This function may not fail.
unsigned read_current_error_id() noexcept;
// Write p to the TLS for T. The TLS may be allocated dynamically on the
// first call to write_ptr_alloc<T>, but subsequent calls must reuse the
// same TLS.
// Reserve TLS storage for T. The TLS may be allocated dynamically on the
// first call to reserve_ptr<T>, but subsequent calls must reuse the same
// TLS. On platforms where allocation is not needed, this function is
// still defined but does nothing.
//
// This function may throw on allocation failure.
template <class T>
void write_ptr_alloc( T * p );
void reserve_ptr();
// Write p to the TLS previously allocated for T by a successful call to
// write_ptr_alloc<T>.
// Write p to the TLS previously reserved for T by a call to reserve_ptr<T>.
// It is illegal to call write_ptr<T> without a prior successful call to
// reserve_ptr<T>.
//
// This function may not fail.
template <class T>
void write_ptr( T * p ) noexcept;
// Read the T * value previously written in the TLS for T. Returns nullptr
// if TLS for T has not yet been allocated.
// if TLS for T has not yet been reserved.
//
// This function may not fail.
template <class T>
T * read_ptr() noexcept;
}
} // namespace tls
} }
} } // namespace boost::leaf
#if defined(BOOST_LEAF_TLS_FREERTOS)
# include <boost/leaf/config/tls_freertos.hpp>

View File

@@ -100,16 +100,16 @@ namespace detail
BOOST_LEAF_CFG_TLS_INDEX_TYPE tls_index<T>::idx = BOOST_LEAF_CFG_TLS_ARRAY_START_INDEX + 1;
template <class T>
struct BOOST_LEAF_SYMBOL_VISIBLE alloc_tls_index
struct BOOST_LEAF_SYMBOL_VISIBLE reserve_tls_index
{
static BOOST_LEAF_CFG_TLS_INDEX_TYPE const idx;
};
template <class T>
BOOST_LEAF_CFG_TLS_INDEX_TYPE const alloc_tls_index<T>::idx = tls_index<T>::idx = index_counter<>::next<T>();
}
BOOST_LEAF_CFG_TLS_INDEX_TYPE const reserve_tls_index<T>::idx = tls_index<T>::idx = index_counter<>::next<T>();
} // namespace detail
} }
} } // namespace boost::leaf
////////////////////////////////////////
@@ -137,12 +137,9 @@ namespace tls
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE void write_ptr_alloc( T * p )
BOOST_LEAF_ALWAYS_INLINE void reserve_ptr()
{
int tls_idx = detail::alloc_tls_index<T>::idx;
--tls_idx;
write_void_ptr(tls_idx, p);
BOOST_LEAF_ASSERT(read_void_ptr(tls_idx) == p);
(void) detail::reserve_tls_index<T>::idx;
}
template <class T>
@@ -165,6 +162,8 @@ namespace tls
--tls_idx;
return reinterpret_cast<T *>(read_void_ptr(tls_idx));
}
}
} // namespace tls
} } // namespace boost::leaf
#endif // #ifndef BOOST_LEAF_CONFIG_TLS_ARRAY_HPP_INCLUDED

View File

@@ -38,15 +38,17 @@ namespace detail
template <class T>
thread_local T * ptr<T>::p;
struct current_error_id_storage
template <class=void>
struct BOOST_LEAF_SYMBOL_VISIBLE current_error_id_storage
{
static thread_local unsigned x;
};
thread_local unsigned current_error_id_storage::x;
}
template <class T>
thread_local unsigned current_error_id_storage<T>::x;
} // namespace detail
} }
} } // namespace boost::leaf
////////////////////////////////////////
@@ -63,30 +65,7 @@ namespace tls
BOOST_LEAF_ALWAYS_INLINE void write_current_error_id( unsigned x ) noexcept
{
detail::current_error_id_storage::x = x;
}
BOOST_LEAF_ALWAYS_INLINE unsigned read_current_error_id() noexcept
{
return detail::current_error_id_storage::x;
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE void write_ptr_alloc( T * p )
{
detail::ptr<T>::p = p;
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE void write_ptr( T * p ) noexcept
{
detail::ptr<T>::p = p;
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE T * read_ptr() noexcept
{
return detail::ptr<T>::p;
detail::current_error_id_storage<>::x = x;
}
BOOST_LEAF_ALWAYS_INLINE unsigned read_current_error_id() noexcept

View File

@@ -42,9 +42,9 @@ namespace detail
template <class T>
unsigned current_error_id_storage<T>::x = 0;
}
} // namespace detail
} }
} } // namespace boost::leaf
////////////////////////////////////////
@@ -69,29 +69,6 @@ namespace tls
return detail::current_error_id_storage<>::x;
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE void write_ptr_alloc( T * p )
{
detail::ptr<T>::p = p;
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE void write_ptr( T * p ) noexcept
{
detail::ptr<T>::p = p;
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE T * read_ptr() noexcept
{
return detail::ptr<T>::p;
}
BOOST_LEAF_ALWAYS_INLINE unsigned read_current_error_id() noexcept
{
return detail::current_error_id_storage<>::x;
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE void reserve_ptr()
{

View File

@@ -211,7 +211,7 @@ namespace detail
{
return error_id_storage_;
}
};
}; // class slot_map
class module_state
{
@@ -300,6 +300,7 @@ namespace detail
}
else if (dwReason == DLL_PROCESS_DETACH)
{
BOOST_LEAF_ASSERT(sm_ || tls_failures_);
if (sm_)
{
sm_->release();
@@ -307,7 +308,7 @@ namespace detail
}
}
}
};
}; // class module_state
template<int = 0>
struct module
@@ -334,7 +335,7 @@ namespace detail
#ifdef _MSC_VER
#pragma section(".CRT$XLB", long, read)
#pragma data_seg(push, ".CRT$XLB")
extern "C" PIMAGE_TLS_CALLBACK boost_leaf_tls_callback = tls_callback;
extern "C" __declspec(selectany) PIMAGE_TLS_CALLBACK boost_leaf_tls_callback = tls_callback;
#pragma data_seg(pop)
#ifdef _WIN64
#pragma comment(linker, "/INCLUDE:boost_leaf_tls_callback")
@@ -342,11 +343,11 @@ namespace detail
#pragma comment(linker, "/INCLUDE:_boost_leaf_tls_callback")
#endif
#elif defined(__GNUC__)
extern "C" __attribute__((used)) PIMAGE_TLS_CALLBACK boost_leaf_tls_callback __attribute__((section(".CRT$XLB"))) = tls_callback;
extern "C" __attribute__((used, weak)) PIMAGE_TLS_CALLBACK boost_leaf_tls_callback __attribute__((section(".CRT$XLB"))) = tls_callback;
#endif
}
} // namespace detail
} }
} } // namespace boost::leaf
////////////////////////////////////////
@@ -377,14 +378,11 @@ namespace tls
}
template <class T>
BOOST_LEAF_ALWAYS_INLINE void write_ptr_alloc(T * p)
BOOST_LEAF_ALWAYS_INLINE void reserve_ptr()
{
using namespace detail;
thread_local DWORD const cached_slot = module<>::state.sm().get(type_hash<T>());
DWORD slot = cached_slot;
BOOST_LEAF_ASSERT(slot != TLS_OUT_OF_INDEXES);
BOOL r = TlsSetValue(slot, p);
BOOST_LEAF_ASSERT(r), (void) r;
BOOST_LEAF_ASSERT(cached_slot != TLS_OUT_OF_INDEXES), (void) cached_slot;
}
template <class T>
@@ -412,8 +410,8 @@ namespace tls
BOOST_LEAF_ASSERT(GetLastError() == ERROR_SUCCESS);
return static_cast<T *>(value);
}
}
} // namespace tls
} }
} } // namespace boost::leaf
#endif // BOOST_LEAF_CONFIG_TLS_WIN32_HPP_INCLUDED
#endif // #ifndef BOOST_LEAF_CONFIG_TLS_WIN32_HPP_INCLUDED

View File

@@ -180,7 +180,7 @@ namespace detail
template <int I, class Tup>
struct tuple_for_each
{
BOOST_LEAF_CONSTEXPR static void activate( Tup & tup )
BOOST_LEAF_CONSTEXPR static void activate( Tup & tup ) noexcept
{
static_assert(!std::is_same<error_info, typename std::decay<decltype(std::get<I-1>(tup))>::type>::value, "Bug in LEAF: context type deduction");
tuple_for_each<I-1,Tup>::activate(tup);
@@ -286,7 +286,7 @@ class context
raii_deactivator & operator=( raii_deactivator const & ) = delete;
context * ctx_;
public:
explicit BOOST_LEAF_CONSTEXPR BOOST_LEAF_ALWAYS_INLINE raii_deactivator(context & ctx):
explicit BOOST_LEAF_CONSTEXPR BOOST_LEAF_ALWAYS_INLINE raii_deactivator(context & ctx) noexcept:
ctx_(ctx.is_active() ? nullptr : &ctx)
{
if( ctx_ )
@@ -335,7 +335,7 @@ public:
return tup_;
}
BOOST_LEAF_CONSTEXPR void activate()
BOOST_LEAF_CONSTEXPR void activate() noexcept
{
using namespace detail;
BOOST_LEAF_ASSERT(!is_active());
@@ -396,7 +396,7 @@ public:
template <class R, class... H>
BOOST_LEAF_CONSTEXPR R handle_error( error_id, H && ... );
friend BOOST_LEAF_CONSTEXPR BOOST_LEAF_ALWAYS_INLINE raii_deactivator activate_context(context & ctx)
friend BOOST_LEAF_CONSTEXPR BOOST_LEAF_ALWAYS_INLINE raii_deactivator activate_context(context & ctx) noexcept
{
return raii_deactivator(ctx);
}

View File

@@ -140,10 +140,10 @@ namespace detail
BOOST_LEAF_ASSERT(tls::read_ptr<slot<E>>() != this);
}
void activate()
void activate() noexcept
{
prev_ = tls::read_ptr<slot<E>>();
tls::write_ptr_alloc<slot<E>>(this);
tls::write_ptr<slot<E>>(this);
}
void deactivate() const noexcept
@@ -179,18 +179,12 @@ namespace detail
{
class preloaded_base;
template <class E>
struct capturing_slot_node_allocator;
class dynamic_allocator:
capture_list
{
dynamic_allocator( dynamic_allocator const & ) = delete;
dynamic_allocator & operator=( dynamic_allocator const & ) = delete;
template <class>
friend struct capturing_slot_node_allocator;
preloaded_base * preloaded_list_;
class capturing_node:
@@ -325,7 +319,7 @@ namespace detail
BOOST_LEAF_ASSERT(last_ != nullptr);
BOOST_LEAF_ASSERT(*last_ == nullptr);
BOOST_LEAF_ASSERT(tls::read_ptr<slot<E>>() == nullptr);
capturing_slot_node<E> * csn = capturing_slot_node_allocator<E>::new_(last_);
capturing_slot_node<E> * csn = new capturing_slot_node<E>(last_);
csn->activate();
return csn;
}
@@ -365,21 +359,6 @@ namespace detail
using capture_list::print;
}; // class dynamic_allocator
template <class E>
struct capturing_slot_node_allocator
{
template <class... A>
static dynamic_allocator::capturing_slot_node<E> * new_( A && ... a )
{
return new dynamic_allocator::capturing_slot_node<E>(std::forward<A>(a)...);
}
static void delete_( dynamic_allocator::capturing_slot_node<E> * p ) noexcept
{
delete p;
}
};
template <>
class slot<dynamic_allocator>
{
@@ -389,64 +368,30 @@ namespace detail
dynamic_allocator da_;
slot * prev_;
template <class E>
inline void dynamic_load_( int err_id, E && e )
{
if( slot<dynamic_allocator> * sl = tls::read_ptr<slot<dynamic_allocator>>() )
if( dynamic_allocator * c = sl->has_value_any_key() )
c->dynamic_load(err_id, std::forward<E>(e));
else
sl->load(err_id).dynamic_load(err_id, std::forward<E>(e));
}
public:
template <class E, class F>
inline void dynamic_accumulate_( int err_id, F && f )
{
if( slot<dynamic_allocator> * sl = tls::read_ptr<slot<dynamic_allocator>>() )
if( dynamic_allocator * c = sl->has_value(err_id) )
(void) std::forward<F>(f)(c->dynamic_load(err_id, E{}));
else
(void) std::forward<F>(f)(sl->load(err_id).dynamic_load(err_id, E{}));
}
template <bool OnError, class E>
inline void dynamic_load( int err_id, E && e ) noexcept(OnError)
{
#ifndef BOOST_LEAF_NO_EXCEPTIONS
if( OnError )
slot() noexcept:
prev_(nullptr)
{
try
{
dynamic_load_(err_id, std::forward<E>(e));
}
catch(...)
{
}
tls::reserve_ptr<slot<dynamic_allocator>>();
}
else
#endif
dynamic_load_(err_id, std::forward<E>(e));
}
template <bool OnError, class E, class F>
inline void dynamic_load_accumulate( int err_id, F && f ) noexcept(OnError)
{
#ifndef BOOST_LEAF_NO_EXCEPTIONS
if( OnError )
slot( slot && x ) noexcept:
da_(std::move(x.da_)),
prev_(nullptr)
{
try
{
dynamic_accumulate_<E>(err_id, std::forward<F>(f));
}
catch(...)
{
}
BOOST_LEAF_ASSERT(x.prev_ == nullptr);
}
~slot() noexcept
{
BOOST_LEAF_ASSERT(tls::read_ptr<slot<dynamic_allocator>>() != this);
}
dynamic_allocator const & get() const noexcept
{
return da_;
}
else
#endif
dynamic_accumulate_<E>(err_id, std::forward<F>(f));
}
}
dynamic_allocator & get() noexcept
{
@@ -599,6 +544,38 @@ namespace detail
namespace detail
{
#if BOOST_LEAF_CFG_CAPTURE
class preloaded_base
{
protected:
preloaded_base() noexcept:
next_(
[]( preloaded_base * this_ ) -> preloaded_base *
{
if( dynamic_allocator * da = get_dynamic_allocator() )
return da->link_preloaded_item(this_);
return nullptr;
}(this))
{
}
~preloaded_base() noexcept
{
if( dynamic_allocator * da = get_dynamic_allocator() )
da->unlink_preloaded_item(next_);
else
BOOST_LEAF_ASSERT(next_ == nullptr);
}
public:
preloaded_base * const next_;
virtual void reserve( dynamic_allocator & ) const = 0;
};
#endif // #if BOOST_LEAF_CFG_CAPTURE
inline int current_id() noexcept
{
unsigned id = tls::read_current_error_id();

View File

@@ -261,6 +261,13 @@ namespace detail
bool moved_ = false;
#endif
#if BOOST_LEAF_CFG_CAPTURE
void reserve( dynamic_allocator & da ) const override
{
tuple_for_each_preload<sizeof...(Item),decltype(p_)>::reserve(p_,da);
}
#endif
public:
BOOST_LEAF_CONSTEXPR explicit preloaded( Item && ... i ):

View File

@@ -238,6 +238,13 @@ if option_enable_unit_tests
so_dll_test_lib2 = shared_library('so_dll_test_lib2', 'test/so_dll_lib2.cpp', dependencies: [leaf, dep_so_dll], gnu_symbol_visibility: 'hidden')
test('so_dll_test', executable('so_dll_test', 'test/so_dll_test.cpp', dependencies: [leaf, dep_so_dll], link_with: [so_dll_test_lib1, so_dll_test_lib2]))
# Test that the DLL test works when linked statically as well.
# This is to verify that we don't get multiply defined symbol link errors when tls_win32.hpp is included in multiple compilation units..
dep_so_dll_static = declare_dependency(compile_args: ['-DBOOST_LEAF_SO_DLL_TEST_STATIC'])
so_dll_static_lib1 = static_library('so_dll_static_lib1', 'test/so_dll_lib1.cpp', dependencies: [leaf, dep_so_dll, dep_so_dll_static])
so_dll_static_lib2 = static_library('so_dll_static_lib2', 'test/so_dll_lib2.cpp', dependencies: [leaf, dep_so_dll, dep_so_dll_static])
test('so_dll_static_test', executable('so_dll_static_test', 'test/so_dll_test.cpp', dependencies: [leaf, dep_so_dll, dep_so_dll_static], link_with: [so_dll_static_lib1, so_dll_static_lib2]))
endif
#################################

View File

@@ -137,6 +137,10 @@ lib so_dll_lib1 : so_dll_lib1.cpp : <link>shared <visibility>hidden <target-os>w
lib so_dll_lib2 : so_dll_lib2.cpp : <link>shared <visibility>hidden <target-os>windows:<define>BOOST_LEAF_CFG_WIN32=2 : : <target-os>windows:<define>BOOST_LEAF_CFG_WIN32=2 ;
run so_dll_test.cpp so_dll_lib1 so_dll_lib2 : : : <target-os>windows:<define>BOOST_LEAF_CFG_WIN32=2 ;
lib so_dll_static_lib1 : so_dll_lib1.cpp : <link>static <define>BOOST_LEAF_SO_DLL_TEST_STATIC <target-os>windows:<define>BOOST_LEAF_CFG_WIN32=2 ;
lib so_dll_static_lib2 : so_dll_lib2.cpp : <link>static <define>BOOST_LEAF_SO_DLL_TEST_STATIC <target-os>windows:<define>BOOST_LEAF_CFG_WIN32=2 ;
run so_dll_test.cpp so_dll_static_lib1 so_dll_static_lib2 : : : <define>BOOST_LEAF_SO_DLL_TEST_STATIC <target-os>windows:<define>BOOST_LEAF_CFG_WIN32=2 : so_dll_static_test ;
compile-fail _compile-fail-arg_boost_error_info_1.cpp ;
compile-fail _compile-fail-arg_boost_error_info_2.cpp ;
compile-fail _compile-fail-arg_catch_1.cpp ;

View File

@@ -8,19 +8,17 @@
#include "so_dll_test.hpp"
#ifdef _WIN32
# ifdef BOOST_LEAF_SO_DLL_TEST_BUILDING_LIB1
# ifdef BOOST_LEAF_SO_DLL_TEST_STATIC
# define BOOST_LEAF_SO_DLL_TEST_LIB1_API
# elif defined(BOOST_LEAF_SO_DLL_TEST_BUILDING_LIB1)
# define BOOST_LEAF_SO_DLL_TEST_LIB1_API __declspec(dllexport)
# else
# define BOOST_LEAF_SO_DLL_TEST_LIB1_API __declspec(dllimport)
# endif
#else
# define BOOST_LEAF_SO_DLL_TEST_LIB1_API BOOST_LEAF_SYMBOL_VISIBLE
# define BOOST_LEAF_SO_DLL_TEST_LIB1_API [[gnu::visibility("default")]]
#endif
namespace boost { namespace leaf {
template <class T> class result;
} }
BOOST_LEAF_SO_DLL_TEST_LIB1_API boost::leaf::result<void> hidden_result1();
#ifndef BOOST_LEAF_NO_EXCEPTIONS

View File

@@ -8,19 +8,17 @@
#include "so_dll_test.hpp"
#ifdef _WIN32
# ifdef BOOST_LEAF_SO_DLL_TEST_BUILDING_LIB2
# ifdef BOOST_LEAF_SO_DLL_TEST_STATIC
# define BOOST_LEAF_SO_DLL_TEST_LIB2_API
# elif defined(BOOST_LEAF_SO_DLL_TEST_BUILDING_LIB2)
# define BOOST_LEAF_SO_DLL_TEST_LIB2_API __declspec(dllexport)
# else
# define BOOST_LEAF_SO_DLL_TEST_LIB2_API __declspec(dllimport)
# endif
#else
# define BOOST_LEAF_SO_DLL_TEST_LIB2_API BOOST_LEAF_SYMBOL_VISIBLE
# define BOOST_LEAF_SO_DLL_TEST_LIB2_API [[gnu::visibility("default")]]
#endif
namespace boost { namespace leaf {
template <class T> class result;
} }
BOOST_LEAF_SO_DLL_TEST_LIB2_API boost::leaf::result<void> hidden_result2();
#ifndef BOOST_LEAF_NO_EXCEPTIONS

View File

@@ -126,7 +126,7 @@ int test_catch(void (*f)())
return 2;
}
}
#endif
#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS
void test_single_thread()
{
@@ -138,7 +138,7 @@ void test_single_thread()
BOOST_TEST_EQ(test_catch(hidden_throw1), 0);
BOOST_TEST_EQ(test_exception(hidden_throw2, true), 0);
BOOST_TEST_EQ(test_catch(hidden_throw2), 0);
#endif
#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS
}
void test_multithreaded()
@@ -212,7 +212,7 @@ void test_multithreaded()
for (auto & f : futures)
BOOST_TEST_EQ(f.get(), 0);
}
#endif
#endif // #ifndef BOOST_LEAF_NO_EXCEPTIONS
}
int main()

View File

@@ -14,7 +14,7 @@
// Note, under BOOST_LEAF_CFG_WIN32==2 (using Win32 TLS) we do not need
// import/export for error types. We still need default visibility on POSIX.
template <int Tag>
struct BOOST_LEAF_SYMBOL_VISIBLE my_info
struct [[gnu::visibility("default")]] my_info
{
int value;