110 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>16// cv_status17// wait_for(unique_lock<mutex>& lock,18// const chrono::duration<Rep, Period>& rel_time);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 Function>31std::chrono::microseconds measure(Function f) {32 std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();33 f();34 std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();35 return std::chrono::duration_cast<std::chrono::microseconds>(end - start);36}37 38int main(int, char**) {39 // Test unblocking via a call to notify_one() in another thread.40 //41 // To test this, we set a very long timeout in wait_for() and we wait42 // again in case we get awoken spuriously. Note that it can actually43 // happen that we get awoken spuriously and fail to recognize it44 // (making this test useless), but the likelihood should be small.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 do {57 std::cv_status result = cv.wait_for(lock, timeout);58 assert(result == std::cv_status::no_timeout);59 } while (likely_spurious);60 });61 62 // This can technically fail if we have many spurious awakenings, but in practice the63 // tolerance is so high that it shouldn't be a problem.64 assert(elapsed < timeout);65 });66 67 std::thread t2 = support::make_test_thread([&] {68 while (!ready) {69 // spin70 }71 72 // Acquire the same mutex as t1. This blocks the condition variable inside its wait call73 // so we can notify it while it is waiting.74 std::unique_lock<std::mutex> lock(mutex);75 cv.notify_one();76 likely_spurious = false;77 lock.unlock();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 variable87 // with a certain timeout, and we never awaken it. To guard against88 // spurious wakeups, we wait again whenever we are awoken for a reason89 // other than a timeout.90 {91 auto timeout = std::chrono::milliseconds(250);92 std::condition_variable cv;93 std::mutex mutex;94 95 std::thread t1 = support::make_test_thread([&] {96 std::unique_lock<std::mutex> lock(mutex);97 std::cv_status result;98 do {99 auto elapsed = measure([&] { result = cv.wait_for(lock, timeout); });100 if (result == std::cv_status::timeout)101 assert(elapsed >= timeout);102 } while (result != std::cv_status::timeout);103 });104 105 t1.join();106 }107 108 return 0;109}110