From 71e9d35ab226602e23f0c52848229758a8fe52aa Mon Sep 17 00:00:00 2001 From: joaquintides Date: Tue, 26 Dec 2023 19:08:51 +0100 Subject: [PATCH] made latch-free unprotected_norehash_emplace_or_visit exception safe --- .../unordered/detail/foa/concurrent_table.hpp | 51 +++++++++++++++---- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/include/boost/unordered/detail/foa/concurrent_table.hpp b/include/boost/unordered/detail/foa/concurrent_table.hpp index cdd9d3f3..337cc7c0 100644 --- a/include/boost/unordered/detail/foa/concurrent_table.hpp +++ b/include/boost/unordered/detail/foa/concurrent_table.hpp @@ -1579,6 +1579,38 @@ private: }; #if defined(BOOST_UNORDERED_LATCH_FREE) + struct latch_free_reserve_slot + { + latch_free_reserve_slot(group_type* pg_,std::size_t pos_):pg{pg_},pos{pos_} + { + unsigned char expected=0; + succeeded_=reinterpret_cast*>(pg)[pos]. + compare_exchange_weak(expected,1); + } + + ~latch_free_reserve_slot() + { + if(succeeded_&&!commit_)pg->reset(pos); + } + + bool succeeded()const{return succeeded_;} + + void commit(){commit_=true;} + + group_type *pg; + std::size_t pos; + bool succeeded_; + bool commit_=false; + }; + + struct assign_insert_counter_on_exit + { + ~assign_insert_counter_on_exit(){counter=x;} + + group_insert_counter_type &counter; + boost::uint32_t x; + }; + template BOOST_FORCEINLINE int unprotected_norehash_emplace_or_visit( @@ -1592,7 +1624,6 @@ private: startover: boost::uint32_t counter=0; while(BOOST_UNLIKELY((counter=insert_counter(pos0))%2==1)){} - if(unprotected_visit( access_mode,k,pos0,hash,std::forward(f)))return 0; @@ -1603,23 +1634,23 @@ private: auto mask=pg->match_available(); if(BOOST_LIKELY(mask!=0)){ auto n=unchecked_countr_zero(mask); - unsigned char expected=0; - if(!reinterpret_cast*>(pg)[n]. - compare_exchange_weak(expected,1)){ + latch_free_reserve_slot rslot{pg,n}; + if(BOOST_UNLIKELY(!rslot.succeeded())){ /* slot wasn't empty */ goto startover; } - auto p=this->arrays.elements()+pos*N+n; - this->construct_element(p,std::forward(args)...); if(BOOST_UNLIKELY( !insert_counter(pos0).compare_exchange_weak(counter,counter+1))){ /* other thread inserted from pos0, need to start over */ - this->destroy_element(p); - pg->reset(n); goto startover; } - pg->set(n,hash); - insert_counter(pos0)=counter+2; + { + assign_insert_counter_on_exit a{insert_counter(pos0),counter+2}; + auto p=this->arrays.elements()+pos*N+n; + this->construct_element(p,std::forward(args)...); + pg->set(n,hash); + } + rslot.commit(); auto& v=local_garbage_vector(); ++v.size; v.mcos-=!pg->is_not_overflowed(hash);