|
|
Subscribe / Log in / New account

Lockless patterns: relaxed access and partial memory barriers

Lockless patterns: relaxed access and partial memory barriers

Posted Feb 27, 2021 23:11 UTC (Sat) by randomguy3 (subscriber, #71063)
In reply to: Lockless patterns: relaxed access and partial memory barriers by PengZheng
Parent article: Lockless patterns: relaxed access and partial memory barriers

My understanding is that if READ_ONCE(data.y) observes the write of 890, then that means the smp_load_acquire(&sc) before it synchronises with (happens after) smp_store_release(&data.x, 567). That, in turn, means READ_ONCE(sc) will see the write of 3 to sc (or a later write to sc), and we'll go round the loop again.


to post comments

Lockless patterns: relaxed access and partial memory barriers

Posted Feb 27, 2021 23:18 UTC (Sat) by randomguy3 (subscriber, #71063) [Link] (6 responses)

Oh, no, wait, the wording from the first article suggests the store and load have to refer to the same variable/memory location in order to synchronise, which makes sense.

Lockless patterns: relaxed access and partial memory barriers

Posted Feb 28, 2021 4:08 UTC (Sun) by PengZheng (subscriber, #108006) [Link] (5 responses)

If READ_ONCE(data.y) observes the write of 890, then READ_ONCE(sc) >= 3. Why?
Because smp_store_release(&data.x, 567) is a stronger form of

smp_wmb();
WRITE_ONCE(data.x, 567);

copy.x = smp_load_acquire(&data.x) is a stronger form of

copy.x = READ_ONCE(data.x)
smp_rmb();

smp_wmb() makes both WRITE_ONCE(data.x, 567) and WRITE_ONCE(data.y, 890) into release operations, while smp_rmb() makes both READ_ONCE(data.y) and READ_ONCE(data.x) into acquire operations.

Lockless patterns: relaxed access and partial memory barriers

Posted Feb 28, 2021 4:19 UTC (Sun) by PengZheng (subscriber, #108006) [Link]

WRITE_ONCE(data.y, 890) after smp_wmb() sync with READ_ONCE(data.y) before smp_rmb().

In thread 1 we have
WRITE_ONCE(sc, sc + 1); // sc == 3
happens before
smp_wmb();
WRITE_ONCE(data.y, 890);

In thread 2 we have
copy.y = READ_ONCE(data.y);
smp_rmb();
happens before READ_ONCE(sc)

Thus WRITE_ONCE(sc, sc + 1); // sc == 3
happens before READ_ONCE(sc) in thread 2.

Uh-oh

Posted Feb 28, 2021 6:33 UTC (Sun) by pbonzini (subscriber, #60935) [Link] (3 responses)

> "smp_store_release(&data.x, 567)" is a stronger form of "smp_wmb(); WRITE_ONCE(data.x, 567);"

This is not entirely correct, because barriers are bidirectional while store-release is unidirectional.

I tried putting this in CPPMEM:

int main() {
  atomic_int sc = 2;
  atomic_int x = 123;
  atomic_int y = 456;
  {{{
   {
     sc.store(3, memory_order_relaxed);
     x.store(567, memory_order_release);
     y.store(890, memory_order_relaxed);
     // sc.store(4, memory_order_release);
   }
  |||
   {
     sc.load(memory_order_acquire).readsvalue(2);
     r1 = y.load(memory_order_relaxed);
     r2 = x.load(memory_order_acquire).readsvalue(123);
   }
  }}};
  return 0;
}

and you are probably correct. The simplest fix is to use load-acquire/store-release for y as well. I'll write up a fix and ask our editor to update the article.

wmb, smp_wmb, and store-release

Posted Mar 1, 2021 3:00 UTC (Mon) by PengZheng (subscriber, #108006) [Link]

> This is not entirely correct, because barriers are bidirectional while store-release is unidirectional.

Thanks for correcting my misunderstanding. I read the following for a brief explanation of memory barriers:
http://bruceblinn.com/linuxinfo/MemoryBarriers.html

Uh-oh

Posted Mar 1, 2021 8:41 UTC (Mon) by peda (subscriber, #5285) [Link] (1 responses)

Are you sure you're not just putting lipstick on a pig?

Uh-oh

Posted Mar 1, 2021 11:24 UTC (Mon) by pbonzini (subscriber, #60935) [Link]

Indeed. :)

But memory barriers do work, they are bidirectional.


Copyright © 2024, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds