Skip to content

Commit 9f72bc5

Browse files
committed
add conditional variables testing
1 parent c41e26d commit 9f72bc5

7 files changed

Lines changed: 184 additions & 8 deletions

File tree

.github/workflows/run-tests.yaml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ jobs:
4545
run: |
4646
cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Debug
4747
cmake --build build --target verifying/targets/nonlinear_queue verifying/targets/nonlinear_ms_queue verifying/targets/nonlinear_treiber_stack verifying/targets/nonlinear_set verifying/blocking/nonlinear_mutex \
48-
verifying/blocking/simple_mutex verifying/blocking/bank verifying/blocking/mutexed_register verifying/blocking/shared_mutexed_register
48+
verifying/blocking/simple_mutex verifying/blocking/bank verifying/blocking/mutexed_register verifying/blocking/shared_mutexed_register verifying/blocking/buffered_channel
4949
- name: Run nonlinear_queue with random strategy
5050
run: |
5151
./scripts/check.sh 3 ./build/verifying/targets/nonlinear_queue --rounds 10000 --strategy random
@@ -88,6 +88,9 @@ jobs:
8888
- name: Run mutexed_register with random strategy
8989
run: |
9090
./scripts/check.sh 0 ./build/verifying/blocking/mutexed_register --strategy random --rounds 100
91+
- name: Run buffered_channel with pct strategy
92+
run: |
93+
./scripts/check.sh 0 ./build/verifying/blocking/buffered_channel --strategy pct --round 100
9194
verifying-test-release:
9295
runs-on: ubuntu-latest
9396
defaults:
@@ -109,7 +112,7 @@ jobs:
109112
run: |
110113
cmake -G Ninja -B build -DCMAKE_BUILD_TYPE=Release
111114
cmake --build build --target verifying/targets/nonlinear_queue verifying/targets/nonlinear_ms_queue verifying/targets/nonlinear_treiber_stack verifying/targets/nonlinear_set verifying/blocking/nonlinear_mutex \
112-
verifying/blocking/simple_mutex verifying/blocking/bank verifying/blocking/mutexed_register verifying/blocking/shared_mutexed_register
115+
verifying/blocking/simple_mutex verifying/blocking/bank verifying/blocking/mutexed_register verifying/blocking/shared_mutexed_register verifying/blocking/buffered_channel
113116
- name: Run nonlinear_queue with random strategy (minimization)
114117
run: |
115118
./scripts/check.sh 3 ./build/verifying/targets/nonlinear_queue --tasks 40 --rounds 100000 --strategy random --minimize
@@ -164,6 +167,9 @@ jobs:
164167
- name: Run mutexed_register with random strategy
165168
run: |
166169
./scripts/check.sh 0 ./build/verifying/blocking/mutexed_register --strategy random --rounds 10000
170+
- name: Run buffered_channel with pct strategy
171+
run: |
172+
./scripts/check.sh 0 ./build/verifying/blocking/buffered_channel --strategy pct --round 10000
167173
verifying-folly-release:
168174
runs-on: ubuntu-latest
169175
env:

syscall_intercept/hook.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ static int hook(long syscall_number, long arg0, long arg1, long arg2, long arg3,
2323
} else if (syscall_number == SYS_futex) {
2424
debug(stderr, "caught futex(0x%lx, %ld), exp: %ld, cur: %d\n",
2525
(unsigned long)arg0, arg1, arg2, *((int *)arg0));
26-
if (arg1 == FUTEX_WAIT_PRIVATE || arg1 == FUTEX_WAIT_BITSET_PRIVATE) {
26+
arg1 = arg1 & FUTEX_CMD_MASK;
27+
if (arg1 == FUTEX_WAIT || arg1 == FUTEX_WAIT_BITSET) {
2728
auto fstate = FutexState{arg0, arg2};
2829
if (fstate.CanBeBlocked()) {
2930
this_coro->SetBlocked(fstate);
@@ -34,8 +35,7 @@ static int hook(long syscall_number, long arg0, long arg1, long arg2, long arg3,
3435
errno = EAGAIN;
3536
*result = 1;
3637
}
37-
} else if (arg1 == FUTEX_WAKE_PRIVATE ||
38-
arg1 == FUTEX_WAKE_BITSET_PRIVATE) {
38+
} else if (arg1 == FUTEX_WAKE || arg1 == FUTEX_WAKE_BITSET) {
3939
debug(stderr, "caught wake\n");
4040
*result = futex_queues.Pop(arg0, arg2);
4141
} else {

third_party/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ include(FetchContent)
33
FetchContent_Declare(
44
fuzztest
55
GIT_REPOSITORY https://github.com/google/fuzztest.git
6-
GIT_TAG main
6+
GIT_TAG 2025-02-14
77
GIT_SHALLOW TRUE
88
SYSTEM
99
)

verifying/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ function(verify_target target)
2222
COMMAND ${CLANG_TOOL_EXECUTABLE}
2323
-p=${CMAKE_BINARY_DIR}/compile_commands.json # passing compilation database, make sure CMAKE_EXPORT_COMPILE_COMMANDS flag is set
2424
--temp-prefix ${CLANG_TOOL_TMP_PREFIX}
25-
--replace-names ::std::mutex,::std::shared_mutex
26-
--insert-names ltest::mutex,ltest::shared_mutex
25+
--replace-names ::std::shared_mutex
26+
--insert-names ltest::shared_mutex
2727
${CMAKE_CURRENT_SOURCE_DIR}/${source_name}
2828
DEPENDS ${CLANG_TOOL}
2929
COMMENT "Running Clang Pass Tool on ${source_name}"

verifying/blocking/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set (SOURCE_TARGET_LIST
44
nonlinear_mutex.cpp
55
shared_mutexed_register.cpp
66
bank.cpp
7+
buffered_channel.cpp
78
)
89

910
set (FOLLY_SOURCE_TARGET_LIST
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
#include <atomic>
2+
#include <condition_variable>
3+
#include <cstring>
4+
#include <mutex>
5+
6+
#include "../specs/queue.h"
7+
#include "runtime/include/verifying.h"
8+
9+
constexpr int THREAD_COUNT = 2;
10+
constexpr int N = 5;
11+
12+
namespace spec {
13+
struct BufferedChannel {
14+
int Send(int v) {
15+
deq.push_back(v);
16+
return 0;
17+
}
18+
19+
int TryRecv() {
20+
if (deq.empty()) {
21+
return -1;
22+
}
23+
auto value = deq.front();
24+
deq.pop_front();
25+
return value;
26+
}
27+
28+
using method_t = std::function<int(BufferedChannel *l, void *args)>;
29+
static auto GetMethods() {
30+
method_t send_func = [](BufferedChannel *l, void *args) -> int {
31+
auto real_args = reinterpret_cast<std::tuple<int> *>(args);
32+
return l->Send(std::get<0>(*real_args));
33+
};
34+
35+
method_t try_recv_func = [](BufferedChannel *l, void *args) -> int {
36+
return l->TryRecv();
37+
};
38+
39+
return std::map<std::string, method_t>{
40+
{"Send", send_func},
41+
{"TryRecv", try_recv_func},
42+
};
43+
}
44+
45+
std::deque<int> deq;
46+
};
47+
48+
struct BufferedChannelHash {
49+
size_t operator()(const BufferedChannel &r) const {
50+
int res = 0;
51+
for (int elem : r.deq) {
52+
res += elem;
53+
}
54+
return res;
55+
}
56+
};
57+
58+
struct BufferedChannelEquals {
59+
bool operator()(const BufferedChannel &lhs,
60+
const BufferedChannel &rhs) const {
61+
return lhs.deq == rhs.deq;
62+
}
63+
};
64+
}; // namespace spec
65+
66+
struct BufferedChannelVerifier {
67+
bool Verify(CreatedTaskMetaData ctask) {
68+
auto [taskName, is_new, thread_id] = ctask;
69+
debug(stderr, "validating method %s, thread_id: %zu\n", taskName.data(),
70+
thread_id);
71+
if (!is_new) {
72+
return true;
73+
}
74+
if (taskName == "Send") {
75+
return size_ + THREAD_COUNT < N;
76+
} else if (taskName == "TryRecv") {
77+
return true;
78+
} else {
79+
assert(false);
80+
}
81+
}
82+
83+
void OnFinished(TaskWithMetaData ctask) {
84+
auto [task, is_new, thread_id] = ctask;
85+
auto taskName = task->GetName();
86+
if (taskName == "Send") {
87+
++size_;
88+
} else if (taskName == "TryRecv") {
89+
if (size_ > 0) {
90+
--size_;
91+
}
92+
} else {
93+
assert(false);
94+
}
95+
debug(stderr, "On finished method %s, thread_id: %zu, size: %zu\n",
96+
taskName.data(), thread_id, size_);
97+
}
98+
99+
std::optional<std::string> ReleaseTask(size_t thread_id) {
100+
if (size_ > 0) {
101+
return {"TryRecv"};
102+
}
103+
return std::nullopt;
104+
}
105+
106+
size_t size_;
107+
};
108+
109+
struct BufferedChannel {
110+
non_atomic int Send(int v) {
111+
std::unique_lock lock{mutex_};
112+
while (!closed_ && full_) {
113+
send_side_cv_.wait(lock);
114+
}
115+
116+
queue_[sidx_] = v;
117+
sidx_ = (sidx_ + 1) % N;
118+
full_ = (sidx_ == ridx_);
119+
empty_ = false;
120+
return 0;
121+
}
122+
123+
// TryRecv is not blocking otherwise it is dual structure
124+
non_atomic int TryRecv() {
125+
std::unique_lock lock{mutex_};
126+
if (closed_ || empty_) {
127+
return -1;
128+
}
129+
auto val = queue_[ridx_];
130+
ridx_ = (ridx_ + 1) % 5;
131+
empty_ = (sidx_ == ridx_);
132+
full_ = false;
133+
send_side_cv_.notify_one();
134+
return val;
135+
}
136+
137+
non_atomic int Close() {
138+
closed_.store(true);
139+
send_side_cv_.notify_all();
140+
return 0;
141+
}
142+
143+
std::mutex mutex_;
144+
std::condition_variable send_side_cv_;
145+
std::atomic_bool closed_{false};
146+
147+
bool full_{false};
148+
bool empty_{true};
149+
150+
uint32_t sidx_{0}, ridx_{0};
151+
152+
std::array<int, N> queue_{};
153+
};
154+
155+
auto generateInt(size_t) {
156+
return ltest::generators::makeSingleArg(rand() % 10 + 1);
157+
}
158+
159+
using spec_t =
160+
ltest::Spec<BufferedChannel, spec::BufferedChannel,
161+
spec::BufferedChannelHash, spec::BufferedChannelEquals>;
162+
163+
LTEST_ENTRYPOINT_CONSTRAINT(spec_t, BufferedChannelVerifier);
164+
165+
target_method(generateInt, void, BufferedChannel, Send, int);
166+
target_method(ltest::generators::genEmpty, int, BufferedChannel, TryRecv);
167+
// target_method(ltest::generators::genEmpty, int, BufferedChannel, Close);

verifying/blocking/mutexed_register.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#include <mutex>
22

3+
#include "runtime/include/lib.h"
34
#include "runtime/include/verifying.h"
45
#include "verifying/specs/register.h"
56

67
struct Register {
78
non_atomic void add() {
89
while (!m_.try_lock()) {
10+
CoroYield();
911
}
1012
++x_;
1113
m_.unlock();

0 commit comments

Comments
 (0)