File: wait_until_pred.pass.cpp

package info (click to toggle)
llvm-toolchain-19 1%3A19.1.7-3~deb12u1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-proposed-updates
  • size: 1,998,492 kB
  • sloc: cpp: 6,951,680; ansic: 1,486,157; asm: 913,598; python: 232,024; f90: 80,126; objc: 75,281; lisp: 37,276; pascal: 16,990; sh: 10,009; ml: 5,058; perl: 4,724; awk: 3,523; makefile: 3,167; javascript: 2,504; xml: 892; fortran: 664; cs: 573
file content (163 lines) | stat: -rw-r--r-- 5,616 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// UNSUPPORTED: no-threads, c++03

// <condition_variable>

// class condition_variable;

// template <class Clock, class Duration, class Predicate>
//     bool
//     wait_until(unique_lock<mutex>& lock,
//                const chrono::time_point<Clock, Duration>& abs_time,
//                Predicate pred);

#include <condition_variable>
#include <atomic>
#include <cassert>
#include <chrono>
#include <mutex>
#include <thread>

#include "make_test_thread.h"
#include "test_macros.h"

struct TestClock {
  typedef std::chrono::milliseconds duration;
  typedef duration::rep rep;
  typedef duration::period period;
  typedef std::chrono::time_point<TestClock> time_point;
  static const bool is_steady = true;

  static time_point now() {
    using namespace std::chrono;
    return time_point(duration_cast<duration>(steady_clock::now().time_since_epoch()));
  }
};

template <class Clock>
void test() {
  // Test unblocking via a call to notify_one() in another thread.
  //
  // To test this, we set a very long timeout in wait_until() and we try to minimize
  // the likelihood that we got awoken by a spurious wakeup by updating the
  // likely_spurious flag only immediately before we perform the notification.
  {
    std::atomic<bool> ready(false);
    std::atomic<bool> likely_spurious(true);
    auto timeout = Clock::now() + std::chrono::seconds(3600);
    std::condition_variable cv;
    std::mutex mutex;

    std::thread t1 = support::make_test_thread([&] {
      std::unique_lock<std::mutex> lock(mutex);
      ready       = true;
      bool result = cv.wait_until(lock, timeout, [&] { return !likely_spurious; });
      assert(result); // return value should be true since we didn't time out
      assert(Clock::now() < timeout);
    });

    std::thread t2 = support::make_test_thread([&] {
      while (!ready) {
        // spin
      }

      // Acquire the same mutex as t1. This ensures that the condition variable has started
      // waiting (and hence released that mutex).
      std::unique_lock<std::mutex> lock(mutex);

      likely_spurious = false;
      lock.unlock();
      cv.notify_one();
    });

    t2.join();
    t1.join();
  }

  // Test unblocking via a timeout.
  //
  // To test this, we create a thread that waits on a condition variable with a certain
  // timeout, and we never awaken it. The "stop waiting" predicate always returns false,
  // which means that we can't get out of the wait via a spurious wakeup.
  {
    auto timeout = Clock::now() + std::chrono::milliseconds(250);
    std::condition_variable cv;
    std::mutex mutex;

    std::thread t1 = support::make_test_thread([&] {
      std::unique_lock<std::mutex> lock(mutex);
      bool result = cv.wait_until(lock, timeout, [] { return false; }); // never stop waiting (until timeout)
      assert(!result); // return value should be false since the predicate returns false after the timeout
      assert(Clock::now() >= timeout);
    });

    t1.join();
  }

  // Test unblocking via a spurious wakeup.
  //
  // To test this, we set a fairly long timeout in wait_until() and we basically never
  // wake up the condition variable. This way, we are hoping to get out of the wait
  // via a spurious wakeup.
  //
  // However, since spurious wakeups are not required to even happen, this test is
  // only trying to trigger that code path, but not actually asserting that it is
  // taken. In particular, we do need to eventually ensure we get out of the wait
  // by standard means, so we actually wake up the thread at the end.
  {
    std::atomic<bool> ready(false);
    std::atomic<bool> awoken(false);
    auto timeout = Clock::now() + std::chrono::seconds(3600);
    std::condition_variable cv;
    std::mutex mutex;

    std::thread t1 = support::make_test_thread([&] {
      std::unique_lock<std::mutex> lock(mutex);
      ready       = true;
      bool result = cv.wait_until(lock, timeout, [&] { return true; });
      awoken      = true;
      assert(result);                 // return value should be true since we didn't time out
      assert(Clock::now() < timeout); // can technically fail if t2 never executes and we timeout, but very unlikely
    });

    std::thread t2 = support::make_test_thread([&] {
      while (!ready) {
        // spin
      }

      // Acquire the same mutex as t1. This ensures that the condition variable has started
      // waiting (and hence released that mutex).
      std::unique_lock<std::mutex> lock(mutex);
      lock.unlock();

      // Give some time for t1 to be awoken spuriously so that code path is used.
      std::this_thread::sleep_for(std::chrono::seconds(1));

      // We would want to assert that the thread has been awoken after this time,
      // however nothing guarantees us that it ever gets spuriously awoken, so
      // we can't really check anything. This is still left here as documentation.
      bool woke = awoken.load();
      assert(woke || !woke);

      // Whatever happened, actually awaken the condition variable to ensure the test
      // doesn't keep running until the timeout.
      cv.notify_one();
    });

    t2.join();
    t1.join();
  }
}

int main(int, char**) {
  test<TestClock>();
  test<std::chrono::steady_clock>();
  return 0;
}