230 lines
4.0 KiB
C++
230 lines
4.0 KiB
C++
// 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 <core/log/Log.h>
|
|
|
|
namespace timprepscius {
|
|
namespace core {
|
|
namespace time {
|
|
|
|
SchedulerManual::SchedulerManual(const StrongPtr<TimeSource> &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<void()> &&f_
|
|
) :
|
|
parent(parent_),
|
|
period(period_),
|
|
repeat(repeat_),
|
|
f(f_)
|
|
{
|
|
}
|
|
|
|
SchedulerEventPtr SchedulerManual::schedule_no_lock (const StrongPtr<EventImp> &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> &eventImp, Time now)
|
|
{
|
|
auto l = lock_of(mutex);
|
|
return schedule_no_lock(eventImp, now);
|
|
}
|
|
|
|
SchedulerEventPtr SchedulerManual::schedule (Time period, bool repeat, std::function<void()> &&f)
|
|
{
|
|
auto eventImp = strong<EventImp>(
|
|
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<double>(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
|