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