Skip to content

Commit fbd384d

Browse files
authored
Merge pull request #1400 from ccotter/relacy
Proof of concept: Relacy tests
2 parents 2de858b + 37ef4c4 commit fbd384d

File tree

8 files changed

+203
-6
lines changed

8 files changed

+203
-6
lines changed

include/exec/__detail/__bwos_lifo_queue.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ namespace exec::bwos {
152152
block_type(block_size, allocator),
153153
allocator_of_t<block_type>(allocator))
154154
, mask_(blocks_.size() - 1) {
155-
blocks_[owner_block_].reclaim();
155+
blocks_[owner_block_.load()].reclaim();
156156
}
157157

158158
template <class Tp, class Allocator>
@@ -468,4 +468,4 @@ namespace exec::bwos {
468468
auto lifo_queue<Tp, Allocator>::block_type::is_stealable() const noexcept -> bool {
469469
return steal_tail_.load(std::memory_order_acquire) != block_size();
470470
}
471-
} // namespace exec::bwos
471+
} // namespace exec::bwos

include/exec/async_scope.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,7 @@ namespace exec {
780780
// after this, which means we can rely on its self-ownership to ensure
781781
// that it is eventually deleted
782782
stdexec::start(
783-
*new __op_t{nest(static_cast<_Sender&&>(__sndr)), static_cast<_Env&&>(__env), &__impl_});
783+
*(new __op_t{nest(static_cast<_Sender&&>(__sndr)), static_cast<_Env&&>(__env), &__impl_}));
784784
}
785785

786786
template <__movable_value _Env = empty_env, sender_in<__env_t<_Env>> _Sender>

include/exec/start_now.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ namespace exec {
7171
}
7272

7373
void __complete() noexcept {
74-
if (--__pending_ == 0) {
74+
if (__pending_.fetch_sub(1) == 1) {
7575
auto __joiner = __joiner_.exchange(nullptr);
7676
if (__joiner) {
7777
__joiner->join();

include/stdexec/__detail/__when_all.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ namespace stdexec {
191191

192192
template <class _Receiver>
193193
void __arrive(_Receiver& __rcvr) noexcept {
194-
if (0 == --__count_) {
194+
if (1 == __count_.fetch_sub(1)) {
195195
__complete(__rcvr);
196196
}
197197
}
@@ -361,7 +361,7 @@ namespace stdexec {
361361
} else if constexpr (!__same_as<decltype(_State::__values_), __ignore>) {
362362
// We only need to bother recording the completion values
363363
// if we're not already in the "error" or "stopped" state.
364-
if (__state.__state_ == __started) {
364+
if (__state.__state_.load() == __started) {
365365
auto& __opt_values = __tup::get<__v<_Index>>(__state.__values_);
366366
using _Tuple = __decayed_tuple<_Args...>;
367367
static_assert(

test/rrd/Makefile

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# User-customizable variables:
2+
CXX ?= c++
3+
CXX_STD ?= c++20
4+
CXXFLAGS ?= -I relacy -I relacy/relacy/fakestd -O1 -std=$(CXX_STD) -I ../../include -I ../../test -g
5+
DEPFLAGS ?= -MD -MF $(@).d -MP -MT $(@)
6+
build_dir = build
7+
8+
.SECONDARY:
9+
10+
test_programs = split async_scope
11+
12+
test_exe_files = $(foreach name,$(test_programs),$(build_dir)/$(name))
13+
14+
exe_files = $(test_exe_files)
15+
o_files = $(exe_files:=.cpp.o)
16+
17+
ansi_term_csi = [
18+
ansi_term_bold = $(ansi_term_csi)1m
19+
ansi_term_green = $(ansi_term_csi)32m
20+
ansi_term_red = $(ansi_term_csi)31m
21+
ansi_term_reset = $(ansi_term_csi)m
22+
23+
COMPILE.cpp = $(CXX) $(DEPFLAGS) $(CXXFLAGS) -c
24+
LINK.cpp = $(CXX) $(CXXFLAGS)
25+
26+
.PHONY: all
27+
all: tests
28+
29+
.PHONY: tests
30+
tests: $(test_exe_files)
31+
32+
$(build_dir)/%.check-result: $(build_dir)/% always-run
33+
@ \
34+
printf '%s%s ...%s\n' $(ansi_term_bold) $(*) $(ansi_term_reset) >&2; \
35+
$(<); \
36+
status="$${?}"; \
37+
printf %d "$${status}" >$(@); \
38+
if [ "$${status}" -eq 0 ]; then \
39+
printf '%s%s %s%s\n' $(ansi_term_green) $(*) OK $(ansi_term_reset); \
40+
else \
41+
printf '%s%s %s%s\n' $(ansi_term_red) $(*) FAIL $(ansi_term_reset); \
42+
fi >&2; \
43+
exit "$${status}"
44+
45+
$(build_dir)/%: $(build_dir)/%.cpp.o
46+
$(LINK.cpp) $(^) -o $(@)
47+
48+
$(build_dir)/%.cpp.o: %.cpp
49+
@mkdir -p $(dir $(@))
50+
$(COMPILE.cpp) -o $(@) $(<)
51+
52+
.PHONY: clean
53+
clean:
54+
rm -fr -- $(build_dir)/
55+
56+
.PHONY: always-run
57+
always-run:
58+
59+
-include $(o_files:=.d)

test/rrd/README.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## Relacy tests
2+
3+
[Relacy (RRD)](https://www.1024cores.net/home/relacy-race-detector/rrd-introduction)
4+
is a data race detector. It replaces the OS scheduler with an scheduler that
5+
explores many different thread interleavings, and logs detected races or assertion
6+
failures. Relacy can also simulate relaxed hardware by simulating old values of a
7+
variable as allowed by the C++11 memory model.
8+
9+
Relacy requires a specialized build. In particular, it is a header only library that
10+
replaces the standard library and pthread APIs at compile time. Since it places some
11+
standard library includes, writing new tests may require working around certain
12+
limitations in terms of what the replacement headers and accompanying runtime can
13+
support. For example, Relacy's atomic replacements cannot support `++x`, so the
14+
stdexec library could needs to use `x.fetch_add(1)` to be compatible with Relacy.
15+
16+
## Instructions
17+
18+
Run the following commands from within this directory (`./tests/rrd`).
19+
20+
```
21+
# TODO: Merge patches into upstream Relacy @ dvyukov's version
22+
git clone -b stdexec https://github.com/ccotter/relacy
23+
CXX=g++-11 make -j 4
24+
./build/split
25+
```
26+
27+
## Recommended use
28+
29+
The Relacy tests can be manually built and executed. New tests can be written for
30+
new algorithms, or new use cases in the stdexec library.
31+
32+
At this time, integrating the tests into CI is not yet recommended. If we can figure
33+
out a more stable build on all environments/compilers, we should revisit this.
34+
35+
## Supported platforms
36+
37+
The stdexec Relacy tests have been verified to build and run on
38+
* Linux based GCC+11 with libstdc++ (`x86_64`)
39+
* Mac with Apple Clang 15 with libc++ (`x86_64`)
40+
41+
G++12 and newer are known to have issues that could be addressed with patches
42+
to Relacy.

test/rrd/async_scope.cpp

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#include "../../relacy/relacy_std.hpp"
2+
#include "../../relacy/relacy_cli.hpp"
3+
4+
#include <stdexec/execution.hpp>
5+
#include <exec/async_scope.hpp>
6+
#include <exec/static_thread_pool.hpp>
7+
#include <test_common/schedulers.hpp>
8+
#include <test_common/type_helpers.hpp>
9+
10+
#include <chrono>
11+
#include <random>
12+
#include <iostream>
13+
14+
using rl::nvar;
15+
using rl::nvolatile;
16+
using rl::mutex;
17+
18+
namespace ex = stdexec;
19+
using exec::async_scope;
20+
21+
struct async_scope_bug : rl::test_suite<async_scope_bug, 1>
22+
{
23+
static size_t const dynamic_thread_count = 2;
24+
25+
void thread(unsigned)
26+
{
27+
exec::static_thread_pool ctx{1};
28+
29+
ex::scheduler auto sch = ctx.get_scheduler();
30+
31+
exec::async_scope scope;
32+
std::atomic_bool produced{false};
33+
ex::sender auto begin = ex::schedule(sch);
34+
{
35+
ex::sender auto ftr = scope.spawn_future(begin | stdexec::then([&]() { produced.store(true); }));
36+
(void) ftr;
37+
}
38+
stdexec::sync_wait(scope.on_empty() | stdexec::then([&]() {
39+
RL_ASSERT(produced.load());
40+
}));
41+
}
42+
};
43+
44+
int main()
45+
{
46+
rl::test_params p;
47+
p.iteration_count = 50000;
48+
p.execution_depth_limit = 10000;
49+
rl::simulate<async_scope_bug>(p);
50+
return 0;
51+
}

test/rrd/split.cpp

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include "../../relacy/relacy_std.hpp"
2+
#include "../../relacy/relacy_cli.hpp"
3+
4+
#include <stdexec/execution.hpp>
5+
#include <exec/async_scope.hpp>
6+
#include <exec/static_thread_pool.hpp>
7+
#include <test_common/schedulers.hpp>
8+
9+
#include <chrono>
10+
#include <random>
11+
#include <iostream>
12+
13+
using rl::nvar;
14+
using rl::nvolatile;
15+
using rl::mutex;
16+
17+
namespace ex = stdexec;
18+
using exec::async_scope;
19+
20+
struct split_bug : rl::test_suite<split_bug, 1>
21+
{
22+
static size_t const dynamic_thread_count = 2;
23+
24+
void thread(unsigned)
25+
{
26+
exec::static_thread_pool pool{1};
27+
auto split = ex::schedule(pool.get_scheduler()) //
28+
| ex::then([] {
29+
return 42;
30+
})
31+
| ex::split();
32+
33+
auto [val] = ex::sync_wait(split).value();
34+
RL_ASSERT(val == 42);
35+
}
36+
};
37+
38+
int main()
39+
{
40+
rl::test_params p;
41+
p.iteration_count = 50000;
42+
p.execution_depth_limit = 10000;
43+
rl::simulate<split_bug>(p);
44+
return 0;
45+
}

0 commit comments

Comments
 (0)