1

This is a rather basic question regarding the memory ordering guarantees inside the Rust async ecosystems. However, I don't seem to find a clear answer anywhere.

C++ memory ordering specifies the memory ordering from a thread-based perspective:

Release-Acquire ordering

If an atomic store in thread A is tagged memory_order_release and an atomic load in thread B from the same variable is tagged memory_order_acquire, all memory writes (non-atomic and relaxed atomic) that happened-before the atomic store from the point of view of thread A, become visible side-effects in thread B. That is, once the atomic load is completed, thread B is guaranteed to see everything thread A wrote to memory.

Various Rust async executors supports moving tasks across thread at .await points. A natural scenario would be:

  1. Task A was at Thread 1 and performed a Relaxed store at variable x.
  2. Task A hit a .await point
  3. Task A was moved to Thread 2 and awaked. And then performed a Release store at variable y.
  4. Task B was at Thread 3 and performed an Acquire load at variable y.

The question is: Is the Relaxed store from Thread 1 guaranteed to be visible in Task B after the Acquire load? E.g. if Task B at Thread 3 performed an Relaxed load at variable x afterwards, is it guaranteed that Task A's side effect will be visible?

This should depend on the implementation of synchronization mechanisms inside each async executor's task scheduler. Anyone familiar with them is appreciated.

First_Strike
  • 1,029
  • 1
  • 10
  • 27

1 Answers1

0

Comment from Tokio maintainer:

When the task is moved from one thread to another, there is a synchronizes-with relation between the things before and after the .await. Depending on the exact circumstances, the synchronization comes from a mutex or an atomic using acquire & release semantics.

In C++ memory spec, synchronizse-with guarantees inter-thread happens-before. Therefore at least in Tokio, it should be safe to ignore memory ordering impact from a .await point.

First_Strike
  • 1,029
  • 1
  • 10
  • 27
  • 1
    All runtimes guarantee this because polling a future requires a mutable reference to it, and the happens-before relationship follows from the uniqueness guarantees of mutable references. It would be undefined behavior for a runtime to not make this guarantee. – Alice Ryhl Feb 09 '22 at 12:03
  • Yes, I believe the same way. Uniqueness guarantee does lead to *happens-before*. However, I am mostly concerned about whether it is possible that any runtime unsafely optimize some Futures using relaxed atomics, since the nomicon does not seem to have defined this behavior. – First_Strike Feb 09 '22 at 12:30
  • Data races are the _first_ thing on the list of [behavior considered undefined](https://doc.rust-lang.org/reference/behavior-considered-undefined.html), and what you described would be a data race on the future. There is no runtime out there that does that (plus/minus bugs). – Alice Ryhl Feb 09 '22 at 18:20