hp.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. // Copyright (c) 2006-2018 Maxim Khizhinsky
  2. //
  3. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  4. // file LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #include <algorithm>
  6. #include <vector>
  7. #include <cds/gc/hp.h>
  8. #include <cds/os/thread.h>
  9. #include <cds/gc/hp_membar.h>
  10. #if CDS_OS_TYPE == CDS_OS_LINUX
  11. # include <unistd.h>
  12. # include <sys/syscall.h>
  13. // membarrier() was added in Linux 4.3
  14. # if !defined( __NR_membarrier )
  15. # define __NR_membarrier 324
  16. # endif
  17. # ifdef CDS_HAVE_LINUX_MEMBARRIER_H
  18. # include <linux/membarrier.h>
  19. # else
  20. # define MEMBARRIER_CMD_QUERY 0
  21. # define MEMBARRIER_CMD_SHARED (1<<0)
  22. # endif
  23. // linux 4.14+
  24. # define CDS_MEMBARRIER_CMD_PRIVATE_EXPEDITED (1<<3)
  25. # define CDS_MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED (1<<4)
  26. #endif
  27. namespace cds { namespace gc { namespace hp {
  28. std::atomic<unsigned> shared_var_membar::shared_var_{ 0 };
  29. #if CDS_OS_TYPE == CDS_OS_LINUX
  30. bool asymmetric_membar::membarrier_available_ = false;
  31. void asymmetric_membar::check_membarrier_available()
  32. {
  33. int res = syscall( __NR_membarrier, MEMBARRIER_CMD_QUERY, 0 );
  34. membarrier_available_ = !( res == -1 || ( res & CDS_MEMBARRIER_CMD_PRIVATE_EXPEDITED ) == 0 )
  35. && syscall( __NR_membarrier, CDS_MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0 ) == 0;
  36. }
  37. void asymmetric_membar::call_membarrier()
  38. {
  39. assert( membarrier_available_ );
  40. syscall( __NR_membarrier, CDS_MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0 );
  41. }
  42. bool asymmetric_global_membar::membarrier_available_ = false;
  43. void asymmetric_global_membar::check_membarrier_available()
  44. {
  45. int res = syscall( __NR_membarrier, MEMBARRIER_CMD_QUERY, 0 );
  46. membarrier_available_ = !( res == -1 || ( res & MEMBARRIER_CMD_SHARED ) == 0 );
  47. }
  48. void asymmetric_global_membar::call_membarrier()
  49. {
  50. assert( membarrier_available_ );
  51. syscall( __NR_membarrier, MEMBARRIER_CMD_SHARED, 0 );
  52. }
  53. #endif
  54. namespace {
  55. void * default_alloc_memory( size_t size )
  56. {
  57. return new uintptr_t[( size + sizeof( uintptr_t ) - 1 ) / sizeof( uintptr_t) ];
  58. }
  59. void default_free_memory( void* p )
  60. {
  61. delete[] reinterpret_cast<uintptr_t*>( p );
  62. }
  63. void* ( *s_alloc_memory )( size_t size ) = default_alloc_memory;
  64. void ( *s_free_memory )( void* p ) = default_free_memory;
  65. template <typename T>
  66. class allocator
  67. {
  68. public:
  69. typedef T value_type;
  70. allocator() {}
  71. allocator( allocator const& ) {}
  72. template <class U>
  73. explicit allocator( allocator<U> const& ) {}
  74. static T* allocate( size_t nCount )
  75. {
  76. return reinterpret_cast<T*>( s_alloc_memory( sizeof( value_type ) * nCount ));
  77. }
  78. static void deallocate( T* p, size_t /*nCount*/ )
  79. {
  80. s_free_memory( reinterpret_cast<void*>( p ));
  81. }
  82. };
  83. struct defaults {
  84. static const size_t c_nHazardPointerPerThread = 8;
  85. static const size_t c_nMaxThreadCount = 100;
  86. };
  87. size_t calc_retired_size( size_t nSize, size_t nHPCount, size_t nThreadCount )
  88. {
  89. size_t const min_size = nHPCount * nThreadCount;
  90. return nSize < min_size ? min_size * 2 : nSize;
  91. }
  92. stat s_postmortem_stat;
  93. } // namespace
  94. /*static*/ CDS_EXPORT_API smr* smr::instance_ = nullptr;
  95. thread_local thread_data* tls_ = nullptr;
  96. /*static*/ CDS_EXPORT_API thread_data* smr::tls()
  97. {
  98. assert( tls_ != nullptr );
  99. return tls_;
  100. }
  101. struct smr::thread_record: thread_data
  102. {
  103. // next hazard ptr record in list
  104. thread_record* next_ = nullptr;
  105. // Owner thread id; 0 - the record is free (not owned)
  106. atomics::atomic<cds::OS::ThreadId> thread_id_{ cds::OS::c_NullThreadId };
  107. // true if record is free (not owned)
  108. atomics::atomic<bool> free_{ false };
  109. thread_record( guard* guards, size_t guard_count, retired_ptr* retired_arr, size_t retired_capacity )
  110. : thread_data( guards, guard_count, retired_arr, retired_capacity )
  111. {}
  112. };
  113. /*static*/ CDS_EXPORT_API void smr::set_memory_allocator(
  114. void* ( *alloc_func )( size_t size ),
  115. void( *free_func )( void * p )
  116. )
  117. {
  118. // The memory allocation functions may be set BEFORE initializing HP SMR!!!
  119. assert( instance_ == nullptr );
  120. s_alloc_memory = alloc_func;
  121. s_free_memory = free_func;
  122. }
  123. /*static*/ CDS_EXPORT_API void smr::construct( size_t nHazardPtrCount, size_t nMaxThreadCount, size_t nMaxRetiredPtrCount, scan_type nScanType )
  124. {
  125. if ( !instance_ ) {
  126. instance_ = new( s_alloc_memory(sizeof(smr))) smr( nHazardPtrCount, nMaxThreadCount, nMaxRetiredPtrCount, nScanType );
  127. }
  128. }
  129. /*static*/ CDS_EXPORT_API void smr::destruct( bool bDetachAll )
  130. {
  131. if ( instance_ ) {
  132. if ( bDetachAll )
  133. instance_->detach_all_thread();
  134. instance_->~smr();
  135. s_free_memory( instance_ );
  136. instance_ = nullptr;
  137. }
  138. }
  139. CDS_EXPORT_API smr::smr( size_t nHazardPtrCount, size_t nMaxThreadCount, size_t nMaxRetiredPtrCount, scan_type nScanType )
  140. : hazard_ptr_count_( nHazardPtrCount == 0 ? defaults::c_nHazardPointerPerThread : nHazardPtrCount )
  141. , max_thread_count_( nMaxThreadCount == 0 ? defaults::c_nMaxThreadCount : nMaxThreadCount )
  142. , max_retired_ptr_count_( calc_retired_size( nMaxRetiredPtrCount, hazard_ptr_count_, max_thread_count_ ))
  143. , scan_type_( nScanType )
  144. , scan_func_( nScanType == classic ? &smr::classic_scan : &smr::inplace_scan )
  145. {
  146. thread_list_.store( nullptr, atomics::memory_order_release );
  147. }
  148. CDS_EXPORT_API smr::~smr()
  149. {
  150. CDS_DEBUG_ONLY( const cds::OS::ThreadId nullThreadId = cds::OS::c_NullThreadId; )
  151. CDS_DEBUG_ONLY( const cds::OS::ThreadId mainThreadId = cds::OS::get_current_thread_id();)
  152. CDS_HPSTAT( statistics( s_postmortem_stat ));
  153. thread_record* pHead = thread_list_.load( atomics::memory_order_relaxed );
  154. thread_list_.store( nullptr, atomics::memory_order_release );
  155. thread_record* pNext = nullptr;
  156. for ( thread_record* hprec = pHead; hprec; hprec = pNext )
  157. {
  158. assert( hprec->thread_id_.load( atomics::memory_order_relaxed ) == nullThreadId
  159. || hprec->thread_id_.load( atomics::memory_order_relaxed ) == mainThreadId );
  160. retired_array& arr = hprec->retired_;
  161. for ( retired_ptr* cur{ arr.first() }, *last{ arr.last() }; cur != last; ++cur ) {
  162. cur->free();
  163. CDS_HPSTAT( ++s_postmortem_stat.free_count );
  164. }
  165. arr.reset( 0 );
  166. pNext = hprec->next_;
  167. hprec->free_.store( true, atomics::memory_order_relaxed );
  168. destroy_thread_data( hprec );
  169. }
  170. }
  171. CDS_EXPORT_API smr::thread_record* smr::create_thread_data()
  172. {
  173. size_t const guard_array_size = thread_hp_storage::calc_array_size( get_hazard_ptr_count());
  174. size_t const retired_array_size = retired_array::calc_array_size( get_max_retired_ptr_count());
  175. size_t const nSize = sizeof( thread_record ) + guard_array_size + retired_array_size;
  176. /*
  177. The memory is allocated by contnuous block
  178. Memory layout:
  179. +--------------------------+
  180. | |
  181. | thread_record |
  182. | hazards_ +---+
  183. +---| retired_ | |
  184. | | | |
  185. | |--------------------------| |
  186. | | hazard_ptr[] |<--+
  187. | | |
  188. | | |
  189. | |--------------------------|
  190. +-->| retired_ptr[] |
  191. | |
  192. | |
  193. +--------------------------+
  194. */
  195. uint8_t* mem = reinterpret_cast<uint8_t*>( s_alloc_memory( nSize ));
  196. return new( mem ) thread_record(
  197. reinterpret_cast<guard*>( mem + sizeof( thread_record )),
  198. get_hazard_ptr_count(),
  199. reinterpret_cast<retired_ptr*>( mem + sizeof( thread_record ) + guard_array_size ),
  200. get_max_retired_ptr_count()
  201. );
  202. }
  203. /*static*/ CDS_EXPORT_API void smr::destroy_thread_data( thread_record* pRec )
  204. {
  205. // all retired pointers must be freed
  206. assert( pRec->retired_.size() == 0 );
  207. pRec->~thread_record();
  208. s_free_memory( pRec );
  209. }
  210. CDS_EXPORT_API smr::thread_record* smr::alloc_thread_data()
  211. {
  212. thread_record * hprec;
  213. const cds::OS::ThreadId nullThreadId = cds::OS::c_NullThreadId;
  214. const cds::OS::ThreadId curThreadId = cds::OS::get_current_thread_id();
  215. // First try to reuse a free (non-active) HP record
  216. for ( hprec = thread_list_.load( atomics::memory_order_acquire ); hprec; hprec = hprec->next_ ) {
  217. cds::OS::ThreadId thId = nullThreadId;
  218. if ( !hprec->thread_id_.compare_exchange_strong( thId, curThreadId, atomics::memory_order_relaxed, atomics::memory_order_relaxed ))
  219. continue;
  220. hprec->free_.store( false, atomics::memory_order_release );
  221. return hprec;
  222. }
  223. // No HP records available for reuse
  224. // Allocate and push a new HP record
  225. hprec = create_thread_data();
  226. hprec->thread_id_.store( curThreadId, atomics::memory_order_relaxed );
  227. thread_record* pOldHead = thread_list_.load( atomics::memory_order_relaxed );
  228. do {
  229. hprec->next_ = pOldHead;
  230. } while ( !thread_list_.compare_exchange_weak( pOldHead, hprec, atomics::memory_order_release, atomics::memory_order_acquire ));
  231. return hprec;
  232. }
  233. CDS_EXPORT_API void smr::free_thread_data( smr::thread_record* pRec, bool callHelpScan )
  234. {
  235. assert( pRec != nullptr );
  236. pRec->hazards_.clear();
  237. scan( pRec );
  238. if ( callHelpScan )
  239. help_scan( pRec );
  240. pRec->thread_id_.store( cds::OS::c_NullThreadId, atomics::memory_order_release );
  241. }
  242. CDS_EXPORT_API void smr::detach_all_thread()
  243. {
  244. thread_record * pNext = nullptr;
  245. const cds::OS::ThreadId nullThreadId = cds::OS::c_NullThreadId;
  246. for ( thread_record * hprec = thread_list_.load( atomics::memory_order_relaxed ); hprec; hprec = pNext ) {
  247. pNext = hprec->next_;
  248. if ( hprec->thread_id_.load( atomics::memory_order_relaxed ) != nullThreadId ) {
  249. free_thread_data( hprec, false );
  250. }
  251. }
  252. }
  253. /*static*/ CDS_EXPORT_API void smr::attach_thread()
  254. {
  255. if ( !tls_ )
  256. tls_ = instance().alloc_thread_data();
  257. }
  258. /*static*/ CDS_EXPORT_API void smr::detach_thread()
  259. {
  260. thread_data* rec = tls_;
  261. if ( rec ) {
  262. tls_ = nullptr;
  263. instance().free_thread_data( static_cast<thread_record*>( rec ), true );
  264. }
  265. }
  266. CDS_EXPORT_API void smr::inplace_scan( thread_data* pThreadRec )
  267. {
  268. thread_record* pRec = static_cast<thread_record*>( pThreadRec );
  269. //CDS_HAZARDPTR_STATISTIC( ++m_Stat.m_ScanCallCount )
  270. // In-place scan algo uses LSB of retired ptr as a mark for internal purposes.
  271. // It is correct if all retired pointers are ar least 2-byte aligned (LSB is zero).
  272. // If it is wrong, we use classic scan algorithm
  273. // Check if all retired pointers has zero LSB
  274. // LSB is used for marking pointers that cannot be deleted yet
  275. retired_ptr* first_retired = pRec->retired_.first();
  276. retired_ptr* last_retired = pRec->retired_.last();
  277. if ( first_retired == last_retired )
  278. return;
  279. for ( auto it = first_retired; it != last_retired; ++it ) {
  280. if ( it->m_n & 1 ) {
  281. // found a pointer with LSB bit set - use classic_scan
  282. classic_scan( pRec );
  283. return;
  284. }
  285. }
  286. CDS_HPSTAT( ++pThreadRec->scan_count_ );
  287. // Sort retired pointer array
  288. std::sort( first_retired, last_retired, retired_ptr::less );
  289. // Check double free
  290. # ifdef _DEBUG
  291. {
  292. auto it = first_retired;
  293. auto itPrev = it;
  294. while ( ++it != last_retired ) {
  295. assert( itPrev->m_p < it->m_p );
  296. itPrev = it;
  297. }
  298. }
  299. # endif
  300. // Search guarded pointers in retired array
  301. thread_record* pNode = thread_list_.load( atomics::memory_order_acquire );
  302. {
  303. retired_ptr dummy_retired;
  304. while ( pNode ) {
  305. if ( pNode->thread_id_.load( atomics::memory_order_relaxed ) != cds::OS::c_NullThreadId ) {
  306. thread_hp_storage& hpstg = pNode->hazards_;
  307. for ( auto hp = hpstg.begin(), end = hpstg.end(); hp != end; ++hp ) {
  308. void * hptr = hp->get( atomics::memory_order_relaxed );
  309. if ( hptr ) {
  310. dummy_retired.m_p = hptr;
  311. retired_ptr* it = std::lower_bound(first_retired, last_retired, dummy_retired, retired_ptr::less);
  312. if ( it != last_retired && it->m_p == hptr ) {
  313. // Mark retired pointer as guarded
  314. it->m_n |= 1;
  315. }
  316. }
  317. }
  318. }
  319. pNode = pNode->next_;
  320. }
  321. }
  322. // Move all marked pointers to head of array
  323. {
  324. retired_ptr* insert_pos = first_retired;
  325. for ( retired_ptr* it = first_retired; it != last_retired; ++it ) {
  326. if ( it->m_n & 1 ) {
  327. it->m_n &= ~uintptr_t(1);
  328. if ( insert_pos != it )
  329. *insert_pos = *it;
  330. ++insert_pos;
  331. }
  332. else {
  333. // Retired pointer may be freed
  334. it->free();
  335. CDS_HPSTAT( ++pRec->free_count_ );
  336. }
  337. }
  338. const size_t nDeferred = insert_pos - first_retired;
  339. pRec->retired_.reset( nDeferred );
  340. }
  341. }
  342. // cppcheck-suppress functionConst
  343. CDS_EXPORT_API void smr::classic_scan( thread_data* pThreadRec )
  344. {
  345. thread_record* pRec = static_cast<thread_record*>( pThreadRec );
  346. CDS_HPSTAT( ++pThreadRec->scan_count_ );
  347. std::vector< void*, allocator<void*>> plist;
  348. plist.reserve( get_max_thread_count() * get_hazard_ptr_count());
  349. assert( plist.size() == 0 );
  350. // Stage 1: Scan HP list and insert non-null values in plist
  351. thread_record* pNode = thread_list_.load( atomics::memory_order_acquire );
  352. while ( pNode ) {
  353. if ( pNode->thread_id_.load( std::memory_order_relaxed ) != cds::OS::c_NullThreadId ) {
  354. for ( size_t i = 0; i < get_hazard_ptr_count(); ++i ) {
  355. void * hptr = pNode->hazards_[i].get();
  356. if ( hptr )
  357. plist.push_back( hptr );
  358. }
  359. }
  360. pNode = pNode->next_;
  361. }
  362. // Sort plist to simplify search in
  363. std::sort( plist.begin(), plist.end());
  364. // Stage 2: Search plist
  365. retired_array& retired = pRec->retired_;
  366. retired_ptr* first_retired = retired.first();
  367. retired_ptr* last_retired = retired.last();
  368. {
  369. auto itBegin = plist.begin();
  370. auto itEnd = plist.end();
  371. retired_ptr* insert_pos = first_retired;
  372. for ( retired_ptr* it = first_retired; it != last_retired; ++it ) {
  373. if ( std::binary_search( itBegin, itEnd, first_retired->m_p )) {
  374. if ( insert_pos != it )
  375. *insert_pos = *it;
  376. ++insert_pos;
  377. }
  378. else {
  379. it->free();
  380. CDS_HPSTAT( ++pRec->free_count_ );
  381. }
  382. }
  383. retired.reset( insert_pos - first_retired );
  384. }
  385. }
  386. CDS_EXPORT_API void smr::help_scan( thread_data* pThis )
  387. {
  388. assert( static_cast<thread_record*>( pThis )->thread_id_.load( atomics::memory_order_relaxed ) == cds::OS::get_current_thread_id());
  389. CDS_HPSTAT( ++pThis->help_scan_count_ );
  390. const cds::OS::ThreadId nullThreadId = cds::OS::c_NullThreadId;
  391. const cds::OS::ThreadId curThreadId = cds::OS::get_current_thread_id();
  392. for ( thread_record* hprec = thread_list_.load( atomics::memory_order_acquire ); hprec; hprec = hprec->next_ )
  393. {
  394. if ( hprec == static_cast<thread_record*>( pThis ))
  395. continue;
  396. // If free_ == true then hprec->retired_ is empty - we don't need to see it
  397. if ( hprec->free_.load( atomics::memory_order_acquire ))
  398. continue;
  399. // Owns hprec if it is empty.
  400. // Several threads may work concurrently so we use atomic technique only.
  401. {
  402. cds::OS::ThreadId curOwner = hprec->thread_id_.load( atomics::memory_order_relaxed );
  403. if ( curOwner == nullThreadId ) {
  404. if ( !hprec->thread_id_.compare_exchange_strong( curOwner, curThreadId, atomics::memory_order_acquire, atomics::memory_order_relaxed ))
  405. continue;
  406. }
  407. else
  408. continue;
  409. }
  410. // We own the thread record successfully. Now, we can see whether it has retired pointers.
  411. // If it has ones then we move them to pThis that is private for current thread.
  412. retired_array& src = hprec->retired_;
  413. retired_array& dest = pThis->retired_;
  414. assert( !dest.full());
  415. retired_ptr* src_first = src.first();
  416. retired_ptr* src_last = src.last();
  417. for ( ; src_first != src_last; ++src_first ) {
  418. if ( !dest.push( std::move( *src_first )))
  419. scan( pThis );
  420. }
  421. src.interthread_clear();
  422. hprec->free_.store( true, atomics::memory_order_release );
  423. hprec->thread_id_.store( nullThreadId, atomics::memory_order_release );
  424. scan( pThis );
  425. }
  426. }
  427. CDS_EXPORT_API void smr::statistics( stat& st )
  428. {
  429. st.clear();
  430. # ifdef CDS_ENABLE_HPSTAT
  431. for ( thread_record* hprec = thread_list_.load( atomics::memory_order_acquire ); hprec; hprec = hprec->next_ )
  432. {
  433. CDS_TSAN_ANNOTATE_IGNORE_READS_BEGIN;
  434. ++st.thread_rec_count;
  435. st.guard_allocated += hprec->hazards_.alloc_guard_count_;
  436. st.guard_freed += hprec->hazards_.free_guard_count_;
  437. st.retired_count += hprec->retired_.retire_call_count_;
  438. st.free_count += hprec->free_count_;
  439. st.scan_count += hprec->scan_count_;
  440. st.help_scan_count += hprec->help_scan_count_;
  441. CDS_TSAN_ANNOTATE_IGNORE_READS_END;
  442. }
  443. # endif
  444. }
  445. }}} // namespace cds::gc::hp
  446. CDS_EXPORT_API /*static*/ cds::gc::HP::stat const& cds::gc::HP::postmortem_statistics()
  447. {
  448. return cds::gc::hp::s_postmortem_stat;
  449. }