Read-copy-update (RCU)

Read-copy-update is strong in multithreading environments where a data structure is read almost exclusively but rarely written.

First of all, what is RCU? The excellent Wikipedia page on Read-copy-update provides a good introduction:

In computer science, read-copy-update (RCU) is a synchronization mechanism that avoids the use of lock primitives while multiple threads concurrently read and update elements that are linked through pointers and that belong to shared data structures (e.g., linked lists, trees, hash tables).[1]

Whenever a thread is inserting or deleting elements of data structures in shared memory, all readers are guaranteed to see and traverse either the older or the new structure, therefore avoiding inconsistencies (e.g., dereferencing null pointers).[1]

It is used when performance of reads is crucial and is an example of space–time tradeoff, enabling fast operations at the cost of more space. This makes all readers proceed as if there were no synchronization involved, hence they will be fast, but also making updates more difficult.

The name RCU was coined by the writer’s workflow. It first reads the current data, copies it to the new data, and then updates the current data.

Proposal P2545R4 provides three use cases for RCU, comparing them with reader-writer locks.

Three Use Cases

The three use cases compare reader-writer locks. with intrusive, non-intrusive, and synchronous RCU.

Intrusive RCU

Non-Intrusive RCU

Synchronous RCU


The main difference between intrusive and non-intrusive RCU is that in the intrusive case, Data is derived directly from the class struct Data : std::rcu_obj_base<Data>. This technique, in which a template has itself as a parameter, is known as the Curiously Recurring Template Pattern (CRTP). On the reader side, std::scoped_lock l(std::rcu_default_domain()) is used. On the writer side, either retire() or std::rcu_synchronize() is used.

This raises the question: when should similar techniques such as atomic shared pointers, read-write locks, hazard pointers, and RCU be used? The Proposal P2545R4 provides a simple rule: As a very rough rule of thumb, Hazard Pointers can be considered to be a scalable replacement for reference counters and RCU can be considered to be a scalable replacement for reader-writer locking. A high-level comparison of reference counting, Hazard Pointers, and RCU is displayed in Table 1.

 

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.

     

    Here is Table 1:

    To conclude this article, here is the interface of RCU.

    Interface

    The interface consists of two classes, rcu_obj_base and rcu_domain and four functions, rcu_default_domain, rcu_synchronize, rcu_barrier, and rcu_retire.

    cppreference provides a concise description:

    Classes

    • rcu_obj_base: allows an object to be protected by RCU
    • rcu_domain: provides regions of RCU protection

    Functions

    • rcu_default_domain: returns a reference to a static-duration object of type std::rcu_domain
    • rcu_synchronize: blocks until a protection region unlocks on a RCU domain
    • rcu_barrier: may evaluate scheduled operations on a RCU domain and blocks until all preceding evaluations are complete
    • rcu_retire: schedules the evaluation of a specified function on a RCU domain, potentially allocating memory, and invoking scheduled evaluations

    What’s next?

    The Data-parallel types (SIMD) library provides data-parallel types and operations on them. I will take a closer look at this library in my next article.

    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