Commit 64e2ee87 authored by Dominik Charousset's avatar Dominik Charousset

Improve tick emitter

parent 8972ef8a
......@@ -103,6 +103,7 @@ set (LIBCAF_CORE_SRCS
src/test_actor_clock.cpp
src/test_coordinator.cpp
src/thread_safe_actor_clock.cpp
src/tick_emitter.cpp
src/timestamp.cpp
src/try_match.cpp
src/type_erased_tuple.cpp
......
......@@ -21,6 +21,7 @@
#define CAF_DETAIL_TICK_EMITTER_HPP
#include <chrono>
#include <initializer_list>
#include "caf/config.hpp"
......@@ -45,17 +46,23 @@ public:
// -- constructors, destructors, and assignment operators --------------------
tick_emitter(time_point now)
: start_(std::move(now)),
interval_(0),
last_tick_id_(0) {
// nop
}
tick_emitter();
void interval(duration_type x) {
interval_ = x;
}
tick_emitter(time_point now);
/// Queries whether the start time is non-default constructed.
bool started() const;
/// Configures the start time.
void start(time_point now);
/// Resets the start time to 0.
void stop();
/// Configures the time interval per tick.
void interval(duration_type x);
/// Advances time and calls `consumer` for each emitted tick.
template <class F>
void update(time_point now, F& consumer) {
CAF_ASSERT(interval_.count() != 0);
......@@ -65,6 +72,14 @@ public:
consumer(++last_tick_id_);
}
/// Advances time by `t` and returns all triggered periods as bitmask.
long timeouts(time_point t, std::initializer_list<long> periods);
/// Returns the next time point after `t` that would trigger any of the tick
/// periods, i.e., returns the next time where any of the tick periods
/// triggers `tick id % period == 0`.
time_point next_timeout(time_point t, std::initializer_list<long> periods);
private:
time_point start_;
duration_type interval_;
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2017 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/detail/tick_emitter.hpp"
#include "caf/logger.hpp"
namespace caf {
namespace detail {
tick_emitter::tick_emitter()
: start_(duration_type{0}),
interval_(0),
last_tick_id_(0) {
// nop
}
tick_emitter::tick_emitter(time_point now) : tick_emitter() {
start(std::move(now));
}
bool tick_emitter::started() const {
return start_.time_since_epoch().count() != 0;
}
void tick_emitter::start(time_point now) {
CAF_LOG_TRACE(CAF_ARG(now));
start_ = std::move(now);
}
void tick_emitter::stop() {
CAF_LOG_TRACE("");
start_ = time_point{duration_type{0}};
}
void tick_emitter::interval(duration_type x) {
CAF_LOG_TRACE(CAF_ARG(x));
interval_ = x;
}
long tick_emitter::timeouts(time_point now,
std::initializer_list<long> periods) {
CAF_LOG_TRACE(CAF_ARG(now) << CAF_ARG(periods));
long result = 0;
auto f = [&](long tick) {
long n = 0;
for (auto p : periods) {
if (tick % p == 0)
result |= 1l << n;
++n;
}
};
update(now, f);
return result;
}
tick_emitter::time_point
tick_emitter::next_timeout(time_point t, std::initializer_list<long> periods) {
CAF_ASSERT(interval_.count() != 0);
auto is_trigger = [&](long tick_id) {
for (auto period : periods)
if (tick_id % period == 0)
return true;
return false;
};
auto diff = t - start_;
auto this_tick = diff.count() / interval_.count();
auto tick_id = this_tick + 1;
while (!is_trigger(tick_id))
++tick_id;
return start_ + (interval_ * tick_id);
}
} // namespace detail
} // namespace caf
......@@ -29,10 +29,12 @@
// We mock just enough of an actor to use the streaming classes and put them to
// work in a pipeline with 2 or 3 stages.
#define CAF_SUITE time_emitter
#define CAF_SUITE tick_emitter
#include <vector>
#include "caf/timestamp.hpp"
#include "caf/detail/gcd.hpp"
#include "caf/detail/tick_emitter.hpp"
......@@ -42,16 +44,36 @@ using std::vector;
using namespace caf;
using time_point = caf::detail::tick_emitter::time_point;
namespace {
timespan credit_interval{200};
timespan force_batch_interval{50};
} // namespace <anonymous>
CAF_TEST(start_and_stop) {
detail::tick_emitter x;
detail::tick_emitter y{time_point{timespan{100}}};
detail::tick_emitter z;
z.start(time_point{timespan{100}});
CAF_CHECK_EQUAL(x.started(), false);
CAF_CHECK_EQUAL(y.started(), true);
CAF_CHECK_EQUAL(z.started(), true);
for (auto t : {&x, &y, &z})
t->stop();
CAF_CHECK_EQUAL(x.started(), false);
CAF_CHECK_EQUAL(y.started(), false);
CAF_CHECK_EQUAL(z.started(), false);
}
CAF_TEST(ticks) {
using timespan = std::chrono::microseconds;
timespan credit_interval{200};
timespan force_batch_interval{50};
auto cycle = detail::gcd(credit_interval.count(),
force_batch_interval.count());
CAF_CHECK_EQUAL(cycle, 50);
auto force_batch_frequency = force_batch_interval.count() / cycle;
auto credit_frequency = credit_interval.count() / cycle;
using time_point = std::chrono::steady_clock::time_point;
detail::tick_emitter tctrl{time_point{timespan{100}}};
tctrl.interval(timespan{cycle});
vector<long> ticks;
......@@ -76,3 +98,54 @@ CAF_TEST(ticks) {
CAF_CHECK_EQUAL(credit_triggers, 1);
}
CAF_TEST(timeouts) {
timespan interval{50};
time_point start{timespan{100}};
auto now = start;
detail::tick_emitter tctrl{now};
tctrl.interval(interval);
CAF_MESSAGE("advance until the first 5-tick-period ends");
now += interval * 5;
auto bitmask = tctrl.timeouts(now, {5, 7});
CAF_CHECK_EQUAL(bitmask, 0x01);
CAF_MESSAGE("advance until the first 7-tick-period ends");
now += interval * 2;
bitmask = tctrl.timeouts(now, {5, 7});
CAF_CHECK_EQUAL(bitmask, 0x02);
CAF_MESSAGE("advance until both tick period ends");
now += interval * 7;
bitmask = tctrl.timeouts(now, {5, 7});
CAF_CHECK_EQUAL(bitmask, 0x03);
CAF_MESSAGE("advance until both tick period end multiple times");
now += interval * 21;
bitmask = tctrl.timeouts(now, {5, 7});
CAF_CHECK_EQUAL(bitmask, 0x03);
CAF_MESSAGE("advance without any timeout");
now += interval * 1;
bitmask = tctrl.timeouts(now, {5, 7});
CAF_CHECK_EQUAL(bitmask, 0x00);
}
CAF_TEST(next_timeout) {
timespan interval{50};
time_point start{timespan{100}};
auto now = start;
detail::tick_emitter tctrl{now};
tctrl.interval(interval);
CAF_MESSAGE("advance until the first 5-tick-period ends");
auto next = tctrl.next_timeout(now, {5, 7});
CAF_CHECK_EQUAL(next, start + timespan(5 * interval));
CAF_MESSAGE("advance until the first 7-tick-period ends");
now = start + timespan(5 * interval);
next = tctrl.next_timeout(now, {5, 7});
CAF_CHECK_EQUAL(next, start + timespan(7 * interval));
CAF_MESSAGE("advance until the second 5-tick-period ends");
now = start + timespan(7 * interval);
next = tctrl.next_timeout(now, {5, 7});
CAF_CHECK_EQUAL(next, start + timespan((2 * 5) * interval));
CAF_MESSAGE("advance until the second 7-tick-period ends");
now = start + timespan(11 * interval);
next = tctrl.next_timeout(now, {5, 7});
CAF_CHECK_EQUAL(next, start + timespan((2 * 7) * interval));
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment