1 #include "support/Cancellation.h"
2 #include "support/Context.h"
3 #include "support/Threading.h"
4 #include "llvm/Support/Error.h"
5 #include "gmock/gmock.h"
6 #include "gtest/gtest.h"
7 #include <atomic>
8 #include <memory>
9 #include <thread>
10
11 namespace clang {
12 namespace clangd {
13 namespace {
14
TEST(CancellationTest,CancellationTest)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
TEST(CancellationTest,CancelerDiesContextLives)23 TEST(CancellationTest, CancelerDiesContextLives) {
24 llvm::Optional<WithContext> ContextWithCancellation;
25 {
26 auto Task = cancelableTask();
27 ContextWithCancellation.emplace(std::move(Task.first));
28 EXPECT_FALSE(isCancelled());
29 Task.second();
30 EXPECT_TRUE(isCancelled());
31 }
32 EXPECT_TRUE(isCancelled());
33 }
34
TEST(CancellationTest,TaskContextDiesHandleLives)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;
NestedTasksclang::clangd::__anon964b70000111::NestedTasks50 NestedTasks() {
51 Outer = cancelableTask(OuterReason);
52 {
53 WithContext WithOuter(Outer.first.clone());
54 Inner = cancelableTask(InnerReason);
55 }
56 }
57 };
58
TEST(CancellationTest,Nested)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
TEST(CancellationTest,AsynCancellationTest)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