brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · eab3808 Raw
122 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>16//   cv_status17//   wait_for(Lock& lock, const chrono::duration<Rep, Period>& rel_time);18 19#include <condition_variable>20#include <atomic>21#include <cassert>22#include <chrono>23#include <mutex>24#include <thread>25 26#include "make_test_thread.h"27#include "test_macros.h"28 29template <class Mutex>30struct MyLock : std::unique_lock<Mutex> {31  using std::unique_lock<Mutex>::unique_lock;32};33 34template <class Function>35std::chrono::microseconds measure(Function f) {36  std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();37  f();38  std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();39  return std::chrono::duration_cast<std::chrono::microseconds>(end - start);40}41 42template <class Lock>43void test() {44  using Mutex = typename Lock::mutex_type;45  // Test unblocking via a call to notify_one() in another thread.46  //47  // To test this, we set a very long timeout in wait_for() and we wait48  // again in case we get awoken spuriously. Note that it can actually49  // happen that we get awoken spuriously and fail to recognize it50  // (making this test useless), but the likelihood should be small.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        do {63          std::cv_status result = cv.wait_for(lock, timeout);64          assert(result == std::cv_status::no_timeout);65        } while (likely_spurious);66      });67 68      // This can technically fail if we have many spurious awakenings, but in practice the69      // tolerance is so high that it shouldn't be a problem.70      assert(elapsed < timeout);71    });72 73    std::thread t2 = support::make_test_thread([&] {74      while (!ready) {75        // spin76      }77 78      // Acquire the same mutex as t1. This blocks the condition variable inside its wait call79      // so we can notify it while it is waiting.80      Lock lock(mutex);81      cv.notify_one();82      likely_spurious = false;83      lock.unlock();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 variable93  // with a certain timeout, and we never awaken it. To guard against94  // spurious wakeups, we wait again whenever we are awoken for a reason95  // other than a timeout.96  {97    auto timeout = std::chrono::milliseconds(250);98    std::condition_variable_any cv;99    Mutex mutex;100 101    std::thread t1 = support::make_test_thread([&] {102      Lock lock(mutex);103      std::cv_status result;104      do {105        auto elapsed = measure([&] { result = cv.wait_for(lock, timeout); });106        if (result == std::cv_status::timeout)107          assert(elapsed >= timeout);108      } while (result != std::cv_status::timeout);109    });110 111    t1.join();112  }113}114 115int main(int, char**) {116  test<std::unique_lock<std::mutex>>();117  test<std::unique_lock<std::timed_mutex>>();118  test<MyLock<std::mutex>>();119  test<MyLock<std::timed_mutex>>();120  return 0;121}122