-
Notifications
You must be signed in to change notification settings - Fork 493
Open
Description
Summary
TSan reports a data race when using msg_ring for cross-thread communication. I'd like to know:
- Does
msg_ringguarantee happens-before between sender's submit and receiver's CQE? - If yes, could TSan annotations be added to liburing to suppress this false positive?
- If no, what is the recommended synchronization pattern?
Environment
- Kernel: 6.14.0-37-generic
- liburing: 2.11, 2.12, 2.13, master (2.14) — all exhibit the same issue
- Compiler: clang 21.1.0 with
-fsanitize=thread
Reproducer
#include <liburing.h>
#include <thread>
#include <latch>
#include <iostream>
int main() {
std::cout << "liburing version: "
<< IO_URING_VERSION_MAJOR << "."
<< IO_URING_VERSION_MINOR << std::endl;
std::latch ready(1);
int target_fd = -1;
int shared = 0;
int result = 0;
std::jthread receiver([&] {
io_uring ring{};
io_uring_queue_init(64, &ring, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER);
target_fd = ring.ring_fd;
ready.count_down();
io_uring_cqe *cqe;
io_uring_wait_cqe(&ring, &cqe);
result = shared; // TSan: READ (line 26)
io_uring_cqe_seen(&ring, cqe);
io_uring_queue_exit(&ring);
});
std::jthread sender([&] {
io_uring ring{};
io_uring_queue_init(64, &ring, IORING_SETUP_DEFER_TASKRUN | IORING_SETUP_SINGLE_ISSUER);
ready.wait();
shared = 42; // TSan: WRITE (line 38)
auto *sqe = io_uring_get_sqe(&ring);
io_uring_prep_msg_ring(sqe, target_fd, 0, 0, 0);
io_uring_submit(&ring);
io_uring_queue_exit(&ring);
});
receiver.join();
sender.join();
std::cout << "result: " << result << " (expected: 42)" << std::endl;
return 0;
}clang++ -std=c++20 -fsanitize=thread -o main main.cpp -luring -lpthread
./mainTSan Output
liburing version: 2.14
==================
WARNING: ThreadSanitizer: data race (pid=165909)
Read of size 4 at 0x7fffffffdcd8 by thread T1:
#0 main::$_0::operator()() const main.cpp:26
Previous write of size 4 at 0x7fffffffdcd8 by thread T2:
#0 main::$_1::operator()() const main.cpp:38
SUMMARY: ThreadSanitizer: data race main.cpp:26 in main::$_0::operator()() const
==================
result: 42 (expected: 42)
Expected Behavior
Sender (T2) Receiver (T1)
─────────── ─────────────
shared = 42;
│
▼
io_uring_submit(msg_ring)
│
└───── kernel ─────► io_uring_wait_cqe() returns
│
▼
result = shared; // should see 42
I expect msg_ring submit → CQE delivery to establish happens-before, making all prior writes visible to the receiver. Is this correct?
espoal
Metadata
Metadata
Assignees
Labels
No labels