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 | |
11 | namespace clang { |
12 | namespace clangd { |
13 | namespace { |
14 | |
15 | TEST(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 | |
23 | TEST(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 | |
35 | TEST(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 | |
47 | struct 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 | |
59 | TEST(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 | |
72 | TEST(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 | |