// Copyright (C) 2017-2021 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/>.

// { dg-do run }
// { dg-additional-options "-pthread" { target pthread } }
// { dg-require-effective-target c++11 }
// { dg-require-gthreads "" }

#include <condition_variable>
#include <testsuite_hooks.h>

// PR libstdc++/68519

void
test_wait_for()
{
  std::mutex mx;
  std::condition_variable cv;

  for (int i = 0; i < 3; ++i)
  {
    std::unique_lock<std::mutex> l(mx);
    auto start = std::chrono::system_clock::now();
    cv.wait_for(l, std::chrono::duration<float>(1), [] { return false; });
    auto t = std::chrono::system_clock::now();
    VERIFY( (t - start) >= std::chrono::seconds(1) );
  }
}

// In order to ensure that the delta calculated in the arbitrary clock overload
// of condition_variable::wait_until fits accurately in a float, but the result
// of adding it to steady_clock with a float duration does not, this clock
// needs to use a more recent epoch.
struct recent_epoch_float_clock
{
  using duration = std::chrono::duration<float>;
  using rep = duration::rep;
  using period = duration::period;
  using time_point
    = std::chrono::time_point<recent_epoch_float_clock, duration>;
  static constexpr bool is_steady = true;

  static const std::chrono::steady_clock::time_point epoch;

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

const std::chrono::steady_clock::time_point recent_epoch_float_clock::epoch =
  std::chrono::steady_clock::now();

void
test_wait_until()
{
  using clock = recent_epoch_float_clock;

  std::mutex mx;
  std::condition_variable cv;

  for (int i = 0; i < 3; ++i)
  {
    std::unique_lock<std::mutex> l(mx);
    const auto start = clock::now();
    const auto wait_time = start + std::chrono::duration<float>{1.0};

    // In theory we could get a spurious wakeup, but in practice we won't.
    const auto result = cv.wait_until(l, wait_time);

    VERIFY( result == std::cv_status::timeout );
    const auto elapsed = clock::now() - start;
    VERIFY( elapsed >= std::chrono::seconds(1) );
  }
}

int
main()
{
  test_wait_for();
  test_wait_until();
}
