Skip to content

Commit 5019b03

Browse files
committed
added chrono CycleTimer
added scope exit
1 parent fd6d171 commit 5019b03

6 files changed

Lines changed: 109 additions & 37 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#pragma once
2+
3+
#include <chrono>
4+
#include <optional>
5+
6+
#include <oryx/scope_exit.hpp>
7+
#include <oryx/chrono/stopwatch.hpp>
8+
9+
namespace oryx::chrono {
10+
11+
template <class Clock = std::chrono::steady_clock>
12+
requires std::chrono::is_clock_v<Clock>
13+
class CycleTimer {
14+
public:
15+
using Duration = std::chrono::milliseconds;
16+
17+
explicit CycleTimer(Duration target)
18+
: target_(target) {}
19+
20+
auto GetNextSleep() -> std::optional<Duration> {
21+
const auto elapsed = sw_.ElapsedMs();
22+
if (elapsed >= target_) {
23+
return std::nullopt;
24+
}
25+
26+
return Duration(target_ - elapsed);
27+
}
28+
29+
void Reset() { sw_.Reset(); }
30+
31+
auto target_cycle_time() const -> Duration { return target_; }
32+
33+
static auto MakeAutoResetGuard(CycleTimer<Clock>& timer) {
34+
return ScopeExit{[&timer] { timer.Reset(); }};
35+
};
36+
37+
static auto MakeFrameRateTimer(int target_fps) { return CycleTimer<Clock>{Duration{1000 / target_fps}}; }
38+
39+
private:
40+
const Duration target_;
41+
details::StopwatchImpl<Clock> sw_{};
42+
};
43+
44+
} // namespace oryx::chrono

include/oryx/scope_exit.hpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#pragma once
2+
3+
#include <utility>
4+
5+
namespace oryx {
6+
7+
template <class F>
8+
class ScopeExit {
9+
public:
10+
explicit ScopeExit(F&& fn)
11+
: fn_(std::forward<F>(fn)) {}
12+
13+
~ScopeExit() {
14+
if (is_owner_) {
15+
fn_();
16+
}
17+
}
18+
19+
void Release() {
20+
is_owner_ = false; // NOTE: Should this return the function object
21+
}
22+
23+
ScopeExit(ScopeExit&& other) = delete;
24+
ScopeExit(const ScopeExit&) = delete;
25+
auto operator=(const ScopeExit&) -> ScopeExit& = delete;
26+
27+
private:
28+
F fn_;
29+
bool is_owner_{true};
30+
};
31+
32+
} // namespace oryx

tests/chrono_mock_clock.hpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#pragma once
2+
3+
#include <chrono>
4+
5+
namespace oryx {
6+
7+
struct ChronoMockClock {
8+
using duration = std::chrono::nanoseconds;
9+
using rep = duration::rep;
10+
using period = duration::period;
11+
using time_point = std::chrono::time_point<std::chrono::steady_clock, duration>;
12+
13+
static constexpr bool is_steady = true;
14+
15+
static auto now() noexcept -> time_point { return now_; }
16+
17+
static time_point now_;
18+
};
19+
20+
ChronoMockClock::time_point ChronoMockClock::now_ = {};
21+
22+
static_assert(std::chrono::is_clock_v<ChronoMockClock>, "ChronoMockClock does not satisfy chrono clock!");
23+
24+
} // namespace oryx

tests/cycle_timer.cpp

Whitespace-only changes.

tests/scope_exit.cpp

Whitespace-only changes.

tests/stopwatch_test.cpp

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,27 @@
11
#include "doctest.hpp"
22

33
#include <chrono>
4-
#include <thread>
54

65
#include <oryx/chrono/stopwatch.hpp>
76

8-
using namespace oryx::chrono;
9-
10-
namespace {
11-
constexpr std::chrono::milliseconds kErrorMargin(100);
12-
13-
struct MockClock {
14-
using duration = std::chrono::nanoseconds;
15-
using rep = duration::rep;
16-
using period = duration::period;
17-
using time_point = std::chrono::time_point<std::chrono::steady_clock, duration>;
18-
19-
static constexpr bool is_steady = true;
20-
21-
static auto now() noexcept -> time_point { return now_; }
7+
#include "chrono_mock_clock.hpp"
228

23-
static time_point now_;
24-
};
25-
26-
MockClock::time_point MockClock::now_ = {};
27-
28-
} // namespace
29-
30-
TEST_CASE("Stopwatch should measure elapsed time") {
31-
std::chrono::milliseconds sleep_time(100);
32-
Stopwatch sw{};
33-
std::this_thread::sleep_for(sleep_time);
34-
auto elapsed = sw.ElapsedMs();
35-
CHECK_GE(elapsed, sleep_time);
36-
CHECK_LT(elapsed, sleep_time + kErrorMargin);
37-
}
9+
using namespace oryx::chrono;
3810

3911
TEST_CASE("Reset calls clock now") {
40-
details::StopwatchImpl<MockClock> sw{};
41-
const auto tp = MockClock::time_point{std::chrono::seconds(5)};
42-
MockClock::now_ = tp;
12+
details::StopwatchImpl<oryx::ChronoMockClock> sw{};
13+
const auto tp = oryx::ChronoMockClock::time_point{std::chrono::seconds(5)};
14+
oryx::ChronoMockClock::now_ = tp;
4315
sw.Reset();
4416
CHECK_EQ(sw.GetStart(), tp);
4517
}
4618

4719
TEST_CASE("Elapsed and elapsed ms subtracts now from start") {
48-
details::StopwatchImpl<MockClock> sw{MockClock::time_point{std::chrono::seconds(5)}};
49-
const auto tp = MockClock::time_point{std::chrono::seconds(10)};
50-
MockClock::now_ = tp;
20+
details::StopwatchImpl<oryx::ChronoMockClock> sw{oryx::ChronoMockClock::time_point{std::chrono::seconds(5)}};
21+
const auto tp = oryx::ChronoMockClock::time_point{std::chrono::seconds(10)};
22+
oryx::ChronoMockClock::now_ = tp;
5123
CHECK_EQ(sw.ElapsedMs(), std::chrono::milliseconds(5000));
5224

53-
MockClock::now_ = tp;
25+
oryx::ChronoMockClock::now_ = tp;
5426
CHECK_EQ(sw.Elapsed(), std::chrono::nanoseconds(5000000000));
5527
}

0 commit comments

Comments
 (0)