brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.6 KiB · 67ba06b Raw
172 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 Rep, class Period, class Predicate>16//   bool17//   wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time,18//            Predicate pred);19 20#include <condition_variable>21#include <atomic>22#include <cassert>23#include <chrono>24#include <mutex>25#include <thread>26 27#include "make_test_thread.h"28#include "test_macros.h"29 30template <class Mutex>31struct MyLock : std::unique_lock<Mutex> {32  using std::unique_lock<Mutex>::unique_lock;33};34 35template <class Function>36std::chrono::microseconds measure(Function f) {37  std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();38  f();39  std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();40  return std::chrono::duration_cast<std::chrono::microseconds>(end - start);41}42 43template <class Lock>44void test() {45  using Mutex = typename Lock::mutex_type;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_for() 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 = std::chrono::seconds(3600);55    std::condition_variable_any cv;56    Mutex mutex;57 58    std::thread t1 = support::make_test_thread([&] {59      Lock lock(mutex);60      auto elapsed = measure([&] {61        ready       = true;62        bool result = cv.wait_for(lock, timeout, [&] { return !likely_spurious; });63        assert(result); // return value should be true since we didn't time out64      });65      assert(elapsed < timeout);66    });67 68    std::thread t2 = support::make_test_thread([&] {69      while (!ready) {70        // spin71      }72 73      // Acquire the same mutex as t1. This ensures that the condition variable has started74      // waiting (and hence released that mutex).75      Lock lock(mutex);76 77      likely_spurious = false;78      lock.unlock();79      cv.notify_one();80    });81 82    t2.join();83    t1.join();84  }85 86  // Test unblocking via a timeout.87  //88  // To test this, we create a thread that waits on a condition variable with a certain89  // timeout, and we never awaken it. The "stop waiting" predicate always returns false,90  // which means that we can't get out of the wait via a spurious wakeup.91  {92    auto timeout = std::chrono::milliseconds(250);93    std::condition_variable_any cv;94    Mutex mutex;95 96    std::thread t1 = support::make_test_thread([&] {97      Lock lock(mutex);98      auto elapsed = measure([&] {99        bool result = cv.wait_for(lock, timeout, [] { return false; }); // never stop waiting (until timeout)100        assert(!result); // return value should be false since the predicate returns false after the timeout101      });102      assert(elapsed >= timeout);103    });104 105    t1.join();106  }107 108  // Test unblocking via a spurious wakeup.109  //110  // To test this, we set a fairly long timeout in wait_for() and we basically never111  // wake up the condition variable. This way, we are hoping to get out of the wait112  // via a spurious wakeup.113  //114  // However, since spurious wakeups are not required to even happen, this test is115  // only trying to trigger that code path, but not actually asserting that it is116  // taken. In particular, we do need to eventually ensure we get out of the wait117  // by standard means, so we actually wake up the thread at the end.118  {119    std::atomic<bool> ready(false);120    std::atomic<bool> awoken(false);121    auto timeout = std::chrono::seconds(3600);122    std::condition_variable_any cv;123    Mutex mutex;124 125    std::thread t1 = support::make_test_thread([&] {126      Lock lock(mutex);127      auto elapsed = measure([&] {128        ready       = true;129        bool result = cv.wait_for(lock, timeout, [&] { return true; });130        awoken      = true;131        assert(result); // return value should be true since we didn't time out132      });133      assert(elapsed < 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  test<std::unique_lock<std::mutex>>();167  test<std::unique_lock<std::timed_mutex>>();168  test<MyLock<std::mutex>>();169  test<MyLock<std::timed_mutex>>();170  return 0;171}172