brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.5 KiB · 847d0c1 Raw
164 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 Clock, class Duration, class Predicate>16//     bool17//     wait_until(unique_lock<mutex>& lock,18//                const chrono::time_point<Clock, Duration>& abs_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 31struct TestClock {32  typedef std::chrono::milliseconds duration;33  typedef duration::rep rep;34  typedef duration::period period;35  typedef std::chrono::time_point<TestClock> time_point;36  static const bool is_steady = true;37 38  static time_point now() {39    using namespace std::chrono;40    return time_point(duration_cast<duration>(steady_clock::now().time_since_epoch()));41  }42};43 44template <class Clock>45void test() {46  // Test unblocking via a call to notify_one() in another thread.47  //48  // To test this, we set a very long timeout in wait_until() and we try to minimize49  // the likelihood that we got awoken by a spurious wakeup by updating the50  // likely_spurious flag only immediately before we perform the notification.51  {52    std::atomic<bool> ready(false);53    std::atomic<bool> likely_spurious(true);54    auto timeout = Clock::now() + std::chrono::seconds(3600);55    std::condition_variable cv;56    std::mutex mutex;57 58    std::thread t1 = support::make_test_thread([&] {59      std::unique_lock<std::mutex> lock(mutex);60      ready       = true;61      bool result = cv.wait_until(lock, timeout, [&] { return !likely_spurious; });62      assert(result); // return value should be true since we didn't time out63      assert(Clock::now() < timeout);64    });65 66    std::thread t2 = support::make_test_thread([&] {67      while (!ready) {68        // spin69      }70 71      // Acquire the same mutex as t1. This ensures that the condition variable has started72      // waiting (and hence released that mutex).73      std::unique_lock<std::mutex> lock(mutex);74 75      likely_spurious = false;76      lock.unlock();77      cv.notify_one();78    });79 80    t2.join();81    t1.join();82  }83 84  // Test unblocking via a timeout.85  //86  // To test this, we create a thread that waits on a condition variable with a certain87  // timeout, and we never awaken it. The "stop waiting" predicate always returns false,88  // which means that we can't get out of the wait via a spurious wakeup.89  {90    auto timeout = Clock::now() + std::chrono::milliseconds(250);91    std::condition_variable cv;92    std::mutex mutex;93 94    std::thread t1 = support::make_test_thread([&] {95      std::unique_lock<std::mutex> lock(mutex);96      bool result = cv.wait_until(lock, timeout, [] { return false; }); // never stop waiting (until timeout)97      assert(!result); // return value should be false since the predicate returns false after the timeout98      assert(Clock::now() >= timeout);99    });100 101    t1.join();102  }103 104  // Test unblocking via a spurious wakeup.105  //106  // To test this, we set a fairly long timeout in wait_until() and we basically never107  // wake up the condition variable. This way, we are hoping to get out of the wait108  // via a spurious wakeup.109  //110  // However, since spurious wakeups are not required to even happen, this test is111  // only trying to trigger that code path, but not actually asserting that it is112  // taken. In particular, we do need to eventually ensure we get out of the wait113  // by standard means, so we actually wake up the thread at the end.114  {115    std::atomic<bool> ready(false);116    std::atomic<bool> awoken(false);117    auto timeout = Clock::now() + std::chrono::seconds(3600);118    std::condition_variable cv;119    std::mutex mutex;120 121    std::thread t1 = support::make_test_thread([&] {122      std::unique_lock<std::mutex> lock(mutex);123      ready       = true;124      bool result = cv.wait_until(lock, timeout, [&] { return true; });125      awoken      = true;126      assert(result);                 // return value should be true since we didn't time out127      assert(Clock::now() < 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 159int main(int, char**) {160  test<TestClock>();161  test<std::chrono::steady_clock>();162  return 0;163}164