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