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
|
2015-01-12 15:04:10 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2015
|
2014-08-29 07:52:33 -07:00
|
|
|
*/
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
2014-11-17 16:03:26 -08:00
|
|
|
//static int timerIsLate = 0;
|
|
|
|
//static uint64_t callbackTime = 0;
|
2014-11-15 08:05:13 -08:00
|
|
|
/**
|
|
|
|
* these fields are global in order to facilitate debugging
|
|
|
|
*/
|
|
|
|
static uint64_t nextEventTimeNt = 0;
|
|
|
|
static uint64_t hwAlarmTime = 0;
|
|
|
|
|
2014-08-29 07:52:33 -07:00
|
|
|
static void executorCallback(void *arg) {
|
2014-09-24 11:04:07 -07:00
|
|
|
(void)arg;
|
2014-11-08 16:03:17 -08:00
|
|
|
efiAssertVoid(getRemainingStack(chThdSelf()) > 256, "lowstck#2y");
|
2014-11-15 08:05:13 -08:00
|
|
|
|
|
|
|
// callbackTime = getTimeNowNt();
|
|
|
|
// if((callbackTime > nextEventTimeNt) && (callbackTime - nextEventTimeNt > US2NT(5000))) {
|
|
|
|
// timerIsLate++;
|
|
|
|
// }
|
|
|
|
|
2014-09-14 13:04:20 -07:00
|
|
|
instance.onTimerCallback();
|
2014-08-29 07:52:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
Executor::Executor() {
|
2014-11-25 13:03:16 -08:00
|
|
|
reentrantFlag = false;
|
2014-11-10 11:06:23 -08:00
|
|
|
queue.setLateDelay(US2NT(100));
|
2014-08-29 07:52:33 -07:00
|
|
|
}
|
|
|
|
|
2014-11-25 13:03:16 -08:00
|
|
|
#define lock() lockAnyContext()
|
|
|
|
#define unlock() unlockAnyContext()
|
2014-08-29 07:52:33 -07:00
|
|
|
|
2015-01-19 12:05:26 -08:00
|
|
|
void Executor::scheduleByTime(scheduling_s *scheduling, efitimeus_t timeUs, schfunc_t callback,
|
2014-09-14 14:03:05 -07:00
|
|
|
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-11-25 13:03:16 -08:00
|
|
|
if (!reentrantFlag) {
|
2014-08-29 07:52:33 -07:00
|
|
|
// this would guard the queue and disable interrupts
|
|
|
|
lock();
|
|
|
|
}
|
2014-11-10 11:06:23 -08:00
|
|
|
queue.insertTask(scheduling, US2NT(timeUs), callback, param);
|
2014-11-25 13:03:16 -08:00
|
|
|
if (!reentrantFlag) {
|
2014-09-14 13:04:20 -07:00
|
|
|
doExecute();
|
2014-08-29 07:52:33 -07:00
|
|
|
unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-17 16:03:26 -08:00
|
|
|
void Executor::schedule(scheduling_s *scheduling, uint64_t nowUs, int delayUs, schfunc_t callback,
|
2014-09-14 14:03:05 -07:00
|
|
|
void *param) {
|
2015-01-19 12:05:26 -08:00
|
|
|
scheduleByTime(scheduling, nowUs + delayUs, callback, param);
|
2014-09-13 11:02:53 -07:00
|
|
|
}
|
|
|
|
|
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.
|
2014-11-25 13:03:16 -08:00
|
|
|
* reentrantFlag takes care of the use case where the actions we are executing are scheduling
|
2014-08-29 07:52:33 -07:00
|
|
|
* further invocations
|
|
|
|
*/
|
2014-11-25 13:03:16 -08:00
|
|
|
reentrantFlag = 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
|
|
|
|
*/
|
2014-11-10 11:06:23 -08:00
|
|
|
uint64_t nowNt = getTimeNowNt();
|
|
|
|
shouldExecute = queue.executeAll(nowNt);
|
2014-09-14 14:03:05 -07:00
|
|
|
}
|
2014-08-29 07:52:33 -07:00
|
|
|
if (!isLocked()) {
|
|
|
|
firmwareError("Someone has stolen my lock");
|
|
|
|
return;
|
|
|
|
}
|
2014-11-10 11:06:23 -08:00
|
|
|
uint64_t nowNt = getTimeNowNt();
|
2014-11-25 13:03:16 -08:00
|
|
|
reentrantFlag = 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
|
|
|
|
*/
|
2014-11-15 08:05:13 -08:00
|
|
|
nextEventTimeNt = queue.getNextEventTime(nowNt);
|
2014-11-10 11:06:23 -08:00
|
|
|
efiAssertVoid(nextEventTimeNt > nowNt, "setTimer constraint");
|
|
|
|
if (nextEventTimeNt == EMPTY_QUEUE)
|
2014-08-29 07:52:33 -07:00
|
|
|
return; // no pending events in the queue
|
2014-11-15 08:05:13 -08:00
|
|
|
hwAlarmTime = NT2US(nextEventTimeNt - nowNt);
|
|
|
|
setHardwareUsTimer(hwAlarmTime == 0 ? 1 : hwAlarmTime);
|
2014-08-29 07:52:33 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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-11-17 16:03:26 -08:00
|
|
|
scheduling->name = prefix;
|
2015-01-19 12:05:26 -08:00
|
|
|
instance.scheduleByTime(scheduling, getTimeNowUs() + delayUs, callback, param);
|
2014-08-29 07:52:33 -07:00
|
|
|
}
|
|
|
|
|
2015-01-19 11:04:10 -08:00
|
|
|
void scheduleByTime(const char *prefix, scheduling_s *scheduling, efitimeus_t time, schfunc_t callback, void *param) {
|
2014-11-17 16:03:26 -08:00
|
|
|
scheduling->name = prefix;
|
2015-01-19 12:05:26 -08:00
|
|
|
instance.scheduleByTime(scheduling, time, callback, param);
|
2014-09-13 11:02:53 -07:00
|
|
|
}
|
|
|
|
|
2014-08-29 07:52:33 -07:00
|
|
|
void initSignalExecutorImpl(void) {
|
|
|
|
globalTimerCallback = executorCallback;
|
|
|
|
#if EFI_PROD_CODE
|
|
|
|
initMicrosecondTimer();
|
|
|
|
#endif /* EFI_PROD_CODE */
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|