Re: Inconsistent use of "volatile" when accessing shared memory?

From: Jeff Davis <pgsql(at)j-davis(dot)com>
To: Andres Freund <andres(at)anarazel(dot)de>
Cc: pgsql-hackers(at)postgresql(dot)org
Subject: Re: Inconsistent use of "volatile" when accessing shared memory?
Date: 2023-11-04 00:44:44
Message-ID: 1b9325d827558eb03a59c5281abed35ad75b165e.camel@j-davis.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On Fri, 2023-11-03 at 15:59 -0700, Andres Freund wrote:
> I don't think so. We used to use volatile for most shared memory
> accesses, but
> volatile doesn't provide particularly useful semantics - and
> generates
> *vastly* slower code in a lot of circumstances. Most of that usage
> predates
> spinlocks being proper compiler barriers, 

A compiler barrier doesn't always force the compiler to generate loads
and stores, though.

For instance (example code I placed at the bottom of xlog.c):

typedef struct DummyStruct {
XLogRecPtr recptr;
} DummyStruct;
extern void DummyFunction(void);
static DummyStruct Dummy = { 5 };
static DummyStruct *pDummy = &Dummy;
void
DummyFunction(void)
{
while(true)
{
pg_compiler_barrier();
pg_memory_barrier();
if (pDummy->recptr == 0)
break;
pg_compiler_barrier();
pg_memory_barrier();
}
}

Generates the following code (clang -O2):

000000000016ed10 <DummyFunction>:
16ed10: f0 83 04 24 00 lock addl $0x0,(%rsp)
16ed15: f0 83 04 24 00 lock addl $0x0,(%rsp)
16ed1a: eb f4 jmp 16ed10 <DummyFunction>
16ed1c: 0f 1f 40 00 nopl 0x0(%rax)

Obviously this is an oversimplified example and if I complicate it in
any number of ways then it will start generating actual loads and
stores, and then the compiler and memory barriers should do their job.

> Note that use of volatile does *NOT* guarantee anything about memory
> ordering!

Right, but it does force loads/stores to be emitted by the compiler;
and without loads/stores a memory barrier is useless.

I understand that my example is too simple and I'm not claiming that
there's a problem. I'd just like to understand the key difference
between my example and what we do with XLogCtl.

Another way to phrase my question: under what specific circumstances
must we use something like UINT32_ACCESS_ONCE()? That seems to be used
for local pointers, but it's not clear to me exactly why that matters.
Intuitively, access through a local pointer seems much more likely to
be optimized and therefore more dangerous, but that doesn't imply that
access through global variables is not dangerous.

Regards,
Jeff Davis

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Andres Freund 2023-11-04 00:51:05 Re: Explicitly skip TAP tests under Meson if disabled
Previous Message Andres Freund 2023-11-04 00:43:48 Re: Improve WALRead() to suck data directly from WAL buffers when possible