-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathAtomicMemoryOrder_ReadModifyWrite.cpp
More file actions
86 lines (73 loc) · 3.2 KB
/
Copy pathAtomicMemoryOrder_ReadModifyWrite.cpp
File metadata and controls
86 lines (73 loc) · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/**
* Demonstrate how to use memory_order_acq_rel (or read-modify-write operation).
*
* Imagine a case when need to load, then modify value then store. Use this memory order.
*
* The difference between memory_order_acq_rel and by default memory_order_seq_cst is that the former
* is relative to the atomic variable, but the latter is globally. Both for read/write. Check
* AtomicMemoryOrder_MemoryOrderSeqCst.cpp for the later usage.
*
*/
#include <iostream>
#include <atomic>
#include <thread>
#include <chrono>
std::atomic<int> data;
std::atomic<bool> readyFlag;
// thread worker to set data value
// then also set the readyFlag
void threadWorkerProducer()
{
// delay a short time to simulate thread consumer to wait for a while
std::this_thread::sleep_for(std::chrono::milliseconds(100));
readyFlag.store(true, std::memory_order_release);
data.store(50, std::memory_order_release);
// delay a short time for a second phase
std::this_thread::sleep_for(std::chrono::milliseconds(500));
data.store(100, std::memory_order_release);
std::cout << "Producer thread finished\n";
}
void threadWorkerConsumer()
{
while (!readyFlag.load(std::memory_order_acquire))
{
int expectedDummy = 100;
// compare_exchange_strong compare value of atomic variable to expectedDummy.
// If both are equal, then it will assign 200 as value to such atomic variable via memory
// order std::memory_order_acq_rel.
//
// But if it isn't equal, then it will assign current value from atomic variable to expectedDummy
// via memory order of std::memory_order_release.
//
// I think this mechasim which requires real variable to be the first parameter as we will
// most likely need to peek into current value of such atomic variable, and make any use of it (if any).
// So we have responsibility to set value of expected variable back to what we need to be checked
// against at the first place provided that we use while loop to keep checking.
while (!data.compare_exchange_strong(expectedDummy, 200, std::memory_order_acq_rel, std::memory_order_release))
{
// reset expected value back to normal
// as if the case is false then its value is updated to be whatever value 'data'
// currently has
expectedDummy = 100;
}
std::cout << "Modified 'data' value to 200\n";
}
std::cout << "Consumer thread finished\n";
}
int main()
{
// set the intial state of ready flag
// use relaxed memory model as we're sure there's no contension,
readyFlag.store(false, std::memory_order_relaxed);
std::thread t1(threadWorkerProducer);
std::thread t2(threadWorkerConsumer);
t1.join();
t2.join();
// notice we use std::endl here only one place, but '\n' for all the less.
// This is due to we have only one console, thus flushing it might mess with output as
// multiple threads also try to print something out possibly at the same time.
//
// Thus for consistency output, we flush via std::endl at this place only.
std::cout << "[Main thread] data: " << data.load(std::memory_order_acquire) << std::endl;
return 0;
}