f33a43f9f7
As Jonathan Wakely pointed out[1], my change in commit
f9ddb696a2
should have been rounding to
the target clock duration type rather than the input clock duration type
in __atomic_futex_unsigned::_M_load_when_equal_until just as (e.g.)
condition_variable does.
As well as fixing this, let's create a rather contrived test that fails
with the previous code, but unfortunately only when run on a machine
with an uptime of over 208.5 days, and even then not always.
[1] https://gcc.gnu.org/pipermail/libstdc++/2020-September/051004.html
libstdc++-v3/ChangeLog:
PR libstdc++/91486
* include/bits/atomic_futex.h:
(__atomic_futex_unsigned::_M_load_when_equal_until): Use target
clock duration type when rounding.
* testsuite/30_threads/async/async.cc (test_pr91486_wait_for):
Rename from test_pr91486.
(float_steady_clock): New class for test.
(test_pr91486_wait_until): New test.
243 lines
8.0 KiB
C++
243 lines
8.0 KiB
C++
// { dg-do run }
|
|
// { dg-additional-options "-pthread" { target pthread } }
|
|
// { dg-require-effective-target c++11 }
|
|
// { dg-require-gthreads "" }
|
|
|
|
// Copyright (C) 2010-2020 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 <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;
|
|
}
|