Home DEVELOPER Deferred reclamation in C++26: read-copy update and threat indicator

Deferred reclamation in C++26: read-copy update and threat indicator

0


A common problem with concurrency is the so-called ABA problem. This means reading a variable twice, returning the same value A each time. Therefore, one comes to the conclusion that nothing has changed in between. But you forgot B.

Advertisement





Rainer Grimm has been working as a software architect, team and training manager for many years. He enjoys writing articles on the programming languages ​​C++, Python, and Haskell, but also frequently speaks at expert conferences. On his blog Modern C++ he discusses his passion C++ in depth.



The following scenario presents the problem.

The scenario is that you are sitting in your car and waiting for the traffic light to turn green. In our case, green means B and red means A. What will happen next?

Deferred reclamation in C++26: read-copy updates and dangerous pointers
  1. You look at a traffic light and it is red (A).
  2. Because it’s boring, you watch the news on your smartphone and forget about the time.
  3. You look at the traffic lights again. Damn it, he’s still red (A).

The traffic light turned green between the two checks (B). So there were two red phases, although there appeared to be only one.

What does it look like with threads (processes)? Now again very formally.

  1. Thread 1 reads a variable var With value A.
  2. Thread 1 is interrupted and thread 2 is running.
  3. Thread 2 changes variable var From A to B to A.
  4. Thread 1 starts execution and checks the value of the variable varSince the value of the variable var remains the same, thread 1 continues its work,

Often you can ignore ABA.

In the code below, the function multiply is fetch_mult (1) A std::atomic&that mult is shared.

// fetch_mult.cpp

#include 
#include 

template 
T fetch_mult(std::atomic& shared, T mult){                          // 1
  T oldValue = shared.load();                                          // 2
  while (!shared.compare_exchange_strong(oldValue, oldValue * mult));  // 3
  return oldValue;
}

int main(){
  std::atomic myInt{5};
  std::cout << myInt << '\n';          
  fetch_mult(myInt,5);
  std::cout << myInt << '\n';         
}

shared.compare_exchange_strong(expected, desired)(3) Have the following behaviour:

  • if comparison false will result expected But shared set.
  • If nuclear comparison true will result shared in a single nuclear operation expected set.

The most important observation is that there is a small time period between reading the old value T oldValue = shared.load (2) and comparison with the new value (3). So another thread can step in and oldValue From oldValue To anotherValue and back again oldValue Change. anotherValue Have B in ABA.

I would like to describe ABA using a lock-free data structure.

I am using a lock-free stack implemented as a linked list. The stack supports only two operations.

  1. Pop the top object and return a pointer to it.
  2. Push the specified object onto the stack.

I would like to describe the pop operation in pseudocode to give you an idea of ​​the ABA problem. The pop operation goes through the following steps until successful.

  • Get head node: Head
  • Get the following nodes: headnext
  • Make headnext If the new chief Head still the head of the pile

Here are the first two nodes of the stack:

Stack: TOP -> head -> headNext -> ...

Let’s start with the following stack:

Stack: TOP -> A -> B -> C

Thread 1 is active and wants to remove the stack head.

Before thread 1 finishes the pop algorithm, thread 2 becomes active.

Stack: TOP -> B -> C

  • Thread 2 deletes B and deletes B

Stack: TOP -> C

  • Thread 2 pushes A back

Stack: TOP -> A -> C

Thread 1 has been rescheduled to check A == headSince A == headbecomes headNextThat means B, for the new chief. But B has already been removed. Therefore the behavior of the program is undefined.

There are some solutions to the ABA problem.

The conceptual problem of ABA is relatively easy to understand. lumpy B == headNext Even if another node is deleted, A == headtold. The solution to our problem is to prevent premature deletion of a node. Here are some possible solutions.

  • Reference to marked position

One can add a tag indicating how many times the node was successfully changed. However, the compare and swap method ultimately fails, even though the check returns (/code) true (/code).

The next three techniques are based on the idea of ​​delayed recapture.

Garbage collection guarantees that variables are only deleted when they are no longer needed. This sounds promising, but it has one major disadvantage. Most garbage collectors are not lock-free. So, you have a lock-free data structure, but the overall system is not lock-free.

From Wikipedia: Danger signs:

In a threat indicator system, each thread maintains a list of threat indicators that indicate which nodes the thread reaches. (This “list” may be limited to only one or two items in many systems.) Nodes on the threat indicator list cannot be modified or released by another thread. …when a thread wants to delete a node, it puts it on the list of nodes to be “released later”, but does not release the node’s memory until another thread’s delete list May not contain a pointer. A dedicated garbage collection thread can perform this manual garbage collection (if the “release later” list is shared among all threads); Alternatively, cleanup of the release list may be performed by each worker thread as part of an operation such as pop.

rcu means Reed Cmake a copy of Youpdate, a synchronization technique for almost read-only data structures developed by Paul McKechnie and used in the Linux kernel since 2002.

The idea is quite simple and follows the acronym. To change data, you make a copy of the data and change that copy. In contrast, all readers work with the original data. If there is no reader, the data structure can be safely changed from the copy.

In my next article I will implement a lock-free stack with deferred reclamation.


(rme)

Javaland 2025: Secure Early Bird Tickets Now

NO COMMENTS

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Exit mobile version