C++ Logo

std-discussion

Advanced search

Generalized atomic updates

From: Nate Eldredge <nate_at_[hidden]>
Date: Tue, 7 May 2024 23:19:57 -0600 (MDT)
Along the lines of std::atomic<T>::fetch_add() and friends, I have been
wondering about the possibility of an interface that would let you
atomically apply an *arbitrary* function to an atomic object. I'm
wondering if such an interface has ever been considered for inclusion in
C++.

To explain what I mean, call it fetch_update() by analogy with Java's
getAndUpdate(). I'm envisioning that something like

std::atomic<int> x;
int f(int);
x.fetch_update(f);

would atomically perform the equivalent of

x.store(f(x.load());

Currently, one would normally accomplish this with a compare-exchange
loop:

int tmp = x.load();
while (!x.compare_exchange_weak(tmp, f(tmp))) { }

and this would be a valid implementation. However, many architectures
have load-link/store-conditional instructions (e.g. ARM64 with LDRX/STRX)
and could implement this more efficiently by executing f() in between the
LL and SC. So in effect, fetch_update would expose the LL/SC
functionality for use at the level of C++. It could also be implemented
with transactional memory where available.

For atomic types that are not lock-free, fetch_update could improve on
compare-exchange by simply executing f() with the lock held. And even on
systems where compare-exchange is the only possible implementation, it
would be simpler and less error-prone for the programmer to write
x.fetch_update(f) instead of writing the compare-exchange loop every time.

Certainly there are a number of possible drawbacks and difficulties, as
well as additional benefits, but rather than go into them right now, I'm
currently just looking for prior work on this topic.

Has such functionality previously been proposed or discussed for inclusion
in the C++ standard? So far I have not found any papers which do so, so I
am wondering if anyone can point me to any, or other references to
consider.

- I did find P0493
(https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p0493r5.pdf)
which proposes fetch_min and fetch_max; these could be implemented
trivially using fetch_update() and a closure.

- And then there is N3696
(https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3696.htm)
proposing a priority_update() function, which given

bool pred(int, int);
int y;
x.priority_update(y, pred);

would atomically perform

if (pred(x.load(), y)
     x.store(y);

(We can easily imagine a generalized fetch_conditionally_update() where
the given function returns a bool, and the store is aborted if it returns
false; in which case my proposal could subsume N3696.)

- And there's P2066
(https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p2066r5.html)
proposing an interface for using transactional memory, on systems that
support it. However, my proposed fetch_update would be usable on all
systems, since it could be validly implemented in terms of
compare_exchange which already exists,

Any other relevant references would be appreciated. Thanks!

-- 
Nate Eldredge
nate_at_[hidden]

Received on 2024-05-08 05:20:04