fome-fw/firmware/controllers/system/SingleTimerExecutor.cpp

148 lines
3.9 KiB
C++
Raw Normal View History

2014-08-29 07:52:33 -07:00
/**
* @file SingleTimerExecutor.cpp
*
* This class combines the powers of a 1MHz hardware timer from microsecond_timer.c
* and pending events queue event_queue.cpp
*
* As of version 2.6.x, ChibiOS tick-based kernel is not capable of scheduling events
* with the level of precision we need, and realistically it should not.
*
* http://sourceforge.net/p/rusefi/tickets/24/
*
* @date: Apr 18, 2014
* @author Andrey Belomutskiy, (c) 2012-2014
*/
#include "SingleTimerExecutor.h"
#include "efitime.h"
#if EFI_PROD_CODE
#include "microsecond_timer.h"
#endif
2014-09-27 13:02:54 -07:00
#if (EFI_SIGNAL_EXECUTOR_ONE_TIMER && EFI_PROD_CODE )|| defined(__DOXYGEN__)
#include "rfiutil.h"
2014-08-29 07:52:33 -07:00
static Executor instance;
extern schfunc_t globalTimerCallback;
static void executorCallback(void *arg) {
2014-09-24 11:04:07 -07:00
(void)arg;
2014-11-08 06:03:25 -08:00
efiAssertVoid(getRemainingStack(chThdSelf()) > 32, "lowstck#2y");
2014-09-14 13:04:20 -07:00
instance.onTimerCallback();
2014-08-29 07:52:33 -07:00
}
Executor::Executor() {
reentrantLock = false;
}
void Executor::lock(void) {
lockAnyContext();
}
void Executor::unlock(void) {
unlockAnyContext();
}
2014-09-14 14:03:05 -07:00
void Executor::schedule2(const char *prefix, scheduling_s *scheduling, uint64_t timeUs, schfunc_t callback,
void *param) {
2014-09-13 11:02:53 -07:00
// if (delayUs < 0) {
// firmwareError("Negative delayUs %s: %d", prefix, delayUs);
// return;
// }
// if (delayUs == 0) {
// callback(param);
// return;
// }
2014-08-29 07:52:33 -07:00
if (!reentrantLock) {
// this would guard the queue and disable interrupts
lock();
}
2014-09-13 11:02:53 -07:00
queue.insertTask(scheduling, timeUs, callback, param);
2014-08-29 07:52:33 -07:00
if (!reentrantLock) {
2014-09-14 13:04:20 -07:00
doExecute();
2014-08-29 07:52:33 -07:00
unlock();
}
}
2014-09-14 14:03:05 -07:00
void Executor::schedule(const char *prefix, scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback,
void *param) {
2014-09-13 11:02:53 -07:00
schedule2(prefix, scheduling, nowUs + delayUs, callback, param);
}
2014-09-14 13:04:20 -07:00
void Executor::onTimerCallback() {
2014-08-29 07:52:33 -07:00
lock();
2014-09-14 13:04:20 -07:00
doExecute();
2014-08-29 07:52:33 -07:00
unlock();
}
/*
* this private method is executed under lock
*/
2014-09-14 13:04:20 -07:00
void Executor::doExecute() {
2014-08-29 07:52:33 -07:00
/**
* Let's execute actions we should execute at this point.
* reentrantLock takes care of the use case where the actions we are executing are scheduling
* further invocations
*/
reentrantLock = TRUE;
2014-09-14 14:03:05 -07:00
bool shouldExecute = true;
2014-09-13 14:02:50 -07:00
/**
2014-09-14 14:03:05 -07:00
* in real life it could be that while we executing listeners time passes and it's already time to execute
* next listeners.
* TODO: add a counter & figure out a limit of iterations?
2014-09-13 14:02:50 -07:00
*/
2014-09-14 14:03:05 -07:00
while (shouldExecute) {
/**
* It's worth noting that that the actions might be adding new actions into the queue
*/
uint64_t nowUs = getTimeNowUs();
shouldExecute = queue.executeAll(nowUs);
}
2014-08-29 07:52:33 -07:00
if (!isLocked()) {
firmwareError("Someone has stolen my lock");
return;
}
2014-09-14 14:03:05 -07:00
uint64_t nowUs = getTimeNowUs();
2014-08-29 07:52:33 -07:00
reentrantLock = false;
2014-09-14 13:04:20 -07:00
/**
* 'executeAll' is potentially invoking heavy callbacks, let's grab fresh time value?
*/
2014-08-29 07:52:33 -07:00
/**
* Let's set up the timer for the next execution
*/
uint64_t nextEventTime = queue.getNextEventTime(nowUs);
efiAssertVoid(nextEventTime > nowUs, "setTimer constraint");
if (nextEventTime == EMPTY_QUEUE)
return; // no pending events in the queue
setHardwareUsTimer(nextEventTime - nowUs);
}
/**
* @brief Schedule an event
*
* Invokes event callback after the specified amount of time.
*
* @param [in, out] scheduling Data structure to keep this event in the collection.
* @param [in] delayUs the number of microseconds before the output signal immediate output if delay is zero.
* @param [in] dwell the number of ticks of output duration.
*/
void scheduleTask(const char *prefix, scheduling_s *scheduling, int delayUs, schfunc_t callback, void *param) {
2014-09-13 05:02:58 -07:00
instance.schedule(prefix, scheduling, getTimeNowUs(), delayUs, callback, param);
2014-08-29 07:52:33 -07:00
}
2014-09-13 11:02:53 -07:00
void scheduleTask2(const char *prefix, scheduling_s *scheduling, uint64_t time, schfunc_t callback, void *param) {
instance.schedule2(prefix, scheduling, time, callback, param);
}
2014-08-29 07:52:33 -07:00
void initSignalExecutorImpl(void) {
globalTimerCallback = executorCallback;
#if EFI_PROD_CODE
initMicrosecondTimer();
#endif /* EFI_PROD_CODE */
}
#endif