Warning: mysqli::__construct(): (HY000/1203): User howardkn already has more than 'max_user_connections' active connections in D:\Inetpub\vhosts\howardknight.net\al.howardknight.net\includes\artfuncs.php on line 21
Failed to connect to MySQL: (1203) User howardkn already has more than 'max_user_connections' active connections
Warning: mysqli::query(): Couldn't fetch mysqli in D:\Inetpub\vhosts\howardknight.net\al.howardknight.net\index.php on line 66
Article <vc7f5o$2atht$5@dont-email.me>
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