1#include "support/Cancellation.h"
2#include "support/Context.h"
3#include "support/Threading.h"
4#include "gmock/gmock.h"
5#include "gtest/gtest.h"
6#include <atomic>
7#include <memory>
8#include <optional>
9#include <thread>
10
11namespace clang {
12namespace clangd {
13namespace {
14
15TEST(CancellationTest, CancellationTest) {
16 auto Task = cancelableTask();
17 WithContext ContextWithCancellation(std::move(Task.first));
18 EXPECT_FALSE(isCancelled());
19 Task.second();
20 EXPECT_TRUE(isCancelled());
21}
22
23TEST(CancellationTest, CancelerDiesContextLives) {
24 std::optional<WithContext> ContextWithCancellation;
25 {
26 auto Task = cancelableTask();
27 ContextWithCancellation.emplace(args: std::move(Task.first));
28 EXPECT_FALSE(isCancelled());
29 Task.second();
30 EXPECT_TRUE(isCancelled());
31 }
32 EXPECT_TRUE(isCancelled());
33}
34
35TEST(CancellationTest, TaskContextDiesHandleLives) {
36 auto Task = cancelableTask();
37 {
38 WithContext ContextWithCancellation(std::move(Task.first));
39 EXPECT_FALSE(isCancelled());
40 Task.second();
41 EXPECT_TRUE(isCancelled());
42 }
43 // Still should be able to cancel without any problems.
44 Task.second();
45}
46
47struct NestedTasks {
48 enum { OuterReason = 1, InnerReason = 2 };
49 std::pair<Context, Canceler> Outer, Inner;
50 NestedTasks() {
51 Outer = cancelableTask(Reason: OuterReason);
52 {
53 WithContext WithOuter(Outer.first.clone());
54 Inner = cancelableTask(Reason: InnerReason);
55 }
56 }
57};
58
59TEST(CancellationTest, Nested) {
60 // Cancelling inner task works but leaves outer task unaffected.
61 NestedTasks CancelInner;
62 CancelInner.Inner.second();
63 EXPECT_EQ(NestedTasks::InnerReason, isCancelled(CancelInner.Inner.first));
64 EXPECT_FALSE(isCancelled(CancelInner.Outer.first));
65 // Cancellation of outer task is inherited by inner task.
66 NestedTasks CancelOuter;
67 CancelOuter.Outer.second();
68 EXPECT_EQ(NestedTasks::OuterReason, isCancelled(CancelOuter.Inner.first));
69 EXPECT_EQ(NestedTasks::OuterReason, isCancelled(CancelOuter.Outer.first));
70}
71
72TEST(CancellationTest, AsynCancellationTest) {
73 std::atomic<bool> HasCancelled(false);
74 Notification Cancelled;
75 auto TaskToBeCancelled = [&](Context Ctx) {
76 WithContext ContextGuard(std::move(Ctx));
77 Cancelled.wait();
78 HasCancelled = isCancelled();
79 };
80 auto Task = cancelableTask();
81 std::thread AsyncTask(TaskToBeCancelled, std::move(Task.first));
82 Task.second();
83 Cancelled.notify();
84 AsyncTask.join();
85
86 EXPECT_TRUE(HasCancelled);
87}
88} // namespace
89} // namespace clangd
90} // namespace clang
91

source code of clang-tools-extra/clangd/unittests/support/CancellationTests.cpp