jaulib v1.3.0
Jau Support Library (C++, Java, ..)
test_mm_sc_drf_01.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24#include <cassert>
25#include <cinttypes>
26#include <cstring>
27
28#include <atomic>
29#include <mutex>
30#include <condition_variable>
31#include <memory>
32
33#include <thread>
34#include <pthread.h>
35
37
39
40using namespace jau;
41
42static int loops = 10;
43
44/**
45 * test_mm_sc_drf_01: Testing SC-DRF non-atomic global read and write within a locked mutex critical block.
46 * <p>
47 * Modified non-atomic memory within the locked mutex acquire and release block,
48 * must be visible for all threads according to memory model (MM) Sequentially Consistent (SC) being data-race-free (DRF).
49 * <br>
50 * See Herb Sutter's 2013-12-23 slides p19, first box "It must be impossible for the assertion to fail – wouldn’t be SC.".
51 * </p>
52 * See 'test_mm_sc_drf_00' implementing same test using an atomic acquire/release critical block with spin-lock.
53 */
55 private:
56 enum Defaults : int {
57 array_size = 10
58 };
59 constexpr int number(const Defaults rhs) noexcept {
60 return static_cast<int>(rhs);
61 }
62
63 int value1 = 0;
64 int array[array_size] = { 0 };
65 std::mutex mtx_value;
66 std::condition_variable cvRead;
67 std::condition_variable cvWrite;
68
69 void reset(int v1, int array_value) {
70 std::unique_lock<std::mutex> lock(mtx_value); // SC-DRF acquire and release @ scope exit
71 value1 = v1;
72 for(int & i : array) {
73 i = array_value;
74 }
75 }
76
77 void putThreadType01(int _len, int startValue) {
78 const int len = std::min(number(array_size), _len);
79 {
80 std::unique_lock<std::mutex> lock(mtx_value); // SC-DRF acquire and release @ scope exit
81 for(int i=0; i<len; i++) {
82 array[i] = startValue+i;
83 }
84 value1 = startValue;
85 cvRead.notify_all(); // notify waiting getter
86 }
87 }
88 void getThreadType01(const std::string& msg, int _len, int startValue) {
89 const int len = std::min(number(array_size), _len);
90
91 std::unique_lock<std::mutex> lock(mtx_value); // SC-DRF acquire and release @ scope exit
92 while( startValue != value1 ) {
93 cvRead.wait(lock);
94 }
95 REQUIRE_MSG(msg+": %s: value at read value1 (start)", startValue == value1);
96
97 for(int i=0; i<len; i++) {
98 int v = array[i];
99 REQUIRE_MSG(msg+": %s: start value at read array #"+std::to_string(i), (startValue+i) == v);
100 }
101 }
102
103 void putThreadType11(int indexAndValue) {
104 const int idx = std::min(number(array_size)-1, indexAndValue);
105 {
106 // idx is encoded on sync_value (v) as follows
107 // v > 0: get @ idx = v -1
108 // v < 0: put @ idx = abs(v) -1
109 std::unique_lock<std::mutex> lock(mtx_value); // SC-DRF acquire and release @ scope exit
110 // SC-DRF acquire atomic with spin-lock waiting for encoded idx
111 while( idx != (value1 * -1) - 1 ) {
112 cvWrite.wait(lock);
113 }
114 // INFO_STR("putThreadType11.done @ %d (has %d, exp %d)\n", idx, value1, (idx+1)*-1);
115 value1 = idx;
116 array[idx] = idx; // last written checked first, SC-DRF should handle...
117 cvRead.notify_all();
118 }
119 }
120 void getThreadType11(const std::string& msg, int _idx) {
121 const int idx = std::min(number(array_size)-1, _idx);
122
123 // idx is encoded on sync_value (v) as follows
124 // v > 0: get @ idx = v -1
125 // v < 0: put @ idx = abs(v) -1
126 // SC-DRF acquire atomic with spin-lock waiting for idx
127 std::unique_lock<std::mutex> lock(mtx_value);
128 while( idx != value1 ) {
129 // INFO_STR("getThreadType11.wait for has %d == exp %d\n", value1, idx);
130 cvRead.wait(lock);
131 }
132 REQUIRE_MSG(msg+": %s: value at read array (idx), idx "+std::to_string(idx), idx == array[idx]); // check last-written first
133 REQUIRE_MSG(msg+": %s: value at read value1 (idx), idx "+std::to_string(idx), idx == value1);
134 // next write encoded idx
135 int next_idx = (idx+1)%array_size;
136 next_idx = ( next_idx + 1 ) * -1;
137 // INFO_STR("getThreadType11.done for %d, next %d (v %d)\n", idx, (idx+1)%array_size, next_idx);
138 value1 = next_idx;
139 cvWrite.notify_all();
140 }
141
142
143 public:
144
146 : value1(0) {}
147
149 INFO_STR("\n\ntest01_Read1Write1.a\n");
150 reset(0, 1010);
151
152 std::thread getThread01(&TestMemModelSCDRF01::getThreadType01, this, "test01.get01", array_size, 3); // @suppress("Invalid arguments")
153 std::thread putThread01(&TestMemModelSCDRF01::putThreadType01, this, array_size, 3); // @suppress("Invalid arguments")
154 putThread01.join();
155 getThread01.join();
156 }
157
159 INFO_STR("\n\ntest01_Read2Write1.a\n");
160 reset(0, 1021);
161 {
162 std::thread getThread00(&TestMemModelSCDRF01::getThreadType01, this, "test01.get00", array_size, 4); // @suppress("Invalid arguments")
163 std::thread getThread01(&TestMemModelSCDRF01::getThreadType01, this, "test01.get01", array_size, 4); // @suppress("Invalid arguments")
164 std::thread putThread01(&TestMemModelSCDRF01::putThreadType01, this, array_size, 4); // @suppress("Invalid arguments")
165 putThread01.join();
166 getThread00.join();
167 getThread01.join();
168 }
169
170 INFO_STR("\n\ntest01_Read2Write1.b\n");
171 reset(0, 1022);
172 {
173 std::thread putThread01(&TestMemModelSCDRF01::putThreadType01, this, array_size, 5); // @suppress("Invalid arguments")
174 std::thread getThread00(&TestMemModelSCDRF01::getThreadType01, this, "test01.get00", array_size, 5); // @suppress("Invalid arguments")
175 std::thread getThread01(&TestMemModelSCDRF01::getThreadType01, this, "test01.get01", array_size, 5); // @suppress("Invalid arguments")
176 putThread01.join();
177 getThread00.join();
178 getThread01.join();
179 }
180 }
181
183 INFO_STR("\n\ntest02_Read4Write1\n");
184 reset(0, 1030);
185
186 std::thread getThread01(&TestMemModelSCDRF01::getThreadType01, this, "test02.get01", array_size, 6); // @suppress("Invalid arguments")
187 std::thread getThread02(&TestMemModelSCDRF01::getThreadType01, this, "test02.get02", array_size, 6); // @suppress("Invalid arguments")
188 std::thread putThread01(&TestMemModelSCDRF01::putThreadType01, this, array_size, 6); // @suppress("Invalid arguments")
189 std::thread getThread03(&TestMemModelSCDRF01::getThreadType01, this, "test02.get03", array_size, 6); // @suppress("Invalid arguments")
190 std::thread getThread04(&TestMemModelSCDRF01::getThreadType01, this, "test02.get04", array_size, 6); // @suppress("Invalid arguments")
191 putThread01.join();
192 getThread01.join();
193 getThread02.join();
194 getThread03.join();
195 getThread04.join();
196 }
197
199 INFO_STR("\n\ntest11_Read10Write10\n");
200 reset(-1, 1110);
201
202 std::thread reader[array_size];
203 std::thread writer[array_size];
204 for(int i=0; i<number(array_size); i++) {
205 reader[i] = std::thread(&TestMemModelSCDRF01::getThreadType11, this, "test11.get11", i); // @suppress("Invalid arguments") // @suppress("Symbol is not resolved")
206 }
207 for(int i=0; i<number(array_size); i++) {
208 writer[i] = std::thread(&TestMemModelSCDRF01::putThreadType11, this, i); // @suppress("Invalid arguments") // @suppress("Symbol is not resolved")
209 }
210 for(int i=0; i<number(array_size); i++) {
211 writer[i].join();
212 }
213 for(int i=0; i<number(array_size); i++) {
214 reader[i].join();
215 }
216 }
217
219 INFO_STR("\n\ntest12_Read10Write10\n");
220 reset(-1, 1120);
221
222 std::thread reader[array_size];
223 std::thread writer[array_size];
224 for(int i=0; i<number(array_size); i++) {
225 writer[i] = std::thread(&TestMemModelSCDRF01::putThreadType11, this, i); // @suppress("Invalid arguments") // @suppress("Symbol is not resolved")
226 }
227 for(int i=0; i<number(array_size); i++) {
228 reader[i] = std::thread(&TestMemModelSCDRF01::getThreadType11, this, "test12.get11", i); // @suppress("Invalid arguments") // @suppress("Symbol is not resolved")
229 }
230 for(int i=0; i<number(array_size); i++) {
231 writer[i].join();
232 }
233 for(int i=0; i<number(array_size); i++) {
234 reader[i].join();
235 }
236 }
237
238 void test_list() {
239 for(int i=loops; i>0; i--) { test01_Read1Write1(); }
240 for(int i=loops; i>0; i--) { test02_Read2Write1(); }
241 for(int i=loops; i>0; i--) { test03_Read4Write1(); }
242 for(int i=loops; i>0; i--) { test11_Read10Write10(); }
243 for(int i=loops; i>0; i--) { test12_Read10Write10(); }
244 }
245};
246
247METHOD_AS_TEST_CASE( TestMemModelSCDRF01::test_list, "Test TestMemModelSCDRF 01- test_list");
#define INFO_STR(msg)
Definition: catch2_ext.hpp:75
#define REQUIRE_MSG(MSG,...)
Definition: catch2_ext.hpp:58
test_mm_sc_drf_01: Testing SC-DRF non-atomic global read and write within a locked mutex critical blo...
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
constexpr T min(const T x, const T y) noexcept
Returns the minimum of two integrals (w/ branching) in O(1)
Definition: base_math.hpp:177
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
static int loops
METHOD_AS_TEST_CASE(TestMemModelSCDRF01::test_list, "Test TestMemModelSCDRF 01- test_list")