blob: 6ab304429137a0722675be543ddac90c865479fd [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#pragma once
#include <stdint.h>
#include <limits>
#include <ostream>
#include <type_traits>
#include "callback.h"
#include "optional.h"
#include <iostream>
namespace overnet {
// Number of microseconds per millisecond.
static const uint64_t kUsPerMs = 1000;
static const uint64_t kUsPerSec = 1000000;
class TimeDelta {
public:
static constexpr TimeDelta PositiveInf() {
return TimeDelta(std::numeric_limits<int64_t>::max());
}
static constexpr TimeDelta NegativeInf() {
return TimeDelta(std::numeric_limits<int64_t>::min());
}
static constexpr TimeDelta Zero() { return TimeDelta(0); }
static constexpr TimeDelta FromMicroseconds(int64_t delta) {
return TimeDelta(delta);
}
static constexpr TimeDelta FromMilliseconds(int64_t delta) {
return TimeDelta(delta * kUsPerMs);
}
static constexpr TimeDelta FromSeconds(int64_t delta) {
return TimeDelta(delta * kUsPerSec);
}
static constexpr TimeDelta FromMinutes(int64_t delta) {
return FromSeconds(delta * 60);
}
static constexpr TimeDelta FromHours(int64_t delta) {
return FromMinutes(delta * 60);
}
constexpr int64_t as_us() const { return delta_; }
private:
explicit constexpr TimeDelta(int64_t delta) : delta_(delta) {}
int64_t delta_;
};
class TimeStamp {
public:
static constexpr TimeStamp Epoch() { return TimeStamp(TimeDelta::Zero()); }
static constexpr TimeStamp AfterEpoch(TimeDelta td) { return TimeStamp(td); }
constexpr TimeDelta after_epoch() const { return td_; }
private:
explicit constexpr TimeStamp(TimeDelta td) : td_(td) {}
TimeDelta td_;
};
inline constexpr bool operator==(TimeDelta a, TimeDelta b) {
return a.as_us() == b.as_us();
}
inline constexpr bool operator!=(TimeDelta a, TimeDelta b) {
return a.as_us() != b.as_us();
}
inline std::ostream& operator<<(std::ostream& out, TimeDelta a) {
if (a == TimeDelta::PositiveInf())
return out << "+inf";
if (a == TimeDelta::NegativeInf())
return out << "-inf";
auto us = a.as_us();
if (us > -1000 && us < 1000)
return out << us << "us";
if (us > -1000000 && us < 1000000)
return out << (us / 1000.0) << "ms";
return out << (us / 1000000.0) << "s";
}
inline std::ostream& operator<<(std::ostream& out, TimeStamp ts) {
return out << '@' << ts.after_epoch();
}
inline constexpr TimeDelta operator+(TimeDelta a, TimeDelta b) {
if (a == TimeDelta::PositiveInf() || a == TimeDelta::NegativeInf() ||
b == TimeDelta::Zero()) {
return a;
}
if (b == TimeDelta::PositiveInf())
return b;
if (b == TimeDelta::NegativeInf())
return b;
if (b.as_us() > 0) {
if (a.as_us() > 0 &&
b.as_us() > TimeDelta::PositiveInf().as_us() - a.as_us()) {
return TimeDelta::PositiveInf();
}
} else {
if (a.as_us() < 0 &&
b.as_us() < TimeDelta::NegativeInf().as_us() - a.as_us()) {
return TimeDelta::NegativeInf();
}
}
return TimeDelta::FromMicroseconds(a.as_us() + b.as_us());
}
inline constexpr TimeDelta operator-(TimeDelta x) {
if (x == TimeDelta::NegativeInf())
return TimeDelta::PositiveInf();
if (x == TimeDelta::PositiveInf())
return TimeDelta::NegativeInf();
return TimeDelta::FromMicroseconds(-x.as_us());
}
inline constexpr TimeDelta operator-(TimeDelta a, TimeDelta b) {
return a + (-b);
}
inline constexpr bool operator>(TimeDelta a, TimeDelta b) {
return a.as_us() > b.as_us();
}
inline constexpr bool operator<(TimeDelta a, TimeDelta b) {
return a.as_us() < b.as_us();
}
inline constexpr bool operator>=(TimeDelta a, TimeDelta b) {
return a.as_us() >= b.as_us();
}
inline constexpr bool operator<=(TimeDelta a, TimeDelta b) {
return a.as_us() <= b.as_us();
}
inline constexpr TimeDelta operator-(TimeStamp a, TimeStamp b) {
return a.after_epoch() - b.after_epoch();
}
inline constexpr TimeStamp operator+(TimeStamp a, TimeDelta b) {
return TimeStamp::AfterEpoch(a.after_epoch() + b);
}
inline constexpr TimeDelta operator*(int multiplier, TimeDelta x) {
if (multiplier == 0)
return TimeDelta::Zero();
bool neg = false;
if (multiplier < 0) {
neg = true;
multiplier = -multiplier;
}
TimeDelta r = x;
for (int i = 1; i < multiplier; i++) {
r = r + x;
}
if (neg) {
return -r;
} else {
return r;
}
}
inline constexpr TimeDelta operator/(TimeDelta x, int divisor) {
if (divisor == 0) {
if (x.as_us() < 0) {
return TimeDelta::NegativeInf();
} else {
return TimeDelta::PositiveInf();
}
}
if (x == TimeDelta::NegativeInf()) {
return TimeDelta::NegativeInf();
}
if (x == TimeDelta::PositiveInf()) {
return TimeDelta::PositiveInf();
}
return TimeDelta::FromMicroseconds(x.as_us() / divisor);
}
inline constexpr bool operator>(TimeStamp a, TimeStamp b) {
return a.after_epoch() > b.after_epoch();
}
inline constexpr bool operator<(TimeStamp a, TimeStamp b) {
return a.after_epoch() < b.after_epoch();
}
inline constexpr bool operator>=(TimeStamp a, TimeStamp b) {
return a.after_epoch() >= b.after_epoch();
}
inline constexpr bool operator<=(TimeStamp a, TimeStamp b) {
return a.after_epoch() <= b.after_epoch();
}
inline constexpr bool operator==(TimeStamp a, TimeStamp b) {
return a.after_epoch() == b.after_epoch();
}
inline constexpr bool operator!=(TimeStamp a, TimeStamp b) {
return a.after_epoch() != b.after_epoch();
}
class Timeout;
class Timer {
friend class Timeout;
public:
virtual TimeStamp Now() = 0;
template <class F>
void At(TimeStamp t, F f);
void At(TimeStamp t, StatusCallback cb);
protected:
template <class T>
T* TimeoutStorage(Timeout* timeout);
static void FireTimeout(Timeout* timeout, Status status);
private:
virtual void InitTimeout(Timeout* timeout, TimeStamp when) = 0;
virtual void CancelTimeout(Timeout* timeout, Status status) = 0;
};
class Timeout {
friend class Timer;
public:
static constexpr uint64_t kMaxTimerStorage = 5 * sizeof(void*);
Timeout(const Timeout&) = delete;
Timeout& operator=(const Timeout&) = delete;
// Initialize a timeout for timestamp when. cb will be called when the timeout
// expires (with status OK) or when the timeout is cancelled (with non-OK
// status).
Timeout(Timer* timer, TimeStamp when, StatusCallback cb)
: timer_(timer), cb_(std::move(cb)) {
assert(!cb_.empty());
timer_->InitTimeout(this, when);
}
~Timeout() {
#ifndef NDEBUG
assert(!destroyed_);
destroyed_ = true;
#endif
if (!cb_.empty())
Cancel();
assert(cb_.empty());
}
void Cancel(Status status = Status::Cancelled()) {
timer_->CancelTimeout(this, std::move(status));
}
private:
#ifndef NDEBUG
bool destroyed_ = false;
#endif
Timer* const timer_;
StatusCallback cb_;
typename std::aligned_storage<kMaxTimerStorage>::type storage_;
};
template <class T>
T* Timer::TimeoutStorage(Timeout* timeout) {
static_assert(Timeout::kMaxTimerStorage >= sizeof(T),
"Must have enough storage in Timeout for Timer data");
return reinterpret_cast<T*>(&timeout->storage_);
}
inline void Timer::FireTimeout(Timeout* timeout, Status status) {
if (timeout->cb_.empty())
return;
auto cb = std::move(timeout->cb_);
cb(status);
}
template <class F>
void Timer::At(TimeStamp t, F f) {
At(t, StatusCallback(ALLOCATED_CALLBACK,
[f = std::move(f)](const Status& status) mutable {
if (status.is_ok()) {
f();
}
}));
}
} // namespace overnet