atomic<shared_ptr<>> by Oliver Schädlich

This blog article is an experiment. A few days ago, I received the following email (translated from German) from Oliver Schädlich (oliver.schaedlich@gmail.com). If, as I hope, this article sparks a discussion, I will be happy to summarize it in my last article.

Please send an email to: Rainer.Grimm@ModernesCpp.de.

I just read your latest article on hazard pointers with a look at RCU on Heise. Inspired by YouTube presentations on lock-free variants of atomic<shared_ptr<>>, I thought to myself: it doesn’t have to be that complicated. In principle, you could implement something like an atomic<shared_ptr<>> simply with Lock, and when updating the shared_ptr<> from the central atomic<shared_ptr<>>, a comparison of the atomic with the shared_ptr is first performed without locking.

Usually, with RCU-like patterns with a central “atomic<shared_ptr<>>,” this is updated relatively rarely, but you very often fetch an update of the current value. Normally, if both pointers point to the same thing, you simply don’t lock, and you get a super-fast update because all cache lines involved remain shared.

I implemented this rudimentarily with VS 2022, and updating the shared_obj<T> from the central tshared_obj<> takes about 1.5 nanoseconds on my Zen4 CPU. The solution is so simple that I wondered why no one had thought of it before.

In principle, you could give the shared_ptr<> from C++ an assignment operator that takes an atomic<shared_ptr<>> that does the same thing. Currently, the atomic<shared_ptr<>> casts to shared_ptr<> and a relatively expensive copy is performed. libstdc++ and MSVC currently work with a lock at this point, which would still not be a problem if you proceed as described for the special case in question. Perhaps you could pass the idea on to the appropriate place so that something like this can be included in one of the next standards.

Here is the code that Oliver Schädlich sent me. It is written in C++20 and uses the standard library. The files are futex.cpp, main.cpp, cl_size.h, futex.h, and shared_obj.h.

You can download the source code here: source.7z.

The most interesting file is shared_obj.h and the following function within it:

template<typename T>
shared_obj<T> &shared_obj<T>::operator =( tshared_obj<T> const &tso ) noexcept
{
	using namespace std;
	ctrl_t *ctrl = tso.m_ctrl.load( memory_order_relaxed );
	if( ctrl == m_ctrl )
		return *this;
	if( m_ctrl )
	{
		if( m_ctrl->decr() == 1 )
			delete m_ctrl;
		m_ctrl = nullptr;
	}
	lock_guard<typename tshared_obj<T>::mutex_t> lock( tso.m_mtx );
	ctrl = tso.m_ctrl.load( memory_order_relaxed );
	m_ctrl = ctrl;
	ctrl->incr();
	return *this;
}

If I remove the first if with return *this, the code is a few thousand times slower, about as slow as updating a shared_ptr<> from an atomic<shared_ptr<>>.

 

Rainer D 6 P2 500x500Modernes C++ Mentoring

  • "Fundamentals for C++ Professionals" (open)
  • "Design Patterns and Architectural Patterns with C++" (open)
  • "C++20: Get the Details" (open)
  • "Concurrency with Modern C++" (open)
  • "Embedded Programming with Modern C++": January 2025
  • "Generic Programming (Templates) with C++": February 2025
  • "Clean Code: Best Practices for Modern C++": May 2025
  • Do you want to stay informed: Subscribe.

     

    What’s next?

    RCU stands for Read Copy Update, a synchronization technique for almost exclusively read-only data structures developed by Paul McKenney and used in the Linux kernel since 2002. RCU is currently mentioned in connection with hazard pointers.

    Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, Honey Sukesan, bruce_lee_wayne, Silviu Ardelean, schnapper79, and Seeker.

    Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.

    My special thanks to Embarcadero
    My special thanks to PVS-Studio
    My special thanks to Tipi.build 
    My special thanks to Take Up Code
    My special thanks to SHAVEDYAKS

    Modernes C++ GmbH

    Modernes C++ Mentoring (English)

    Do you want to stay informed about my mentoring programs? Subscribe Here

    Rainer Grimm
    Yalovastraße 20
    72108 Rottenburg

    Mobil: +49 176 5506 5086
    Mail: schulung@ModernesCpp.de
    Mentoring: www.ModernesCpp.org