#include"Boss/Mod/Timers.hpp" #include"Boss/Mod/Waiter.hpp" #include"Boss/Msg/Init.hpp" #include"Boss/Msg/Timer10Minutes.hpp" #include"Boss/Msg/TimerRandomDaily.hpp" #include"Boss/Msg/TimerRandomHourly.hpp" #include"Boss/concurrent.hpp" #include"Boss/log.hpp" #include"Boss/random_engine.hpp" #include"Ev/Io.hpp" #include"S/Bus.hpp" #include #include #include namespace { template class RandomTimeLoop : public std::enable_shared_from_this> { private: std::string name; S::Bus& bus; Boss::Mod::Waiter& waiter; std::uniform_real_distribution dist; RandomTimeLoop( std::string name_ , S::Bus& bus_ , Boss::Mod::Waiter& waiter_ , std::uniform_real_distribution dist_ ) : name(std::move(name_)) , bus(bus_) , waiter(waiter_) , dist(std::move(dist_)) { } Ev::Io loop() { return Ev::lift().then([this]() { auto time = dist(Boss::random_engine); return waiter.wait(time); }).then([this]() { return Boss::log( bus, Boss::Debug , "Timers: triggering %s" , name.c_str() ); }).then([this]() { return Boss::concurrent(bus.raise(T())); }).then([this]() { return loop(); }); } public: ~RandomTimeLoop() { } static std::shared_ptr create( std::string name , S::Bus& bus , Boss::Mod::Waiter& waiter , std::uniform_real_distribution dist ) { /* Cannot use std::make_shared, constructor is private. */ return std::shared_ptr( new RandomTimeLoop( std::move(name) , bus , waiter , std::move(dist) ) ); } Ev::Io enter_loop() { auto self = RandomTimeLoop::shared_from_this(); return self->loop().then([self]() { /* This function becomes part of the * pass continuation that is given to * the loop operation. * This keeps the self-pointer alive. * This function never actually gets * invoked, but if an exception is * thrown within the Ev::Io system, * this function get destructed, and * only then will the self-pointer get * destructed. */ return Ev::lift(); }); } }; template Ev::Io random_time_loop( std::string name , S::Bus& bus , Boss::Mod::Waiter& waiter , std::uniform_real_distribution dist ) { auto loop_obj = RandomTimeLoop::create( std::move(name), bus, waiter, std::move(dist) ); return loop_obj->enter_loop(); } } namespace Boss { namespace Mod { void Timers::start() { bus.subscribe([this](Boss::Msg::Init const& _) { /* Start timers at init. */ return Ev::lift().then([this]() { return Boss::concurrent(random_daily()); }).then([this]() { return Boss::concurrent(random_hourly()); }).then([this]() { return Boss::concurrent(every_10_minutes()); }); }); } Ev::Io Timers::random_daily() { auto dist = std::uniform_real_distribution(43200, 86400 + 43200); return random_time_loop( "random daily", bus, waiter, std::move(dist) ); } Ev::Io Timers::random_hourly() { auto dist = std::uniform_real_distribution(1800, 3600 + 1800); return random_time_loop( "random hourly", bus, waiter, std::move(dist) ); } Ev::Io Timers::every_10_minutes() { /* Reuse the time loop to reduce our code burden. */ auto dist = std::uniform_real_distribution(600, 600); return random_time_loop( "regular 10 minutes", bus, waiter, std::move(dist) ); } }}