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

// Copyright (C) 2010-2023 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 <iostream>

#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 steady_start = steady_clock::now();
  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 status = f1.wait_until(slow_start + milliseconds(100));
    VERIFY(status == std::future_status::timeout);
    auto const elapsed = steady_clock::now() - steady_start;
    if (elapsed < seconds(1))
      std::cout << elapsed.count () << "ns < 1s" << std::endl;
    VERIFY(elapsed >= seconds(1));
    VERIFY(elapsed < seconds(2));
  }

  // Wait for up to ~4s more, but since the async sleep completes, the
  // actual wait may be shorter than 1s.  Tolerate 3s because 2s
  // hasn't been enough in some extreme cases.
  {
    auto const steady_begin = steady_clock::now();
    auto const status = f1.wait_until(slow_start + milliseconds(500));
    VERIFY(status == std::future_status::ready);
    auto const elapsed = steady_clock::now() - steady_begin;
    if (elapsed >= seconds(3))
      std::cout << elapsed.count () << "ns > 2s" << std::endl;
    VERIFY(elapsed < seconds(3));
  }
}

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;
}
