Path: csiph.com!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.std.c Subject: Re: atomic_compare_exchange atomicity of write to *expected Date: Sat, 09 Oct 2021 03:04:29 -0700 Organization: A noiseless patient Spider Lines: 70 Message-ID: <86o87ypl8y.fsf@linuxsc.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="4b78c8c73e68d6ec569569f083685d5c"; logging-data="28562"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX18rYg8ZtDYebddpySotEJTvwpXXeJ6ozI0=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:1g7oW181DaCTDsOAoJnePJulnZ0= sha1:98FLeF0zgxLHBMJtThDuceb3edw= Xref: csiph.com comp.std.c:6342 Philipp Klaus Krause writes: > For the atomic_compare-exchange family, e.g. > > _Bool atomic_compare_exchange_strong(volatile A *object, > C *expected, C desired); > > the description states "Atomically, compares the contents of the > memory pointed to by object for equality with that pointed to by > expected, and if true, replaces the contents of the memory pointed > to by object with desired, and if false, updates the contents of the > memory pointed to by expected with that pointed to by object." > > This reads to me as if the write to *expected is also part of the > atomic operation. For what it's worth my reading agrees on this point: any update of *expected is included in the indivisible atomic operation of comparing and either replacing of *object or updating of *expected. > This doesn't map well to compare-and-swap hardware instructions, > where the content of *object would be written to a register, and > later stored into *expected by a separate instruction. What I think you mean is that it /might/ not map nicely onto an unprotected compare-and-swap operation, depending on context. More on that point given below. > Of course, this difference only matters if it is observable. I > think it is observable if just after the compare-and swap, a second > thread writes *object, while the first thread has already done the > comparison, but not yet updated *expected. Yes, in the worst case the combination of compare-and-swap and (possible) subsequent update of *expected needs to be wrapped in a protective shell covered by an inter-thread mutex, to prevent other threads from breaking the atomicity requirements. But please note: in the worst case. See below. > Then e.g. a thread thread could read the new value from > *object, but the old one from *expected, which is not possible > if atomic_compare_exchange_strong is fully atomic (i.e. also > wrt. *expected). Consider the following function (disclaimer: not compiled): void update_atomic( volatile A* it, C new_from_old( C ) ){ C old = { 0 }, new; do { new = new_from_old( old ); } while( ! atomic_compare_exchange_strong( it, &old, new ) ); } An implementation may compile this function using an inline compare-and-swap, with no surrounding protection of using an inter-thread mutex. A surrounding mutex call is not needed, because the variable 'old' cannot be affected by what other threads are doing (at least not in a way that has defined behavior), and implementations can know that based on the code in the function definition. > Is my understanding here correct (I'm not an expert on atomics)? My comments above are made primarily based on what I believe the C standard requires, and not so much on trying to discern what I think your understanding may be. If what I've said helps your understanding then that's great but my emphasis has been on explicating the consequences of what is in the C standard.