Skip to content

Commit cdbb0d3

Browse files
committed
Stores to m_leftright field need seq_cst
With a test too. The test needs -O3 on my mache to repro. Closes #3
1 parent 37084c1 commit cdbb0d3

File tree

4 files changed

+49
-5
lines changed

4 files changed

+49
-5
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 3.2.2)
22

33
set(LEFTRIGHT_VERSION_MAJOR 1)
44
set(LEFTRIGHT_VERSION_MINOR 1)
5-
set(LEFTRIGHT_VERSION_PATCH 1)
5+
set(LEFTRIGHT_VERSION_PATCH 3)
66
set(LEFTRIGHT_VERSION ${LEFTRIGHT_VERSION_MAJOR}.${LEFTRIGHT_VERSION_MINOR}.${LEFTRIGHT_VERSION_PATCH})
77

88
project(leftright VERSION ${LEFTRIGHT_VERSION} LANGUAGES CXX)

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ want to run the unit tests or generate docs you can use the cmake build. To
3737
perform an out-of-tree build
3838

3939
$ cd "$(mktemp -d)"
40-
$ cmake -DCMAKE_BUILD_TYPE=debug /path/to/leftright/repository
40+
$ cmake -DCMAKE_BUILD_TYPE=release /path/to/leftright/repository
4141
$ make && make test
4242
$ make docs # generates doxygen documentation under docs/
4343

44+
Use of release builds is suggested here as optimizations are more likely to
45+
trigger concurrency bugs.

include/mpm/leftright.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,14 +291,14 @@ namespace mpm
291291
if(read_left == m_leftright.load(std::memory_order_relaxed))
292292
{
293293
f(m_right);
294-
m_leftright.store(read_right, std::memory_order_release);
294+
m_leftright.store(read_right, std::memory_order_seq_cst);
295295
toggle_reader_registry(xlock);
296296
return f(m_left);
297297
}
298298
else
299299
{
300300
f(m_left);
301-
m_leftright.store(read_left, std::memory_order_release);
301+
m_leftright.store(read_left, std::memory_order_seq_cst);
302302
toggle_reader_registry(xlock);
303303
return f(m_right);
304304
}

test/test_leftright.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include <mpm/leftright.h>
2+
#include <atomic>
23
#include <map>
3-
#include <string>
44
#include <catch2/catch.hpp>
55

66

@@ -78,6 +78,48 @@ TEST_CASE("noexcept specs", "[mpm.leftright]")
7878
//auto null_modifier = [](lrint::reference i){ return i; };
7979
//CHECK(0 == lri.modify(null_modifier));
8080
}
81+
TEST_CASE("hammer it", "[mpm.leftright]")
82+
{
83+
// repro of https://github.com/mmcshane/leftright/issues/3
84+
85+
// this test may require compiler optimizations (e.g. -O3) to fail as
86+
// expected. To induce failure, change the memory order used in the stores
87+
// to basic_leftright::m_leftright performed in the two branches of
88+
// basic_leftright::modify to std::memory_order_release.
89+
90+
struct Bigish {
91+
public:
92+
void add1() { a1 += 1; a2 += 1; a3 += 1; a4 += 1; }
93+
94+
alignas(MPM_LEFTRIGHT_CACHE_LINE_SIZE) int a1 = 0;
95+
alignas(MPM_LEFTRIGHT_CACHE_LINE_SIZE) int a2 = 0;
96+
alignas(MPM_LEFTRIGHT_CACHE_LINE_SIZE) int a3 = 0;
97+
alignas(MPM_LEFTRIGHT_CACHE_LINE_SIZE) int a4 = 0;
98+
};
99+
100+
mpm::leftright<Bigish> lrb{};
101+
102+
std::atomic_uint_fast32_t violations{0};
103+
std::thread reader([&] {
104+
for(int i = 0; i < 10000000; i++) {
105+
auto observed = lrb.observe([](const Bigish& val) noexcept{ return val; });
106+
auto a1 = observed.a1;
107+
if (observed.a2 != a1 || observed.a3 != a1 || observed.a4 != a1) {
108+
violations.fetch_add(1, std::memory_order_relaxed);
109+
}
110+
}
111+
});
112+
113+
Bigish in{};
114+
for(int i = 0; i < 10000000; i++){
115+
in.add1();
116+
lrb.modify([&](Bigish& b)noexcept{ b = in; });
117+
}
118+
119+
reader.join();
120+
121+
CHECK(0 == violations.load(std::memory_order_relaxed));
122+
}
81123

82124
/*
83125
struct unsafe_reader_registry

0 commit comments

Comments
 (0)