blob: 2144d46b75c151689beb2a1e26a0ccd77f3da776 [file]
/*
* Copyright 2010-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#pragma once
#include <condition_variable>
#include <future>
#include <mutex>
#include <thread>
#include "Saturating.hpp"
namespace kotlin {
namespace internal {
template <typename NowF, typename StepDuration, typename Clock, typename Rep, typename Period, typename Ret, typename WaitForF>
Ret waitUntilViaFor(
NowF&& nowF,
StepDuration step,
std::chrono::time_point<Clock, std::chrono::duration<saturating<Rep>, Period>> until,
Ret timeoutValue,
WaitForF&& waitForF) {
while (true) {
auto current = std::invoke(std::forward<NowF>(nowF));
if (current >= until) {
return timeoutValue;
}
auto left = until - current;
// Shield standard library from saturating types.
auto interval = left > step ? std::chrono::duration<Rep, Period>(step) : std::chrono::duration<Rep, Period>(left);
if (auto value = std::invoke(std::forward<WaitForF>(waitForF), interval); value != timeoutValue) {
return value;
}
}
}
template <typename NowF, typename StepDuration, typename Clock, typename Rep, typename Period, typename WaitForF>
void waitUntilViaFor(
NowF&& nowF,
StepDuration step,
std::chrono::time_point<Clock, std::chrono::duration<saturating<Rep>, Period>> until,
WaitForF&& waitForF) {
while (true) {
auto current = std::invoke(std::forward<NowF>(nowF));
if (current >= until) {
return;
}
auto left = until - current;
// Shield standard library from saturating types.
auto interval = left > step ? std::chrono::duration<Rep, Period>(step) : std::chrono::duration<Rep, Period>(left);
std::invoke(std::forward<WaitForF>(waitForF), interval);
}
}
template <typename T, typename Lock>
struct IsStdCV : public std::false_type {};
template <>
struct IsStdCV<std::condition_variable, std::unique_lock<std::mutex>> : public std::true_type {};
template <typename Lock>
struct IsStdCV<std::condition_variable_any, Lock> : public std::true_type {};
template <typename T, typename Lock>
inline constexpr bool isStdCV = IsStdCV<T, Lock>::value;
template <typename T>
struct IsStdFuture : public std::false_type {};
template <typename T>
struct IsStdFuture<std::future<T>> : public std::true_type {};
template <typename T>
struct IsStdFuture<std::shared_future<T>> : public std::true_type {};
template <typename T>
inline constexpr bool isStdFuture = IsStdFuture<T>::value;
template <typename Clock>
class ClockWaitImpl {
public:
template <typename Rep, typename Period>
static void sleep_for(std::chrono::duration<Rep, Period> interval) {
// Not using this_thread::sleep_for, because it may mishandle "infinite" intervals. Use saturating arithmetics to address this.
return ClockWaitImpl<Clock>::sleep_until(Clock::now() + interval);
}
template <typename Rep, typename Period>
static void sleep_until(std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>> until) {
if constexpr (is_saturating_v<Rep>) {
return Clock::sleepImpl(until);
} else {
return ClockWaitImpl<Clock>::sleep_until(std::chrono::time_point<Clock, std::chrono::duration<saturating<Rep>, Period>>(until));
}
}
template <typename CV, typename Lock, typename Rep, typename Period, typename F, typename = std::enable_if_t<isStdCV<CV, Lock>>>
static bool wait_for(CV& cv, Lock& lock, std::chrono::duration<Rep, Period> interval, F&& f) {
// Not using cv.wait_for, because it may mishandle "infinite" intervals. Use saturating arithmetics to address this.
return ClockWaitImpl<Clock>::wait_until(cv, lock, Clock::now() + interval, std::forward<F>(f));
}
template <typename CV, typename Lock, typename Rep, typename Period, typename F, typename = std::enable_if_t<isStdCV<CV, Lock>>>
static bool wait_until(CV& cv, Lock& lock, std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>> until, F&& f) {
if constexpr (is_saturating_v<Rep>) {
[[maybe_unused]] auto pendingWait = Clock::addPendingWait(until);
// Implement in terms of repeated cv.wait_for of non-"infinite" intervals.
return internal::waitUntilViaFor(&Clock::now, Clock::wait_step, until, false, [&](auto interval) {
return cv.wait_for(lock, interval, std::forward<F>(f));
});
} else {
return ClockWaitImpl<Clock>::wait_until(
cv, lock, std::chrono::time_point<Clock, std::chrono::duration<saturating<Rep>, Period>>(until), std::forward<F>(f));
}
}
template <typename Future, typename Rep, typename Period, typename = std::enable_if_t<isStdFuture<Future>>>
static std::future_status wait_for(const Future& future, std::chrono::duration<Rep, Period> interval) {
// Not using future.wait_for, because it may mishandle "infinite" intervals. Use saturating arithmetics to address this.
return ClockWaitImpl<Clock>::wait_until(future, Clock::now() + interval);
}
template <typename Future, typename Rep, typename Period, typename = std::enable_if_t<isStdFuture<Future>>>
static std::future_status wait_until(const Future& future, std::chrono::time_point<Clock, std::chrono::duration<Rep, Period>> until) {
if constexpr (is_saturating_v<Rep>) {
[[maybe_unused]] auto pendingWait = Clock::addPendingWait(until);
// Implement in terms of repeated future.wait_for of non-"infinite" intervals.
return internal::waitUntilViaFor(&Clock::now, Clock::wait_step, until, std::future_status::timeout, [&](auto interval) {
return future.wait_for(interval);
});
} else {
return ClockWaitImpl<Clock>::wait_until(
future, std::chrono::time_point<Clock, std::chrono::duration<saturating<Rep>, Period>>(until));
}
}
};
} // namespace internal
using nanoseconds = std::chrono::duration<saturating<std::chrono::nanoseconds::rep>, std::chrono::nanoseconds::period>;
using microseconds = std::chrono::duration<saturating<std::chrono::microseconds::rep>, std::chrono::microseconds::period>;
using milliseconds = std::chrono::duration<saturating<std::chrono::milliseconds::rep>, std::chrono::milliseconds::period>;
using seconds = std::chrono::duration<saturating<std::chrono::seconds::rep>, std::chrono::seconds::period>;
using minutes = std::chrono::duration<saturating<std::chrono::minutes::rep>, std::chrono::minutes::period>;
using hours = std::chrono::duration<saturating<std::chrono::hours::rep>, std::chrono::hours::period>;
class steady_clock : public internal::ClockWaitImpl<steady_clock> {
public:
using rep = saturating<std::chrono::steady_clock::rep>;
using period = std::chrono::steady_clock::period;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<steady_clock>;
static constexpr bool is_steady = true;
static time_point now() noexcept {
auto time = std::chrono::steady_clock::now().time_since_epoch();
return time_point(time);
}
private:
friend class internal::ClockWaitImpl<steady_clock>;
// Use non-saturating type here, because step may be fed into the standard library.
static inline constexpr auto wait_step = std::chrono::hours(24);
template <typename Rep, typename Period>
static void sleepImpl(std::chrono::time_point<steady_clock, std::chrono::duration<saturating<Rep>, Period>> until) {
// Implement in terms of repeated this_thread::sleep_for of non-"infinite" intervals.
return internal::waitUntilViaFor(&now, wait_step, until, [&](auto interval) { std::this_thread::sleep_for(interval); });
}
template <typename Rep, typename Period>
static int addPendingWait(std::chrono::time_point<steady_clock, std::chrono::duration<saturating<Rep>, Period>> until) {
// No need to register here.
return 0;
}
};
} // namespace kotlin