jaulib v1.3.0
Jau Support Library (C++, Java, ..)
test_mm_sc_drf_00.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 <memory>
30
31#include <thread>
32#include <pthread.h>
33
35
37
38using namespace jau;
39
40static int loops = 10;
41
42/**
43 * test_mm_sc_drf_00: Testing SC-DRF non-atomic global read and write within an atomic acquire/release critical block.
44 * <p>
45 * Modified non-atomic memory within the atomic acquire (load) and release (store) block,
46 * must be visible for all threads according to memory model (MM) Sequentially Consistent (SC) being data-race-free (DRF).
47 * <br>
48 * See Herb Sutter's 2013-12-23 slides p19, first box "It must be impossible for the assertion to fail – wouldn’t be SC.".
49 * </p>
50 * <p>
51 * This test's threads utilize a spin-lock, waiting for their turn.
52 * Such busy cycles were chosen to simplify the test and are not recommended
53 * as they expose poor performance on a high thread-count and hence long 'working thread pipe'.
54 * </p>
55 * See 'test_mm_sc_drf_01' implementing same test using mutex-lock and condition wait.
56 */
58 private:
59 enum Defaults : int {
60 array_size = 10
61 };
62 constexpr int number(const Defaults rhs) noexcept {
63 return static_cast<int>(rhs);
64 }
65
66 int value1 = 0;
67 int array[array_size] = { 0 };
68 sc_atomic_int sync_value;
69
70 void reset(int v1, int array_value) {
71 int _sync_value = sync_value; // SC-DRF acquire atomic
72 (void) _sync_value;
73 value1 = v1;
74 for(int & i : array) {
75 i = array_value;
76 }
77 sync_value = v1; // SC-DRF release atomic
78 }
79
80 void putThreadType01(int _len, int startValue) {
81 const int len = std::min(number(array_size), _len);
82 {
83 int _sync_value = sync_value; // SC-DRF acquire atomic
84 (void) _sync_value;
85 _sync_value = startValue;
86 for(int i=0; i<len; i++) {
87 array[i] = _sync_value+i;
88 }
89 value1 = startValue;
90 sync_value = _sync_value; // SC-DRF release atomic
91 }
92 }
93 void getThreadType01(const std::string& msg, int _len, int startValue) {
94 const int len = std::min(number(array_size), _len);
95
96 int _sync_value;
97 while( startValue != ( _sync_value = sync_value ) ) ; // SC-DRF acquire atomic with spin-lock waiting for startValue
98 REQUIRE_MSG(msg+": %s: value at read value1 (sync)", _sync_value == value1);
99 REQUIRE_MSG(msg+": %s: value at read value1 (start)", startValue == value1);
100
101 for(int i=0; i<len; i++) {
102 int v = array[i];
103 REQUIRE_MSG(msg+": %s: sync value at read array #"+std::to_string(i), (_sync_value+i) == v);
104 REQUIRE_MSG(msg+": %s: start value at read array #"+std::to_string(i), (startValue+i) == v);
105 }
106 sync_value = _sync_value; // SC-DRF release atomic
107 }
108
109 void putThreadType11(int indexAndValue) {
110 const int idx = std::min(number(array_size)-1, indexAndValue);
111 {
112 // idx is encoded on sync_value (v) as follows
113 // v > 0: get @ idx = v -1
114 // v < 0: put @ idx = abs(v) -1
115 int _sync_value;
116 // SC-DRF acquire atomic with spin-lock waiting for encoded idx
117 do {
118 _sync_value = sync_value;
119 } while( idx != (_sync_value * -1) - 1 );
120 // INFO_STR("putThreadType11.done @ %d (has %d, exp %d)\n", idx, _sync_value, (idx+1)*-1);
121 _sync_value = idx;
122 value1 = idx;
123 array[idx] = idx; // last written checked first, SC-DRF should handle...
124 sync_value = _sync_value; // SC-DRF release atomic
125 }
126 }
127 void getThreadType11(const std::string& msg, int _idx) {
128 const int idx = std::min(number(array_size)-1, _idx);
129
130 // idx is encoded on sync_value (v) as follows
131 // v > 0: get @ idx = v -1
132 // v < 0: put @ idx = abs(v) -1
133 int _sync_value;
134 // SC-DRF acquire atomic with spin-lock waiting for idx
135 do {
136 _sync_value = sync_value;
137 } while( idx != _sync_value );
138 REQUIRE_MSG(msg+": %s: value at read array (a), idx "+std::to_string(idx), idx == array[idx]); // check last-written first
139 REQUIRE_MSG(msg+": %s: value at read value1, idx "+std::to_string(idx), idx == value1);
140 REQUIRE_MSG(msg+": %s: value at read sync, idx "+std::to_string(idx), idx == _sync_value);
141 // next write encoded idx
142 _sync_value = (idx+1)%array_size;
143 _sync_value = ( _sync_value + 1 ) * -1;
144 // INFO_STR("getThreadType11.done for %d, next %d (v %d)\n", idx, (idx+1)%array_size, _sync_value);
145 value1 = _sync_value;
146 sync_value = _sync_value; // SC-DRF release atomic
147 }
148
149
150 public:
151
153 : value1(0), sync_value(0) {}
154
156 INFO_STR("\n\ntest01_Read1Write1.a\n");
157 reset(0, 1010);
158
159 std::thread getThread01(&TestMemModelSCDRF00::getThreadType01, this, "test01.get01", array_size, 3); // @suppress("Invalid arguments")
160 std::thread putThread01(&TestMemModelSCDRF00::putThreadType01, this, array_size, 3); // @suppress("Invalid arguments")
161 putThread01.join();
162 getThread01.join();
163 }
164
166 INFO_STR("\n\ntest01_Read2Write1.a\n");
167 reset(0, 1021);
168 {
169 std::thread getThread00(&TestMemModelSCDRF00::getThreadType01, this, "test01.get00", array_size, 4); // @suppress("Invalid arguments")
170 std::thread getThread01(&TestMemModelSCDRF00::getThreadType01, this, "test01.get01", array_size, 4); // @suppress("Invalid arguments")
171 std::thread putThread01(&TestMemModelSCDRF00::putThreadType01, this, array_size, 4); // @suppress("Invalid arguments")
172 putThread01.join();
173 getThread00.join();
174 getThread01.join();
175 }
176
177 INFO_STR("\n\ntest01_Read2Write1.b\n");
178 reset(0, 1022);
179 {
180 std::thread putThread01(&TestMemModelSCDRF00::putThreadType01, this, array_size, 5); // @suppress("Invalid arguments")
181 std::thread getThread00(&TestMemModelSCDRF00::getThreadType01, this, "test01.get00", array_size, 5); // @suppress("Invalid arguments")
182 std::thread getThread01(&TestMemModelSCDRF00::getThreadType01, this, "test01.get01", array_size, 5); // @suppress("Invalid arguments")
183 putThread01.join();
184 getThread00.join();
185 getThread01.join();
186 }
187 }
188
190 INFO_STR("\n\ntest02_Read4Write1\n");
191 reset(0, 1030);
192
193 std::thread getThread01(&TestMemModelSCDRF00::getThreadType01, this, "test02.get01", array_size, 6); // @suppress("Invalid arguments")
194 std::thread getThread02(&TestMemModelSCDRF00::getThreadType01, this, "test02.get02", array_size, 6); // @suppress("Invalid arguments")
195 std::thread putThread01(&TestMemModelSCDRF00::putThreadType01, this, array_size, 6); // @suppress("Invalid arguments")
196 std::thread getThread03(&TestMemModelSCDRF00::getThreadType01, this, "test02.get03", array_size, 6); // @suppress("Invalid arguments")
197 std::thread getThread04(&TestMemModelSCDRF00::getThreadType01, this, "test02.get04", array_size, 6); // @suppress("Invalid arguments")
198 putThread01.join();
199 getThread01.join();
200 getThread02.join();
201 getThread03.join();
202 getThread04.join();
203 }
204
206 INFO_STR("\n\ntest11_Read10Write10\n");
207 reset(-1, 1110); // start put idx 0
208
209 std::thread reader[array_size];
210 std::thread writer[array_size];
211 for(int i=0; i<number(array_size); i++) {
212 reader[i] = std::thread(&TestMemModelSCDRF00::getThreadType11, this, "test11.get11", i); // @suppress("Invalid arguments") // @suppress("Symbol is not resolved")
213 }
214 for(int i=0; i<number(array_size); i++) {
215 writer[i] = std::thread(&TestMemModelSCDRF00::putThreadType11, this, i); // @suppress("Invalid arguments") // @suppress("Symbol is not resolved")
216 }
217 for(int i=0; i<number(array_size); i++) {
218 writer[i].join();
219 }
220 for(int i=0; i<number(array_size); i++) {
221 reader[i].join();
222 }
223 }
224
226 INFO_STR("\n\ntest12_Read10Write10\n");
227 reset(-1, 1120); // start put idx 0
228
229 std::thread reader[array_size];
230 std::thread writer[array_size];
231 for(int i=0; i<number(array_size); i++) {
232 writer[i] = std::thread(&TestMemModelSCDRF00::putThreadType11, this, i); // @suppress("Invalid arguments") // @suppress("Symbol is not resolved")
233 }
234 for(int i=0; i<number(array_size); i++) {
235 reader[i] = std::thread(&TestMemModelSCDRF00::getThreadType11, this, "test12.get11", i); // @suppress("Invalid arguments") // @suppress("Symbol is not resolved")
236 }
237 for(int i=0; i<number(array_size); i++) {
238 writer[i].join();
239 }
240 for(int i=0; i<number(array_size); i++) {
241 reader[i].join();
242 }
243 }
244
245 void test_list() {
246 for(int i=loops; i>0; i--) { test01_Read1Write1(); }
247 for(int i=loops; i>0; i--) { test02_Read2Write1(); }
248 for(int i=loops; i>0; i--) { test03_Read4Write1(); }
249 for(int i=loops; i>0; i--) { test11_Read10Write10(); }
250 for(int i=loops; i>0; i--) { test12_Read10Write10(); }
251 }
252};
253
254METHOD_AS_TEST_CASE( TestMemModelSCDRF00::test_list, "Test TestMemModelSCDRF 00- test_list");
#define INFO_STR(msg)
Definition: catch2_ext.hpp:75
#define REQUIRE_MSG(MSG,...)
Definition: catch2_ext.hpp:58
test_mm_sc_drf_00: Testing SC-DRF non-atomic global read and write within an atomic acquire/release c...
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
METHOD_AS_TEST_CASE(TestMemModelSCDRF00::test_list, "Test TestMemModelSCDRF 00- test_list")
static int loops