brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.6 KiB · dff73ae Raw
255 lines · cpp
1// RUN: %check_clang_tidy -std=c++20 %s misc-coroutine-hostile-raii %t \2// RUN:   -config="{CheckOptions: {\3// RUN:             misc-coroutine-hostile-raii.RAIITypesList: 'my::Mutex; ::my::other::Mutex', \4// RUN:             misc-coroutine-hostile-raii.AllowedAwaitablesList: 'safe::awaitable; ::transformable::awaitable', \5// RUN:             misc-coroutine-hostile-raii.AllowedCallees: 'safe::AwaitFunc; ::safe::Obj::AwaitMethod; retExemptedAwaitable' \6// RUN:             }}"7 8namespace std {9 10template <typename R, typename...> struct coroutine_traits {11  using promise_type = typename R::promise_type;12};13 14template <typename Promise = void> struct coroutine_handle;15 16template <> struct coroutine_handle<void> {17  static coroutine_handle from_address(void *addr) noexcept {18    coroutine_handle me;19    me.ptr = addr;20    return me;21  }22  void operator()() { resume(); }23  void *address() const noexcept { return ptr; }24  void resume() const {  }25  void destroy() const { }26  bool done() const { return true; }27  coroutine_handle &operator=(decltype(nullptr)) {28    ptr = nullptr;29    return *this;30  }31  coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}32  coroutine_handle() : ptr(nullptr) {}33  //  void reset() { ptr = nullptr; } // add to P0057?34  explicit operator bool() const { return ptr; }35 36protected:37  void *ptr;38};39 40template <typename Promise> struct coroutine_handle : coroutine_handle<> {41  using coroutine_handle<>::operator=;42 43  static coroutine_handle from_address(void *addr) noexcept {44    coroutine_handle me;45    me.ptr = addr;46    return me;47  }48 49  Promise &promise() const {50    return *reinterpret_cast<Promise *>(51        __builtin_coro_promise(ptr, alignof(Promise), false));52  }53  static coroutine_handle from_promise(Promise &promise) {54    coroutine_handle p;55    p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);56    return p;57  }58};59 60struct suspend_always {61  bool await_ready() noexcept { return false; }62  void await_suspend(std::coroutine_handle<>) noexcept {}63  void await_resume() noexcept {}64};65} // namespace std66 67struct ReturnObject {68    struct promise_type {69        ReturnObject get_return_object() { return {}; }70        std::suspend_always initial_suspend() { return {}; }71        std::suspend_always final_suspend() noexcept { return {}; }72        void unhandled_exception() {}73        std::suspend_always yield_value(int value) { return {}; }74    };75};76 77#define SCOPED_LOCKABLE __attribute__ ((scoped_lockable))78 79namespace absl {80class SCOPED_LOCKABLE Mutex {};81using Mutex2 = Mutex;82} // namespace absl83 84ReturnObject BasicWarning() {85  absl::Mutex mtx;86  // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'mtx' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]87  int no_warning;88  {89    co_yield 1;90    // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here91  }92}93 94ReturnObject BasicNoWarning() {95  co_yield 1;96  {  absl::Mutex no_warning; }97  int no_warning;98  {99    co_yield 1;100    absl::Mutex no_warning;101  }102  co_yield 1;103}104 105ReturnObject scopedLockableTest() {106    co_yield 0;107    absl::Mutex a;108    // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]109    absl::Mutex2 b;110    // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'b' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]111    {112        absl::Mutex no_warning_1;113        { absl::Mutex no_warning_2; }114    }115 116    co_yield 1;117    // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here118    absl::Mutex c;119    // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'c' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]120    co_await std::suspend_always{};121    // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here122    for(int i=1; i<=10; ++i ) {123      absl::Mutex d;124      // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 'd' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]125      co_await std::suspend_always{};126      // CHECK-MESSAGES: :[[@LINE-1]]:7: note: suspension point is here127      co_yield 1;128      absl::Mutex no_warning_3;129    }130    if (true) {131      absl::Mutex e;132      // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 'e' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]133      co_yield 1;134      // CHECK-MESSAGES: :[[@LINE-1]]:7: note: suspension point is here135      absl::Mutex no_warning_4;136    }137    absl::Mutex no_warning_5;138}139 140// ================================================================================141// Safe awaitable142// ================================================================================143namespace safe {144  struct awaitable {145  bool await_ready() noexcept { return false; }146  void await_suspend(std::coroutine_handle<>) noexcept {}147  void await_resume() noexcept {}148};149  std::suspend_always AwaitFunc();150  struct Obj {151    std::suspend_always AwaitMethod();152  };153} // namespace safe154ReturnObject RAIISafeSuspendTest() {155  absl::Mutex a;156  co_await safe::awaitable{};157  using other = safe::awaitable;158  co_await other{};159  co_await safe::AwaitFunc();160  co_await safe::Obj().AwaitMethod();161} 162 163// ================================================================================164// Safe transformable awaitable165// ================================================================================166struct transformable {167  struct awaitable{};168  struct unsafe_awaitable{};169};170using alias_transformable_awaitable = transformable::awaitable;171struct UseTransformAwaitable {172  struct promise_type {173    UseTransformAwaitable get_return_object() { return {}; }174    std::suspend_always initial_suspend() { return {}; }175    std::suspend_always final_suspend() noexcept { return {}; }176    void unhandled_exception() {}177    std::suspend_always await_transform(transformable::awaitable) { return {}; }178    std::suspend_always await_transform(transformable::unsafe_awaitable) {179      return {};180    }181  };182};183 184auto retAwaitable() { return transformable::awaitable{}; }185auto retExemptedAwaitable() { return transformable::unsafe_awaitable{}; }186UseTransformAwaitable RAIISafeSuspendTest2() {187  absl::Mutex a;188  co_await retAwaitable();189  co_await retExemptedAwaitable();190  co_await transformable::awaitable{};191  co_await alias_transformable_awaitable{};192}193 194// ================================================================================195// Lambdas196// ================================================================================197void lambda() {198  absl::Mutex no_warning;199  auto lambda = []() -> ReturnObject {200    co_await std::suspend_always{};201    absl::Mutex a;202    // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]203    co_yield 1;204    // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here205    co_await std::suspend_always{};206    co_yield 1;207  };208  absl::Mutex no_warning_2;209}210 211// ================================================================================212// Denylisted RAII213// ================================================================================214template<class T>215ReturnObject raii_in_template(){216  T a;217  // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]218  co_yield 1;219  // CHECK-MESSAGES: :[[@LINE-1]]:3: note: suspension point is here220}221void foo_template() { raii_in_template<absl::Mutex>(); }222 223namespace my {224class Mutex{};225namespace other {226class Mutex{};227} // namespace other228 229using Mutex2 = Mutex;230} // namespace my231 232ReturnObject denyListTest() {233    my::Mutex a;234    // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'a' persists across a suspension point of coroutine [misc-coroutine-hostile-raii]235    my::other::Mutex b;236    // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 'b' persists across a suspension point of coroutine [misc-coroutine-hostile-raii]237    my::Mutex2 c;238    // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'c' persists across a suspension point of coroutine [misc-coroutine-hostile-raii]239    co_yield 1;240    // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here241}242 243ReturnObject referenceTest(my::Mutex& ref) {244    my::Mutex& a = ref;245    co_yield 1;246}247ReturnObject pointerTest(my::Mutex* ref) {248    my::Mutex* a = ref;249    co_yield 1;250}251 252ReturnObject functionArgTest(my::Mutex ref) {253    co_yield 1;254}255