Deutsch English Français Italiano |
<vc7f5o$2atht$5@dont-email.me> View for Bookmarking (what is this?) Look up another Usenet article |
Path: ...!3.eu.feeder.erje.net!2.eu.feeder.erje.net!feeder.erje.net!eternal-september.org!feeder3.eternal-september.org!news.eternal-september.org!.POSTED!not-for-mail From: "Chris M. Thomasson" <chris.m.thomasson.1@gmail.com> Newsgroups: comp.lang.c++ Subject: Re: Atomic caching of smart pointers Date: Sun, 15 Sep 2024 13:13:44 -0700 Organization: A noiseless patient Spider Lines: 100 Message-ID: <vc7f5o$2atht$5@dont-email.me> References: <vc7ahq$2akr4$1@dont-email.me> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Injection-Date: Sun, 15 Sep 2024 22:13:44 +0200 (CEST) Injection-Info: dont-email.me; posting-host="78a7b50be27a8595bbf19a3d12d98608"; logging-data="2455101"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/XISuGz850fhx9Z3a2ZHoNsc3Vx+qunUQ=" User-Agent: Mozilla Thunderbird Cancel-Lock: sha1:xHp4znRfTckBvzXW33G5bMRsF88= Content-Language: en-US In-Reply-To: <vc7ahq$2akr4$1@dont-email.me> Bytes: 4914 On 9/15/2024 11:54 AM, Paavo Helde wrote: > > I am thinking of developing some lock-free data structures for better > scaling on multi-core hardware and avoiding potential deadlocks. In > particular, I have got a lot of classes which are mostly immutable after > construction, except for some cached data members which are calculated > on demand only, then stored in the object for later use. > > Caching single numeric values is easy. However, some cached data is > large and accessed via a std::shared_ptr type refcounted smartpointers. > Updating such a smartpointer in a thread-shared object is a bit more > tricky. There is a std::atomic<std::shared_ptr> in C++20, but I wonder > if I can do a bit better by providing my own implementation which uses > CAS on a single pointer (instead of DCAS with additional data fields or > other trickery). > > This is assuming that > > a) the cached value will not change any more after assigned, and will > stay intact until the containing object destruction; > > b) it's ok if multiple threads calculate the value at the same time; the > first one stored will be the one which gets used. > > My current prototype code is as follows (Ptr<T> is similar to > std::shared_ptr<T>, but is using an internal atomic refcounter; using an > internal counter allows me to generate additional smartpointers from a > raw pointer). > > template<typename T> > class CachedAtomicPtr { > public: > CachedAtomicPtr(): ptr_(nullptr) {} > > /// Store p in *this if *this is not yet assigned. > /// Return pointer stored in *this, which can be \a p or not. > Ptr<T> AssignIfNull(Ptr<T> p) { > const T* other = nullptr; > if (ptr_.compare_exchange_weak(other, p.get(), > std::memory_order_release, std::memory_order_acquire)) { > p->IncrementRefcount(); > return p; > } else { > // wrap in an extra smartptr (increments refcount) > return Ptr<T>(other); > } > } > > /// Return pointer stored in *this, > Ptr<T> Load() const { > return Ptr<T>(ptr_); > } > > ~CachedAtomicPtr() { > if (const T* ptr = ptr_) { > ptr->DecrementRefcount(); > } > } > private: > std::atomic<const T*> ptr_; > }; > > Example usage: > > /// Objects of this class are in shared use by multiple threads. > class A { > // Returns B corresponding to the value of *this. > // If not yet in cache, B is calculated and cached in *this. > // Calculating can happen in multiple threads in parallel, > // the first cached result will be used in all threads. > Ptr<B> GetOrCalcB() const { > Ptr<B> b = cached_.Load(); > if (!b) { > b = cached_.AssignIfNull(CalcB()); > } > return b; > } > // ... > private: > // Calculates cached B object according to value of *this. > Ptr<B> CalcB() const; > private: > mutable CachedAtomicPtr<B> cached_; > // ... own data ... > }; > > So, what do you think? Should I just use std::atomic<std::shared_ptr> > instead? Any other suggestions? Did I get the memory order parameters > right in compare_exchange_weak()? > I need to look at this when I get some time. Been very busy lately. Humm... Perhaps, when you get some free time to burn... Try to model it in Relacy and see what happens: https://www.1024cores.net/home/relacy-race-detector/rrd-introduction https://groups.google.com/g/relacy