From: | Andres Freund <andres(at)anarazel(dot)de> |
---|---|
To: | Alexander Korotkov <aekorotkov(at)gmail(dot)com> |
Cc: | pgsql-hackers <pgsql-hackers(at)postgresql(dot)org> |
Subject: | Re: pg_atomic_compare_exchange_*() and memory barriers |
Date: | 2025-03-07 17:07:56 |
Message-ID: | oc4iicdkwyhdf5o5vbwsl7jdlqnds37xtf27wuxvhy3abxoo6i@4ek3xp5j6niy |
Views: | Raw Message | Whole Thread | Download mbox | Resend email |
Thread: | |
Lists: | pgsql-hackers |
Hi,
On 2025-03-07 18:39:42 +0200, Alexander Korotkov wrote:
> On Fri, Mar 7, 2025 at 6:02 PM Andres Freund <andres(at)anarazel(dot)de> wrote:
> >
> > On 2025-03-07 17:47:08 +0200, Alexander Korotkov wrote:
> > > While investigating a bug in the patch to get rid of WALBufMappingLock, I
> > > found that the surrounding pg_atomic_compare_exchange_u64() fixes the
> > > problem for me.
> >
> > That sounds more likely to be due to slowing down things enough to make a race
> > less likely to be hit. Or a compiler bug.
> >
> >
> > > That doesn't feel right because, according to the
> > > comments, both pg_atomic_compare_exchange_u32() and
> > > pg_atomic_compare_exchange_u64() should provide full memory barrier
> > > semantics. So, I decided to investigate this further.
> > >
> > > In my case, the implementation of pg_atomic_compare_exchange_u64() is based
> > > on __atomic_compare_exchange_n().
> > >
> > > static inline bool
> > > pg_atomic_compare_exchange_u64_impl(volatile pg_atomic_uint64 *ptr,
> > > uint64 *expected, uint64 newval)
> > > {
> > > AssertPointerAlignment(expected, 8);
> > > return __atomic_compare_exchange_n(&ptr->value, expected, newval, false,
> > > __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
> > > }
> > >
> > > According to the docs __ATOMIC_SEQ_CST enforces total ordering with *all
> > > other __ATOMIC_SEQ_CST operations*. It only says about other
> > > __ATOMIC_SEQ_CST operations; nothing is said about regular reads/writes.
> > > This sounds quite far from our comment, promising full barrier semantics,
> > > which, in my understanding, means the equivalent of
> > > both pg_read_barrier()/pg_write_barrier(), which should barrier *all other
> > > read/writes*.
> >
> > A memory barrier in one process/thread always needs be paired with a barrier
> > in another process/thread. It's not enough to have a memory barrier on one
> > side but not the other.
>
> Yes, there surely should be a memory barrier on another side. But
> does __ATOMIC_SEQ_CST works as a barrier for the regular read/write
> operations on the same side?
Yes, if it's paired with another barrier on the other side. The regular
read/write operations are basically protected transitively, due to
a) An acquire barrier preventing non-atomic reads/writes in the same thread
from appearing to have been moved to before the barrier
b) A release barrier preventing non-atomic reads/writes in the same thread
from appearing to have been moved to after the barrier
c) The other thread being guaranteed a) and b) for the other threads'
non-atomic reads/writes depending on the type of barrier
d) The atomic value itself being guaranteed to be, well, atomic
> > > This function first checks if LSE instructions are present. If so,
> > > instruction LSE casal is used. If not (in my case), the loop of
> > > ldaxr/stlxr is used instead. The documentation says both ldaxr/stlxr
> > > provides one-way barriers. Read/writes after ldaxr will be observed after,
> > > and read/writes before stlxr will be observed before. So, a pair of these
> > > instructions provides a full memory barrier. However, if we don't observe
> > > the expected value, only ldaxr gets executed. So, an unsuccessful
> > > pg_atomic_compare_exchange_u64() attempt will leave us with a one-way
> > > barrier, and that caused a bug in my case.
> >
> > That has to be a compiler bug. We specify __ATOMIC_SEQ_CST for both
> > success_memorder *and* failure_memorder.
> >
> > What compiler & version is this?
>
> I've tried and got the same results with two compilers.
> gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
> Ubuntu clang version 19.1.1 (1ubuntu1~24.04.2)
Thinking more about it I wonder if the behaviour of not doing a release in
case the atomic fails *might* arguably actually be correct - if the
compare-exchange fails, there's nothing that the non-atomic values could be
ordered against.
What is the access pattern and the observed problems with it that made you
look at the disassembly?
Greetings,
Andres Freund
From | Date | Subject | |
---|---|---|---|
Next Message | Pavel Borisov | 2025-03-07 17:08:34 | Re: pg_atomic_compare_exchange_*() and memory barriers |
Previous Message | Jacob Champion | 2025-03-07 17:03:18 | Re: [PATCH] pg_stat_activity: make slow/hanging authentication more visible |