How to guarantee that load completes before store occurs?

此生再无相见时 提交于 2020-03-03 07:03:19

问题


In the following code, how could one ensure that ptr not incremented until after *ptr has been loaded/assigned/"extracted"?

extern int arr[some_constexpr]; // assume pre-populated
extern int* ptr; // assume points to non-atomic arr
int a = *ptr;
// want "memory barrier/fence" here
++ptr;

Would an atomic pointer ensure the correct ordering/sequencing?

#include <atomic>

extern int arr[some_constexpr];
extern std::atomic<int*> ptr;
int a = *(ptr.load());
// implicit "memory barrier" achieved here by use of atomics?
ptr.store(ptr + 1);

This relates to a lock-free queue shared between two threads. I want to ensure that the data associated with the pointer is not lost/corrupted before updating the pointer.


回答1:


When ptr is std::atomic<int*>, ++ptr, or ptr++ or ptr.fetch_add(1, std::memory_order_acq_rel) ensure that no preceding/following loads/stores get reordered past/before this operation.

++ptr, or ptr++ are essentially ptr.fetch_add(1, std::memory_order_seq_cst) and std::memory_order_seq_cst is almost always an overkill (cannot give an example where it is not).

Even more efficient single reader is:

int arr[some_constexpr];
std::atomic<int*> ptr;

int* p = ptr.load(std::memory_order_acquire);
int element = *p;
ptr.store(p + 1, memory_order_release);

The above is basically how boost::lockfree::spsc_queue is implemented.


As a side note, boost::lockfree::spsc_queue is a true wait-free (strongest non-blocking guarantee of progress) queue. What push/pop operations do is 1 relaxed load, 1 acquire load and 1 release store and it is fundamentally not possible to implement a single-producer-single-consumer queue faster than that (sans implementation quality defects) with FIFO order guarantee. It is often used as a benchmark for all other queues. You may like to look into it.




回答2:


According to Herb Sutter's talk (which I highly recommend for trying to understand this stuff), e.g. see at time 36:25-45:25, the atomic example given in the OP will suffice to ensure that a is assigned before ptr is incremented.

This is because incrementing ptr is a store (since it is being written), and therefore a "release" given the default memory order of std::memory_order_seq_cst (but would also apply for *_acq_rel or *_release memory order in this case). This means that nothing occurring before the increment/store/release of ptr can be re-ordered to occur after the increment/store/release of ptr, whether explicitly or implicitly by the compiler, processor, and/or cache.

No explicit memory fence is required beyond those implied by atomic load/stores, and they are actually discouraged in the talk.



来源:https://stackoverflow.com/questions/60420163/how-to-guarantee-that-load-completes-before-store-occurs

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!