Хочу убедиться, что правильно понимаю порядки, поэтому спрашиваю ваше мнение о нескольких ситуациях ниже:
1 - Once. Предположим, что надо написать Once только с двумя значениями (WasNotCalled = 0, WasCalled = 1). Для метод call может использовать swap и вернуть предыдущее значение. Но какой порядок нужно использовать в этом swap?
2 - SmartMutex. Предположим, что у нас есть очередь ожидающих задач. Тогда мы можем записать длину этого списка в SmartMutex. Если она равна -1 мы можем захватить блокировку. Если она равна 0, мы можем подождать пару спинов и попробовать снова. Если она больше 0, мы можем только встать в очередь.
Соответственно, нужно написать 2 метода: первый пробует взять лок, но не встаёт в очередь. То есть он должен сделать CAS и посмотреть на вернувшееся значение (если > 0 вернуть None). Какие порядки нужны этому CAS?
Второй метод увеличивает счётчик на один и встаёт в очередь, если предыдущее значение не -1, иначе возвращает блокировку. Какой порядок нужно использовать тут?
Я ничего не знаю про rust, но это же вопрос про memory model, а эта тема уже обсосана C и С++. Например: eel.is/c++draft/atomics.order Самый строгий вариант ордеринга (sequential consistency) будет работать корректно всегда, и если вопрос не стоит как "насколько его можно ослабить", то разумно использовать его. Кроме того, для CAS это же единственная возможная опция, наксколько мне известно.
jcmvbkbc, использовать всегда самый строгий вариант является довольно спорным решением. Хотелось бы использовать настолько слабый вариант, насколько это возможно.
Я тоже читал, что Rust использует такую же модель, как в C, но в Rust можно использовать не только SeqCst в CAS операциях. По крайней мере в compare_exchange, который используется как альтернатива устаревшему compare_and_swap. Но он принимает 2 порядка, так что может иметь и другие различия. Например, для захвата блокировки обычно используют Acquire + Relaxed.
Eugene Usachev, говоря, что для CAS это единственная опция я имел в виду вот что. Acquire говорит о том, что операция чтения должна обозреть произошедшие записи в память, Release говорит о том, что операция записи должна опубликовать произведённые изменения. Поскольку CAS -- это обе эти операции, то для него имееют смысл только Relaxed и SeqCst. Relaxed -- это по сути ничего, ни упорядочивания, ни публикации изменений. Acquire и Release имеет смысл применять только тогда, когда между ними есть что-то ещё. Это не твой первый случай (где нужно просто атомарно установить значение 1 если было 0 и вернуть, что было). И это не твой второй случай (где нужно в одном месте атомарно увеличить значение и вернуть что было, а в другом -- атомарно уменьшить).