summaryrefslogtreecommitdiff
path: root/libstdc++-v3/testsuite/30_threads/async/async.cc
blob: 38943ff1a9a5e02bafc095517ee731c18aaf318b (plain)
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
// { dg-do run }
// { dg-additional-options "-pthread" { target pthread } }
// { dg-require-effective-target c++11 }
// { dg-require-gthreads "" }

// Copyright (C) 2010-2022 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library.  This library is free
// software; you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3, or (at your option)
// any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3.  If not see
// <http://www.gnu.org/licenses/>.


#include <future>
#include <thread>
#include <testsuite_hooks.h>

using namespace std;

void work(mutex& m)
{
  unique_lock<mutex> l(m);
}

void test01()
{
  mutex m;
  unique_lock<mutex> l(m);
  future<void> f1 = async(launch::async, &work, ref(m));
  l.unlock();  // allow async thread to proceed
  f1.get();    // wait for it to finish
}

void test02()
{
  mutex m;
  unique_lock<mutex> l(m);
  future<void> f1 = async(launch::async, &work, ref(m));
  std::future_status status;
  status = f1.wait_for(std::chrono::milliseconds(1));
  VERIFY( status == std::future_status::timeout );
  status = f1.wait_until(std::chrono::system_clock::now());
  VERIFY( status == std::future_status::timeout );
  status = f1.wait_until(std::chrono::steady_clock::now());
  VERIFY( status == std::future_status::timeout );
  l.unlock();  // allow async thread to proceed
  f1.wait();   // wait for it to finish
  status = f1.wait_for(std::chrono::milliseconds(0));
  VERIFY( status == std::future_status::ready );
  status = f1.wait_until(std::chrono::system_clock::now());
  VERIFY( status == std::future_status::ready );
  status = f1.wait_until(std::chrono::steady_clock::now());
  VERIFY( status == std::future_status::ready );
}

// This clock behaves exactly the same as steady_clock, but it is not
// steady_clock which means that the generic clock overload of
// future::wait_until is used.
struct steady_clock_copy
{
  using rep = std::chrono::steady_clock::rep;
  using period = std::chrono::steady_clock::period;
  using duration = std::chrono::steady_clock::duration;
  using time_point = std::chrono::time_point<steady_clock_copy, duration>;
  static constexpr bool is_steady = true;

  static time_point now()
  {
    const auto steady = std::chrono::steady_clock::now();
    return time_point{steady.time_since_epoch()};
  }
};

// This test is prone to failures if run on a loaded machine where the
// kernel decides not to schedule us for several seconds. It also
// assumes that no-one will warp CLOCK whilst the test is
// running when CLOCK is std::chrono::system_clock.
template<typename CLOCK>
void test03()
{
  auto const start = CLOCK::now();
  future<void> f1 = async(launch::async, []() {
      std::this_thread::sleep_for(std::chrono::seconds(2));
    });
  std::future_status status;

  status = f1.wait_for(std::chrono::milliseconds(500));
  VERIFY( status == std::future_status::timeout );

  status = f1.wait_until(start + std::chrono::seconds(1));
  VERIFY( status == std::future_status::timeout );

  status = f1.wait_until(start + std::chrono::seconds(5));
  VERIFY( status == std::future_status::ready );

  auto const elapsed = CLOCK::now() - start;
  VERIFY( elapsed >= std::chrono::seconds(2) );
  VERIFY( elapsed < std::chrono::seconds(5) );
}

// This clock is supposed to run at a tenth of normal speed, but we
// don't have to worry about rounding errors causing us to wake up
// slightly too early below if we actually run it at an eleventh of
// normal speed. It is used to exercise the
// __atomic_futex_unsigned::_M_load_when_equal_until overload that
// takes an arbitrary clock.
struct slow_clock
{
  using rep = std::chrono::steady_clock::rep;
  using period = std::chrono::steady_clock::period;
  using duration = std::chrono::steady_clock::duration;
  using time_point = std::chrono::time_point<slow_clock, duration>;
  static constexpr bool is_steady = true;

  static time_point now()
  {
    const auto steady = std::chrono::steady_clock::now();
    return time_point{steady.time_since_epoch() / 11};
  }
};

void test04()
{
  using namespace std::chrono;

  auto const slow_start = slow_clock::now();
  future<void> f1 = async(launch::async, []() {
      std::this_thread::sleep_for(std::chrono::seconds(2));
    });

  // Wait for ~1s
  {
    auto const steady_begin = steady_clock::now();
    auto const status = f1.wait_until(slow_start + milliseconds(100));
    VERIFY(status == std::future_status::timeout);
    auto const elapsed = steady_clock::now() - steady_begin;
    VERIFY(elapsed >= seconds(1));
    VERIFY(elapsed < seconds(2));
  }

  // Wait for up to ~2s more
  {
    auto const steady_begin = steady_clock::now();
    auto const status = f1.wait_until(slow_start + milliseconds(300));
    VERIFY(status == std::future_status::ready);
    auto const elapsed = steady_clock::now() - steady_begin;
    VERIFY(elapsed < seconds(2));
  }
}

void test_pr91486_wait_for()
{
  future<void> f1 = async(launch::async, []() {
      std::this_thread::sleep_for(std::chrono::seconds(1));
    });

  std::chrono::duration<float> const wait_time = std::chrono::seconds(1);
  auto const start_steady = chrono::steady_clock::now();
  auto status = f1.wait_for(wait_time);
  auto const elapsed_steady = chrono::steady_clock::now() - start_steady;

  VERIFY( elapsed_steady >= std::chrono::seconds(1) );
}

// This is a clock with a very recent epoch which ensures that the difference
// between now() and one second in the future is representable in a float so
// that when the generic clock version of
// __atomic_futex_unsigned::_M_load_when_equal_until calculates the delta it
// gets a duration of 1.0f.  When chrono::steady_clock has moved sufficiently
// far from its epoch (about 208.5 days in my testing - about 2^54ns because
// there's a promotion to double happening too somewhere) adding 1.0f to the
// current time has no effect.  Using this clock ensures that
// __atomic_futex_unsigned::_M_load_when_equal_until is using
// chrono::__detail::ceil correctly so that the function actually sleeps rather
// than spinning.
struct float_steady_clock
{
  using duration = std::chrono::duration<float>;
  using rep = typename duration::rep;
  using period = typename duration::period;
  using time_point = std::chrono::time_point<float_steady_clock, duration>;
  static constexpr bool is_steady = true;

  static chrono::steady_clock::time_point epoch;
  static int call_count;

  static time_point now()
  {
    ++call_count;
    auto real = std::chrono::steady_clock::now();
    return time_point{real - epoch};
  }
};

chrono::steady_clock::time_point float_steady_clock::epoch = chrono::steady_clock::now();
int float_steady_clock::call_count = 0;

void test_pr91486_wait_until()
{
  future<void> f1 = async(launch::async, []() {
      std::this_thread::sleep_for(std::chrono::seconds(1));
    });

  float_steady_clock::time_point const now = float_steady_clock::now();

  std::chrono::duration<float> const wait_time = std::chrono::seconds(1);
  float_steady_clock::time_point const expire = now + wait_time;
  VERIFY( expire > now );

  auto const start_steady = chrono::steady_clock::now();
  auto status = f1.wait_until(expire);
  auto const elapsed_steady = chrono::steady_clock::now() - start_steady;

  // This checks that we didn't come back too soon
  VERIFY( elapsed_steady >= std::chrono::seconds(1) );

  // This checks that wait_until didn't busy wait checking the clock more times
  // than necessary.
  VERIFY( float_steady_clock::call_count <= 3 );
}

int main()
{
  test01();
  test02();
  test03<std::chrono::system_clock>();
  test03<std::chrono::steady_clock>();
  test03<steady_clock_copy>();
  test04();
  test_pr91486_wait_for();
  test_pr91486_wait_until();
  return 0;
}