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::__anonb5133f370111::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