diff --git a/CMakeLists.txt b/CMakeLists.txt index a7da0d3bb..fb2c29291 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,6 +157,7 @@ set(HEADERS_COMPONENT devel) set(SOURCES src/init.cpp src/hp.cpp + src/hp_thread_local.cpp src/dhp.cpp src/urcu_gp.cpp src/urcu_sh.cpp diff --git a/cds/gc/hp.h b/cds/gc/hp.h index 0d5b942c4..e8202bf1d 100644 --- a/cds/gc/hp.h +++ b/cds/gc/hp.h @@ -307,6 +307,8 @@ namespace cds { namespace gc { } }; + static stat const& postmortem_statistics(); + //@cond /// Per-thread data struct thread_data { @@ -356,6 +358,8 @@ namespace cds { namespace gc { //@cond /// Hazard Pointer SMR (Safe Memory Reclamation) class basic_smr { + template + friend class generic_smr; struct thread_record; public: @@ -472,13 +476,6 @@ namespace cds { namespace gc { } } - /// Returns thread-local data for the current thread - static CDS_EXPORT_API thread_data *tls(); - - static CDS_EXPORT_API void attach_thread(); - - static CDS_EXPORT_API void detach_thread(); - /// Get internal statistics CDS_EXPORT_API void statistics(stat &st); @@ -579,14 +576,62 @@ namespace cds { namespace gc { }; //@endcond + class DefaultTLSManager { + public: + static CDS_EXPORT_API thread_data* getTLS(); + static CDS_EXPORT_API void setTLS(thread_data*); + }; + + class StrangeTLSManager { + public: + static CDS_EXPORT_API thread_data* getTLS(); + static CDS_EXPORT_API void setTLS(thread_data*); + }; + + class HeapTLSManager { + public: + static CDS_EXPORT_API thread_data* getTLS(); + static CDS_EXPORT_API void setTLS(thread_data*); + }; + + template + class generic_smr : public basic_smr { + public: + /// Returns thread-local data for the current thread + static CDS_EXPORT_API thread_data* tls() + { + thread_data* data = TLSManager::getTLS(); + assert( data != nullptr ); + return data; + } + + static CDS_EXPORT_API void attach_thread() + { + thread_data* data = TLSManager::getTLS(); + if ( !data ) + TLSManager::setTLS(reinterpret_cast(instance().alloc_thread_data())); + } + + static CDS_EXPORT_API void detach_thread() + { + thread_data* rec = TLSManager::getTLS(); + if ( rec ) { + TLSManager::setTLS(nullptr); + instance().free_thread_data(reinterpret_cast( rec ), true ); + } + } + }; + + + } // namespace details //@cond // for backward compatibility - typedef details::basic_smr smr; + typedef details::generic_smr smr; typedef smr GarbageCollector; //@endcond - } // namespace hp + } // namespace cds::gc::hp namespace details { /// Hazard Pointer SMR (Safe Memory Reclamation) @@ -604,7 +649,8 @@ namespace cds { namespace gc { by contructing \p %cds::gc::HP object in beginning of your \p main(). See \ref cds_how_to_use "How to use" section for details how to apply SMR schema. */ - class HP + template + class generic_HP { public: /// Native guarded pointer type @@ -649,7 +695,7 @@ namespace cds { namespace gc { @warning Can throw \p not_enough_hazard_ptr if internal hazard pointer objects are exhausted. */ Guard() - : guard_( hp::smr::tls()->hazards_.alloc()) + : guard_( hp::details::generic_smr::tls()->hazards_.alloc()) {} /// Initilalizes an unlinked guard i.e. the guard contains no hazard pointer. Used for move semantics support @@ -699,14 +745,14 @@ namespace cds { namespace gc { void link() { if ( !guard_ ) - guard_ = hp::smr::tls()->hazards_.alloc(); + guard_ = hp::details::generic_smr::tls()->hazards_.alloc(); } /// Unlinks the guard from internal hazard pointer; the guard becomes in unlinked state void unlink() { if ( guard_ ) { - hp::smr::tls()->hazards_.free( guard_ ); + hp::details::generic_smr::tls()->hazards_.free( guard_ ); guard_ = nullptr; } } @@ -774,7 +820,7 @@ namespace cds { namespace gc { assert( guard_ != nullptr ); guard_->set( p ); - hp::smr::tls()->sync(); + hp::details::generic_smr::tls()->sync(); return p; } @@ -871,7 +917,7 @@ namespace cds { namespace gc { /// Default ctor allocates \p Count hazard pointers GuardArray() { - hp::smr::tls()->hazards_.alloc( guards_ ); + hp::details::generic_smr::tls()->hazards_.alloc( guards_ ); } /// Move ctor is prohibited @@ -889,7 +935,7 @@ namespace cds { namespace gc { /// Frees allocated hazard pointers ~GuardArray() { - hp::smr::tls()->hazards_.free( guards_ ); + hp::details::generic_smr::tls()->hazards_.free( guards_ ); } /// Protects a pointer of type \p atomic @@ -945,7 +991,7 @@ namespace cds { namespace gc { assert( nIndex < capacity()); guards_.set( nIndex, p ); - hp::smr::tls()->sync(); + hp::details::generic_smr::tls()->sync(); return p; } @@ -1202,13 +1248,13 @@ namespace cds { namespace gc { void alloc_guard() { if ( !guard_ ) - guard_ = hp::smr::tls()->hazards_.alloc(); + guard_ = hp::details::generic_smr::tls()->hazards_.alloc(); } void free_guard() { if ( guard_ ) { - hp::smr::tls()->hazards_.free( guard_ ); + hp::details::generic_smr::tls()->hazards_.free( guard_ ); guard_ = nullptr; } } @@ -1240,14 +1286,14 @@ namespace cds { namespace gc { - \p nMaxRetiredPtrCount - capacity of array of retired pointers for each thread. Must be greater than nHazardPtrCount * nMaxThreadCount . Default is 2 * nHazardPtrCount * nMaxThreadCount . */ - HP( + generic_HP( size_t nHazardPtrCount = 0, ///< Hazard pointer count per thread size_t nMaxThreadCount = 0, ///< Max count of simultaneous working thread in your application size_t nMaxRetiredPtrCount = 0, ///< Capacity of the array of retired objects for the thread scan_type nScanType = scan_type::inplace ///< Scan type (see \p scan_type enum) ) { - hp::smr::construct( + hp::details::generic_smr::construct( nHazardPtrCount, nMaxThreadCount, nMaxRetiredPtrCount, @@ -1261,9 +1307,9 @@ namespace cds { namespace gc { use CDS data structures based on \p %cds::gc::HP. Usually, %HP object is destroyed at the end of your \p main(). */ - ~HP() + ~generic_HP() { - hp::smr::destruct( true ); + hp::details::generic_smr::destruct( true ); } /// Checks that required hazard pointer count \p nCountNeeded is less or equal then max hazard pointer count @@ -1272,7 +1318,7 @@ namespace cds { namespace gc { */ static void check_available_guards( size_t nCountNeeded ) { - hp::smr::check_hazard_ptr_count( nCountNeeded ); + hp::details::generic_smr::check_hazard_ptr_count( nCountNeeded ); } /// Set memory management functions @@ -1289,25 +1335,25 @@ namespace cds { namespace gc { void( *free_func )( void * p ) ///< \p free() function ) { - hp::smr::set_memory_allocator( alloc_func, free_func ); + hp::details::generic_smr::set_memory_allocator( alloc_func, free_func ); } /// Returns max Hazard Pointer count static size_t max_hazard_count() { - return hp::smr::instance().get_hazard_ptr_count(); + return hp::details::generic_smr::instance().get_hazard_ptr_count(); } /// Returns max count of thread static size_t max_thread_count() { - return hp::smr::instance().get_max_thread_count(); + return hp::details::generic_smr::instance().get_max_thread_count(); } /// Returns capacity of retired pointer array static size_t retired_array_capacity() { - return hp::smr::instance().get_max_retired_ptr_count(); + return hp::details::generic_smr::instance().get_max_retired_ptr_count(); } /// Retire pointer \p p with function \p func @@ -1319,9 +1365,9 @@ namespace cds { namespace gc { template static void retire( T * p, void( *func )( void * )) { - hp::details::thread_data* rec = hp::smr::tls(); + hp::details::thread_data* rec = hp::details::generic_smr::tls(); if ( !rec->retired_.push( hp::details::retired_ptr( p, func ))) - hp::smr::instance().scan( rec ); + hp::details::generic_smr::instance().scan( rec ); } /// Retire pointer \p p with functor of type \p Disposer @@ -1376,20 +1422,20 @@ namespace cds { namespace gc { template static void retire( T * p ) { - if ( !hp::smr::tls()->retired_.push( hp::details::retired_ptr( p, +[]( void* p ) { Disposer()( static_cast( p )); }))) + if ( !hp::details::generic_smr::tls()->retired_.push( hp::details::retired_ptr( p, +[]( void* p ) { Disposer()( static_cast( p )); }))) scan(); } /// Get current scan strategy static scan_type getScanType() { - return static_cast( hp::smr::instance().get_scan_type()); + return static_cast( hp::details::generic_smr::instance().get_scan_type()); } /// Checks if Hazard Pointer GC is constructed and may be used static bool isUsed() { - return hp::smr::isUsed(); + return hp::details::generic_smr::isUsed(); } /// Forces SMR call for current thread @@ -1398,7 +1444,7 @@ namespace cds { namespace gc { */ static void scan() { - hp::smr::instance().scan( hp::smr::tls()); + hp::details::generic_smr::instance().scan( hp::details::generic_smr::tls()); } /// Synonym for \p scan() @@ -1416,7 +1462,7 @@ namespace cds { namespace gc { */ static void statistics( stat& st ) { - hp::smr::instance().statistics( st ); + hp::details::generic_smr::instance().statistics( st ); } /// Returns post-mortem statistics @@ -1462,11 +1508,14 @@ namespace cds { namespace gc { } \endcode */ - CDS_EXPORT_API static stat const& postmortem_statistics(); + static stat const& postmortem_statistics() { + return cds::gc::hp::details::postmortem_statistics(); + } }; } // namespace cds::gc::details - typedef details::HP HP; + // for backward compatibility + typedef details::generic_HP HP; typedef hp::details::basic_smr smr; }} // namespace cds::gc diff --git a/src/hp.cpp b/src/hp.cpp index 94d0c50db..6b88686b6 100644 --- a/src/hp.cpp +++ b/src/hp.cpp @@ -120,12 +120,6 @@ namespace cds { namespace gc { namespace hp { namespace details { /*static*/ CDS_EXPORT_API basic_smr* basic_smr::instance_ = nullptr; thread_local thread_data* tls_ = nullptr; - /*static*/ CDS_EXPORT_API thread_data* basic_smr::tls() - { - assert( tls_ != nullptr ); - return tls_; - } - struct basic_smr::thread_record: thread_data { // next hazard ptr record in list @@ -310,22 +304,6 @@ namespace cds { namespace gc { namespace hp { namespace details { } } - /*static*/ CDS_EXPORT_API void basic_smr::attach_thread() - { - if ( !tls_ ) - tls_ = instance().alloc_thread_data(); - } - - /*static*/ CDS_EXPORT_API void basic_smr::detach_thread() - { - thread_data* rec = tls_; - if ( rec ) { - tls_ = nullptr; - instance().free_thread_data( static_cast( rec ), true ); - } - } - - CDS_EXPORT_API void basic_smr::inplace_scan(thread_data* pThreadRec ) { thread_record* pRec = static_cast( pThreadRec ); @@ -539,10 +517,9 @@ namespace cds { namespace gc { namespace hp { namespace details { # endif } -}}}} // namespace cds::gc::hp::details - -CDS_EXPORT_API /*static*/ cds::gc::HP::stat const& cds::gc::HP::postmortem_statistics() -{ - return cds::gc::hp::details::s_postmortem_stat; -} + cds::gc::hp::details::stat const& postmortem_statistics() + { + return s_postmortem_stat; + } +}}}} // namespace cds::gc::hp::details diff --git a/src/hp_thread_local.cpp b/src/hp_thread_local.cpp new file mode 100644 index 000000000..76dcf6d07 --- /dev/null +++ b/src/hp_thread_local.cpp @@ -0,0 +1,62 @@ +// Copyright (c) 2020-2020 Alexander Gaev +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#include + +namespace cds { + namespace gc { + namespace hp { + namespace details { + thread_local thread_data *tls_ = nullptr; + thread_local std::pair *tls2_ = new std::pair( + nullptr, + nullptr); + const size_t MAXIMUM_THREAD_ID = 100000; + static thread_data *heap_tls_[MAXIMUM_THREAD_ID]; + + /*static*/ CDS_EXPORT_API thread_data *DefaultTLSManager::getTLS() { + return tls_; + } + + /*static*/ CDS_EXPORT_API void DefaultTLSManager::setTLS(thread_data *new_tls) { + tls_ = new_tls; + } + + /*static*/ CDS_EXPORT_API thread_data *StrangeTLSManager::getTLS() { + if (cds::OS::get_current_thread_id() % 2 == 0) { // % 2 with ThreadId structure? + return tls2_->second; + } else { + return tls2_->first; + } + } + + /*static*/ CDS_EXPORT_API void StrangeTLSManager::setTLS(thread_data *new_tls) { + if (cds::OS::get_current_thread_id() % 2 == 0) { // % 2 with ThreadId structure? + tls2_->second = new_tls; + } else { + tls2_->first = new_tls; + } + } + + /*static*/ CDS_EXPORT_API thread_data *HeapTLSManager::getTLS() { + cds::OS::posix::ThreadId thread_id = cds::OS::get_current_thread_id(); + if (thread_id < 0 || thread_id >= MAXIMUM_THREAD_ID) { + throw std::runtime_error("HeapTLSManager too big thread_id"); + } + return heap_tls_[thread_id]; + } + + /*static*/ CDS_EXPORT_API void HeapTLSManager::setTLS(thread_data *new_tls) { + cds::OS::posix::ThreadId thread_id = cds::OS::get_current_thread_id(); + if (thread_id < 0 || thread_id >= MAXIMUM_THREAD_ID) { + throw std::runtime_error("HeapTLSManager too big thread_id"); + } + heap_tls_[thread_id] = new_tls; + } + } + } + } +}