129 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 Predicate>16// void wait(Lock& lock, Predicate pred);17 18#include <condition_variable>19#include <atomic>20#include <cassert>21#include <mutex>22#include <thread>23 24#include "make_test_thread.h"25#include "test_macros.h"26 27template <class Mutex>28struct MyLock : std::unique_lock<Mutex> {29 using std::unique_lock<Mutex>::unique_lock;30};31 32template <class Lock>33void test() {34 using Mutex = typename Lock::mutex_type;35 36 // Test unblocking via a call to notify_one() in another thread.37 //38 // To test this, we try to minimize the likelihood that we got awoken by a39 // spurious wakeup by updating the likely_spurious flag only immediately40 // before we perform the notification.41 {42 std::atomic<bool> ready(false);43 std::atomic<bool> likely_spurious(true);44 std::condition_variable_any cv;45 Mutex mutex;46 47 std::thread t1 = support::make_test_thread([&] {48 Lock lock(mutex);49 ready = true;50 cv.wait(lock, [&] { return !likely_spurious; });51 });52 53 std::thread t2 = support::make_test_thread([&] {54 while (!ready) {55 // spin56 }57 58 // Acquire the same mutex as t1. This ensures that the condition variable has started59 // waiting (and hence released that mutex).60 Lock lock(mutex);61 62 likely_spurious = false;63 lock.unlock();64 cv.notify_one();65 });66 67 t2.join();68 t1.join();69 }70 71 // Test unblocking via a spurious wakeup.72 //73 // To test this, we basically never wake up the condition variable. This way, we74 // are hoping to get out of the wait via a spurious wakeup.75 //76 // However, since spurious wakeups are not required to even happen, this test is77 // only trying to trigger that code path, but not actually asserting that it is78 // taken. In particular, we do need to eventually ensure we get out of the wait79 // by standard means, so we actually wake up the thread at the end.80 {81 std::atomic<bool> ready(false);82 std::atomic<bool> awoken(false);83 std::condition_variable_any cv;84 Mutex mutex;85 86 std::thread t1 = support::make_test_thread([&] {87 Lock lock(mutex);88 ready = true;89 cv.wait(lock, [&] { return true; });90 awoken = true;91 });92 93 std::thread t2 = support::make_test_thread([&] {94 while (!ready) {95 // spin96 }97 98 // Acquire the same mutex as t1. This ensures that the condition variable has started99 // waiting (and hence released that mutex).100 Lock lock(mutex);101 lock.unlock();102 103 // Give some time for t1 to be awoken spuriously so that code path is used.104 std::this_thread::sleep_for(std::chrono::seconds(1));105 106 // We would want to assert that the thread has been awoken after this time,107 // however nothing guarantees us that it ever gets spuriously awoken, so108 // we can't really check anything. This is still left here as documentation.109 bool woke = awoken.load();110 assert(woke || !woke);111 112 // Whatever happened, actually awaken the condition variable to ensure the test finishes.113 cv.notify_one();114 });115 116 t2.join();117 t1.join();118 }119}120 121int main(int, char**) {122 test<std::unique_lock<std::mutex>>();123 test<std::unique_lock<std::timed_mutex>>();124 test<MyLock<std::mutex>>();125 test<MyLock<std::timed_mutex>>();126 127 return 0;128}129