// TJP COPYRIGHT HEADER // // Timer.h // algorithm // // Created by Timothy Prepscius on 1/19/18. // Copyright © 2018 Timothy Prepscius. All rights reserved. // #include "SchedulerManual.hpp" #include namespace timprepscius { namespace core { namespace time { SchedulerManual::SchedulerManual(const StrongPtr &timeSource_) : timeSource (timeSource_), running(false), stopped(false), nextIndex(1) { } SchedulerManual::~SchedulerManual() { debug_assert(!running); } SchedulerManual::EventImp::EventImp( SchedulerManual *parent_, Time period_, bool repeat_, std::function &&f_ ) : parent(parent_), period(period_), repeat(repeat_), f(f_) { } SchedulerEventPtr SchedulerManual::schedule_no_lock (const StrongPtr &eventImp, Time now) { debug_assert(mutex.locked_by_caller()); eventImp->at = { now + eventImp->period, nextIndex++ }; sLogDebug ("core::time::SchedulerManual", logVar(ptr_of(eventImp)) << logVar(now) << logVar(eventImp->at.time) << logVar(eventImp->period) << logVar(eventImp->repeat)); auto insertion = events.emplace(eventImp->at, weak(eventImp)); debug_assert(insertion.second); debug_assert(eventImp->position == events.end()); eventImp->position = insertion.first; changed.notify_all(); return eventImp; } SchedulerEventPtr SchedulerManual::schedule (const StrongPtr &eventImp, Time now) { auto l = lock_of(mutex); return schedule_no_lock(eventImp, now); } SchedulerEventPtr SchedulerManual::schedule (Time period, bool repeat, std::function &&f) { auto eventImp = strong( this, period, repeat, std::move(f) ); eventImp->position = events.end(); schedule(eventImp, timeSource->getTime()); return eventImp; } void SchedulerManual::EventImp::cancel () { auto lock = lock_of(mutex); parent->cancel(this); } void SchedulerManual::cancel(EventImp *event) { sLogDebug("core::time::scheduler", logVar(event)); if (event) { auto l = lock_of(mutex); if (event->position != events.end()) events.erase(event->position); event->position = events.end(); changed.notify_all(); } } Duration SchedulerManual::durationUntilNextEvent() { debug_assert(mutex.locked_by_caller()); if (events.empty()) return time::FarFuture; return events.begin()->first.time - timeSource->getTime(); } void SchedulerManual::update_has_lock () { auto currentTime = EventTime { timeSource->getTime(), nextIndex }; while (!events.empty()) { if (stopped) return; auto position = events.begin(); auto &event_ = position->second; if (position->first < currentTime) { auto event = strong(event_); debug_assert(mutex.locked_by_caller()); events.erase(position); if (event) { event->position = events.end(); if (event->repeat) { schedule_no_lock(event, currentTime.time); } } mutex.unlock(); if (event) { auto lock = lock_of(event->mutex); try { event->f(); } catch (Exception &e) { sLogError("core::time::SchedulerManual", "there was a problem houston " << e.what()); } } } else { break; } mutex.lock(); } }; void SchedulerManual::run() { running = true; stopped = false; mutex.lock(); while (!stopped) { auto duration = std::chrono::duration(toReal(durationUntilNextEvent())); auto now = std::chrono::system_clock::now(); auto then = now + duration; changed.wait_until(mutex, then); update_has_lock(); } running = false; stopped_.notify_all(); mutex.unlock(); } void SchedulerManual::cycle() { mutex.lock(); running = true; if (!events.empty()) { update_has_lock(); } running = false; mutex.unlock(); } void SchedulerManual::stop () { auto lock = lock_of(mutex); stopped = true; changed.notify_all(); } void SchedulerManual::clear () { auto l = lock_of(mutex); for (auto &[id, event_]: events) { if (auto event = strong(event_)) { event->position = events.end(); } } events.clear(); } } // namespace } // namespace } // namespace