brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.3 KiB · ee7c172 Raw
190 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_any;14 15// template <class Lock, class Duration, class Predicate>16//     bool17//     wait_until(Lock& 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 Mutex>45struct MyLock : std::unique_lock<Mutex> {46  using std::unique_lock<Mutex>::unique_lock;47};48 49template <class Lock, class Clock>50void test() {51  using Mutex = typename Lock::mutex_type;52  // Test unblocking via a call to notify_one() in another thread.53  //54  // To test this, we set a very long timeout in wait_until() and we try to minimize55  // the likelihood that we got awoken by a spurious wakeup by updating the56  // likely_spurious flag only immediately before we perform the notification.57  {58    std::atomic<bool> ready(false);59    std::atomic<bool> likely_spurious(true);60    auto timeout = Clock::now() + std::chrono::seconds(3600);61    std::condition_variable_any cv;62    Mutex mutex;63 64    std::thread t1 = support::make_test_thread([&] {65      Lock lock(mutex);66      ready       = true;67      bool result = cv.wait_until(lock, timeout, [&] { return !likely_spurious; });68      assert(result); // return value should be true since we didn't time out69      assert(Clock::now() < timeout);70    });71 72    std::thread t2 = support::make_test_thread([&] {73      while (!ready) {74        // spin75      }76 77      // Acquire the same mutex as t1. This ensures that the condition variable has started78      // waiting (and hence released that mutex).79      Lock lock(mutex);80 81      likely_spurious = false;82      lock.unlock();83      cv.notify_one();84    });85 86    t2.join();87    t1.join();88  }89 90  // Test unblocking via a timeout.91  //92  // To test this, we create a thread that waits on a condition variable with a certain93  // timeout, and we never awaken it. The "stop waiting" predicate always returns false,94  // which means that we can't get out of the wait via a spurious wakeup.95  {96    auto timeout = Clock::now() + std::chrono::milliseconds(250);97    std::condition_variable_any cv;98    Mutex mutex;99 100    std::thread t1 = support::make_test_thread([&] {101      Lock lock(mutex);102      bool result = cv.wait_until(lock, timeout, [] { return false; }); // never stop waiting (until timeout)103      assert(!result); // return value should be false since the predicate returns false after the timeout104      assert(Clock::now() >= timeout);105    });106 107    t1.join();108  }109 110  // Test unblocking via a spurious wakeup.111  //112  // To test this, we set a fairly long timeout in wait_until() and we basically never113  // wake up the condition variable. This way, we are hoping to get out of the wait114  // via a spurious wakeup.115  //116  // However, since spurious wakeups are not required to even happen, this test is117  // only trying to trigger that code path, but not actually asserting that it is118  // taken. In particular, we do need to eventually ensure we get out of the wait119  // by standard means, so we actually wake up the thread at the end.120  {121    std::atomic<bool> ready(false);122    std::atomic<bool> awoken(false);123    auto timeout = Clock::now() + std::chrono::seconds(3600);124    std::condition_variable_any cv;125    Mutex mutex;126 127    std::thread t1 = support::make_test_thread([&] {128      Lock lock(mutex);129      ready       = true;130      bool result = cv.wait_until(lock, timeout, [&] { return true; });131      awoken      = true;132      assert(result);                 // return value should be true since we didn't time out133      assert(Clock::now() < timeout); // can technically fail if t2 never executes and we timeout, but very unlikely134    });135 136    std::thread t2 = support::make_test_thread([&] {137      while (!ready) {138        // spin139      }140 141      // Acquire the same mutex as t1. This ensures that the condition variable has started142      // waiting (and hence released that mutex).143      Lock lock(mutex);144      lock.unlock();145 146      // Give some time for t1 to be awoken spuriously so that code path is used.147      std::this_thread::sleep_for(std::chrono::seconds(1));148 149      // We would want to assert that the thread has been awoken after this time,150      // however nothing guarantees us that it ever gets spuriously awoken, so151      // we can't really check anything. This is still left here as documentation.152      bool woke = awoken.load();153      assert(woke || !woke);154 155      // Whatever happened, actually awaken the condition variable to ensure the test156      // doesn't keep running until the timeout.157      cv.notify_one();158    });159 160    t2.join();161    t1.join();162  }163}164 165int main(int, char**) {166  // Run on multiple threads to speed up the test, and because it ought to work anyways.167  std::thread tests[] = {168      support::make_test_thread([] {169        test<std::unique_lock<std::mutex>, TestClock>();170        test<std::unique_lock<std::mutex>, std::chrono::steady_clock>();171      }),172      support::make_test_thread([] {173        test<std::unique_lock<std::timed_mutex>, TestClock>();174        test<std::unique_lock<std::timed_mutex>, std::chrono::steady_clock>();175      }),176      support::make_test_thread([] {177        test<MyLock<std::mutex>, TestClock>();178        test<MyLock<std::mutex>, std::chrono::steady_clock>();179      }),180      support::make_test_thread([] {181        test<MyLock<std::timed_mutex>, TestClock>();182        test<MyLock<std::timed_mutex>, std::chrono::steady_clock>();183      })};184 185  for (std::thread& t : tests)186    t.join();187 188  return 0;189}190