brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.4 KiB · 76fc739 Raw
160 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: no-threads, c++0310 11// <condition_variable>12 13// class condition_variable;14 15// template <class Rep, class Period, class Predicate>16//     bool17//     wait_for(unique_lock<mutex>& lock,18//              const chrono::duration<Rep, Period>& rel_time,19//              Predicate pred);20 21#include <condition_variable>22#include <atomic>23#include <cassert>24#include <chrono>25#include <mutex>26#include <thread>27 28#include "make_test_thread.h"29#include "test_macros.h"30 31template <class Function>32std::chrono::microseconds measure(Function f) {33  std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();34  f();35  std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();36  return std::chrono::duration_cast<std::chrono::microseconds>(end - start);37}38 39int main(int, char**) {40  // Test unblocking via a call to notify_one() in another thread.41  //42  // To test this, we set a very long timeout in wait_for() and we try to minimize43  // the likelihood that we got awoken by a spurious wakeup by updating the44  // likely_spurious flag only immediately before we perform the notification.45  {46    std::atomic<bool> ready(false);47    std::atomic<bool> likely_spurious(true);48    auto timeout = std::chrono::seconds(3600);49    std::condition_variable cv;50    std::mutex mutex;51 52    std::thread t1 = support::make_test_thread([&] {53      std::unique_lock<std::mutex> lock(mutex);54      auto elapsed = measure([&] {55        ready       = true;56        bool result = cv.wait_for(lock, timeout, [&] { return !likely_spurious; });57        assert(result); // return value should be true since we didn't time out58      });59      assert(elapsed < timeout);60    });61 62    std::thread t2 = support::make_test_thread([&] {63      while (!ready) {64        // spin65      }66 67      // Acquire the same mutex as t1. This ensures that the condition variable has started68      // waiting (and hence released that mutex).69      std::unique_lock<std::mutex> lock(mutex);70 71      likely_spurious = false;72      lock.unlock();73      cv.notify_one();74    });75 76    t2.join();77    t1.join();78  }79 80  // Test unblocking via a timeout.81  //82  // To test this, we create a thread that waits on a condition variable with a certain83  // timeout, and we never awaken it. The "stop waiting" predicate always returns false,84  // which means that we can't get out of the wait via a spurious wakeup.85  {86    auto timeout = std::chrono::milliseconds(250);87    std::condition_variable cv;88    std::mutex mutex;89 90    std::thread t1 = support::make_test_thread([&] {91      std::unique_lock<std::mutex> lock(mutex);92      auto elapsed = measure([&] {93        bool result = cv.wait_for(lock, timeout, [] { return false; }); // never stop waiting (until timeout)94        assert(!result); // return value should be false since the predicate returns false after the timeout95      });96      assert(elapsed >= timeout);97    });98 99    t1.join();100  }101 102  // Test unblocking via a spurious wakeup.103  //104  // To test this, we set a fairly long timeout in wait_for() and we basically never105  // wake up the condition variable. This way, we are hoping to get out of the wait106  // via a spurious wakeup.107  //108  // However, since spurious wakeups are not required to even happen, this test is109  // only trying to trigger that code path, but not actually asserting that it is110  // taken. In particular, we do need to eventually ensure we get out of the wait111  // by standard means, so we actually wake up the thread at the end.112  {113    std::atomic<bool> ready(false);114    std::atomic<bool> awoken(false);115    auto timeout = std::chrono::seconds(3600);116    std::condition_variable cv;117    std::mutex mutex;118 119    std::thread t1 = support::make_test_thread([&] {120      std::unique_lock<std::mutex> lock(mutex);121      auto elapsed = measure([&] {122        ready       = true;123        bool result = cv.wait_for(lock, timeout, [&] { return true; });124        awoken      = true;125        assert(result); // return value should be true since we didn't time out126      });127      assert(elapsed < timeout); // can technically fail if t2 never executes and we timeout, but very unlikely128    });129 130    std::thread t2 = support::make_test_thread([&] {131      while (!ready) {132        // spin133      }134 135      // Acquire the same mutex as t1. This ensures that the condition variable has started136      // waiting (and hence released that mutex).137      std::unique_lock<std::mutex> lock(mutex);138      lock.unlock();139 140      // Give some time for t1 to be awoken spuriously so that code path is used.141      std::this_thread::sleep_for(std::chrono::seconds(1));142 143      // We would want to assert that the thread has been awoken after this time,144      // however nothing guarantees us that it ever gets spuriously awoken, so145      // we can't really check anything. This is still left here as documentation.146      bool woke = awoken.load();147      assert(woke || !woke);148 149      // Whatever happened, actually awaken the condition variable to ensure the test150      // doesn't keep running until the timeout.151      cv.notify_one();152    });153 154    t2.join();155    t1.join();156  }157 158  return 0;159}160