Add test to verify TaskQueue memory access order.
Bug: webrtc:10138
Change-Id: I53e8a3a612ad44feced8d63a4035d79b7e0f22a9
Reviewed-on: https://webrtc-review.googlesource.com/c/120601
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Artem Titov <titovartem@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26497}
diff --git a/api/task_queue/task_queue_test.cc b/api/task_queue/task_queue_test.cc
index 103581a..ca7aee9 100644
--- a/api/task_queue/task_queue_test.cc
+++ b/api/task_queue/task_queue_test.cc
@@ -208,5 +208,37 @@
EXPECT_EQ(tasks_cleaned_up, kTaskCount);
}
+// Test posting two tasks that have shared state not protected by a
+// lock. The TaskQueue should guarantee memory read-write order and
+// FIFO task execution order, so the second task should always see the
+// changes that were made by the first task.
+//
+// If the TaskQueue doesn't properly synchronize the execution of
+// tasks, there will be a data race, which is undefined behavior. The
+// EXPECT calls may randomly catch this, but to make the most of this
+// unit test, run it under TSan or some other tool that is able to
+// directly detect data races.
+TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) {
+ struct SharedState {
+ // First task will set this value to 1 and second will assert it.
+ int state = 0;
+ } state;
+
+ auto queue = CreateTaskQueue(GetParam(), "PostTwoWithSharedUnprotectedState");
+ rtc::Event done;
+ queue->PostTask(rtc::NewClosure([&state, &queue, &done] {
+ // Post tasks from queue to guarantee, that 1st task won't be
+ // executed before the second one will be posted.
+ queue->PostTask(rtc::NewClosure([&state] { state.state = 1; }));
+ queue->PostTask(rtc::NewClosure([&state, &done] {
+ EXPECT_EQ(state.state, 1);
+ done.Set();
+ }));
+ // Check, that state changing tasks didn't start yet.
+ EXPECT_EQ(state.state, 0);
+ }));
+ EXPECT_TRUE(done.Wait(1000));
+}
+
} // namespace
} // namespace webrtc
diff --git a/rtc_base/task_queue_unittest.cc b/rtc_base/task_queue_unittest.cc
index d4ef105..9086206 100644
--- a/rtc_base/task_queue_unittest.cc
+++ b/rtc_base/task_queue_unittest.cc
@@ -367,4 +367,37 @@
EXPECT_EQ(kTaskCount, tasks_cleaned_up);
}
+// Test posting two tasks that have shared state not protected by a
+// lock. The TaskQueue should guarantee memory read-write order and
+// FIFO task execution order, so the second task should always see the
+// changes that were made by the first task.
+//
+// If the TaskQueue doesn't properly synchronize the execution of
+// tasks, there will be a data race, which is undefined behavior. The
+// EXPECT calls may randomly catch this, but to make the most of this
+// unit test, run it under TSan or some other tool that is able to
+// directly detect data races.
+TEST(TaskQueueTest, PostTwoWithSharedUnprotectedState) {
+ static const char kQueueName[] = "PostTwoWithSharedUnprotectedState";
+ struct SharedState {
+ // First task will set this value to 1 and second will assert it.
+ int state = 0;
+ } state;
+
+ TaskQueue queue(kQueueName);
+ rtc::Event done;
+ queue.PostTask([&state, &queue, &done] {
+ // Post tasks from queue to guarantee, that 1st task won't be
+ // executed before the second one will be posted.
+ queue.PostTask([&state] { state.state = 1; });
+ queue.PostTask([&state, &done] {
+ EXPECT_EQ(state.state, 1);
+ done.Set();
+ });
+ // Check, that state changing tasks didn't start yet.
+ EXPECT_EQ(state.state, 0);
+ });
+ EXPECT_TRUE(done.Wait(1000));
+}
+
} // namespace rtc