custom-board-bundle-sample-.../firmware/controllers/system/timer/event_queue.cpp

238 lines
6.5 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file event_queue.cpp
* This is a data structure which keeps track of all pending events
* Implemented as a linked list, which is fine since the number of
* pending events is pretty low
* todo: MAYBE migrate to a better data structure, but that's low priority
*
* this data structure is NOT thread safe
*
* @date Apr 17, 2014
2018-01-20 17:55:31 -08:00
* @author Andrey Belomutskiy, (c) 2012-2018
2015-07-10 06:01:56 -07:00
*/
2018-09-16 19:26:57 -07:00
#include "global.h"
#include "os_access.h"
2015-07-10 06:01:56 -07:00
#include "event_queue.h"
#include "efitime.h"
2019-07-06 17:15:49 -07:00
#include "os_util.h"
2019-10-11 17:43:21 -07:00
#include "perf_trace.h"
2015-07-10 06:01:56 -07:00
2019-08-24 23:01:09 -07:00
#if EFI_UNIT_TEST
extern bool verboseMode;
#endif /* EFI_UNIT_TEST */
2017-05-21 08:09:47 -07:00
uint32_t maxSchedulingPrecisionLoss = 0;
2015-07-10 06:01:56 -07:00
EventQueue::EventQueue() {
2019-09-22 05:22:35 -07:00
head = nullptr;
2015-07-10 06:01:56 -07:00
setLateDelay(100);
}
bool EventQueue::checkIfPending(scheduling_s *scheduling) {
assertNotInListMethodBody(scheduling_s, head, scheduling, nextScheduling_s);
2015-07-10 06:01:56 -07:00
}
/**
* @return true if inserted into the head of the list
*/
2017-05-15 06:21:40 -07:00
bool EventQueue::insertTask(scheduling_s *scheduling, efitime_t timeX, schfunc_t callback, void *param) {
2019-10-11 17:43:21 -07:00
ScopePerf perf(PE::EventQueueInsertTask);
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
assertListIsSorted();
2016-08-27 07:03:11 -07:00
#endif /* EFI_UNIT_TEST */
2018-07-25 20:30:00 -07:00
efiAssert(CUSTOM_ERR_ASSERT, callback != NULL, "NULL callback", false);
2015-07-10 06:01:56 -07:00
2016-09-23 22:02:43 -07:00
// please note that simulator does not use this code at all - simulator uses signal_executor_sleep
2016-09-04 22:03:25 -07:00
if (scheduling->isScheduled) {
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2019-08-24 23:01:09 -07:00
if (verboseMode) {
printf("Already scheduled was %d\r\n", (int)scheduling->momentX);
printf("Already scheduled now %d\r\n", (int)timeX);
}
2017-06-03 20:36:34 -07:00
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
return false;
2016-09-04 22:03:25 -07:00
}
2015-07-10 06:01:56 -07:00
scheduling->momentX = timeX;
2019-11-23 15:38:16 -08:00
scheduling->action.setAction(callback, param);
2015-07-10 06:01:56 -07:00
scheduling->isScheduled = true;
if (head == NULL || timeX < head->momentX) {
2018-01-28 15:04:41 -08:00
// here we insert into head of the linked list
LL_PREPEND2(head, scheduling, nextScheduling_s);
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
assertListIsSorted();
2016-08-27 07:03:11 -07:00
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
return true;
} else {
2018-01-28 15:04:41 -08:00
// here we know we are not in the head of the list, let's find the position - linear search
2015-07-10 06:01:56 -07:00
scheduling_s *insertPosition = head;
while (insertPosition->nextScheduling_s != NULL && insertPosition->nextScheduling_s->momentX < timeX) {
insertPosition = insertPosition->nextScheduling_s;
2015-07-10 06:01:56 -07:00
}
scheduling->nextScheduling_s = insertPosition->nextScheduling_s;
insertPosition->nextScheduling_s = scheduling;
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
assertListIsSorted();
2016-08-27 07:03:11 -07:00
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
return false;
}
}
/**
* On this layer it does not matter which units are used - us, ms ot nt.
*
* This method is always invoked under a lock
* @return Get the timestamp of the soonest pending action, skipping all the actions in the past
*/
efitime_t EventQueue::getNextEventTime(efitime_t nowX) const {
2016-12-17 06:02:59 -08:00
2015-07-10 06:01:56 -07:00
if (head != NULL) {
if (head->momentX <= nowX) {
/**
* We are here if action timestamp is in the past
*
* looks like we end up here after 'writeconfig' (which freezes the firmware) - we are late
* for the next scheduled event
*/
efitime_t aBitInTheFuture = nowX + lateDelay;
return aBitInTheFuture;
} else {
return head->momentX;
}
}
return EMPTY_QUEUE;
}
static scheduling_s * longScheduling;
2017-05-21 07:25:35 -07:00
/**
* See also maxPrecisionCallbackDuration for total hw callback time
*/
uint32_t maxEventCallbackDuration = 0;
static uint32_t lastEventCallbackDuration;
2015-07-10 06:01:56 -07:00
/**
* Invoke all pending actions prior to specified timestamp
* @return number of executed actions
*/
int EventQueue::executeAll(efitime_t now) {
2019-10-11 17:43:21 -07:00
ScopePerf perf(PE::EventQueueExecuteAll);
2015-07-10 06:01:56 -07:00
scheduling_s * current, *tmp;
2019-09-22 05:22:35 -07:00
scheduling_s * executionList = nullptr;
scheduling_s * lastInExecutionList = nullptr;
2015-07-10 06:01:56 -07:00
int listIterationCounter = 0;
int executionCounter = 0;
// we need safe iteration because we are removing elements inside the loop
LL_FOREACH_SAFE2(head, current, tmp, nextScheduling_s)
2015-07-10 06:01:56 -07:00
{
if (++listIterationCounter > QUEUE_LENGTH_LIMIT) {
2017-04-19 19:03:14 -07:00
firmwareError(CUSTOM_LIST_LOOP, "Is this list looped?");
2015-07-10 06:01:56 -07:00
return false;
}
if (current->momentX <= now) {
executionCounter++;
2018-07-25 20:30:00 -07:00
efiAssert(CUSTOM_ERR_ASSERT, head == current, "removing from head", -1);
2015-07-10 06:01:56 -07:00
//LL_DELETE(head, current);
head = head->nextScheduling_s;
2016-09-03 19:01:51 -07:00
if (executionList == NULL) {
lastInExecutionList = executionList = current;
} else {
lastInExecutionList->nextScheduling_s = current;
2016-09-03 19:01:51 -07:00
lastInExecutionList = current;
}
current->nextScheduling_s = nullptr;
2015-07-10 06:01:56 -07:00
} else {
/**
* The list is sorted. Once we find one action in the future, all the remaining ones
* are also in the future.
*/
break;
}
}
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
2015-07-10 06:01:56 -07:00
assertListIsSorted();
#endif
/*
* we need safe iteration here because 'callback' might change change 'current->next'
* while re-inserting it into the queue from within the callback
*/
2019-11-23 15:38:16 -08:00
LL_FOREACH_SAFE2(executionList, current, tmp, nextScheduling_s) {
2019-05-07 16:32:08 -07:00
uint32_t before = getTimeNowLowerNt();
2015-07-10 06:01:56 -07:00
current->isScheduled = false;
2017-05-21 08:09:47 -07:00
uint32_t howFarOff = now - current->momentX;
maxSchedulingPrecisionLoss = maxI(maxSchedulingPrecisionLoss, howFarOff);
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
printf("QUEUE: execute current=%d param=%d\r\n", (long)current, (long)current->action.getArgument());
2016-09-23 22:02:43 -07:00
#endif
2019-10-13 13:14:08 -07:00
{
ScopePerf perf2(PE::EventQueueExecuteCallback);
2019-11-23 15:38:16 -08:00
current->action.execute();
2019-10-13 13:14:08 -07:00
}
2015-07-10 06:01:56 -07:00
// even with overflow it's safe to subtract here
2019-05-07 16:32:08 -07:00
lastEventCallbackDuration = getTimeNowLowerNt() - before;
2017-05-21 07:25:35 -07:00
if (lastEventCallbackDuration > maxEventCallbackDuration)
maxEventCallbackDuration = lastEventCallbackDuration;
if (lastEventCallbackDuration > 2000) {
2015-07-10 06:01:56 -07:00
longScheduling = current;
2017-05-21 07:25:35 -07:00
// what is this line about? lastEventCallbackDuration++;
2015-07-10 06:01:56 -07:00
}
}
return executionCounter;
}
int EventQueue::size(void) const {
2015-07-10 06:01:56 -07:00
scheduling_s *tmp;
int result;
LL_COUNT2(head, tmp, result, nextScheduling_s);
2015-07-10 06:01:56 -07:00
return result;
}
2019-04-12 19:07:03 -07:00
#if EFI_UNIT_TEST
void EventQueue::assertListIsSorted() const {
2015-07-10 06:01:56 -07:00
scheduling_s *current = head;
while (current != NULL && current->nextScheduling_s != NULL) {
efiAssertVoid(CUSTOM_ERR_6623, current->momentX <= current->nextScheduling_s->momentX, "list order");
current = current->nextScheduling_s;
2015-07-10 06:01:56 -07:00
}
}
#endif
void EventQueue::setLateDelay(int value) {
lateDelay = value;
}
scheduling_s * EventQueue::getHead() {
return head;
}
2019-12-02 21:20:47 -08:00
// todo: reduce code duplication with another 'getElementAtIndexForUnitText'
scheduling_s *EventQueue::getElementAtIndexForUnitText(int index) {
2015-07-10 06:01:56 -07:00
scheduling_s * current;
LL_FOREACH2(head, current, nextScheduling_s)
2015-07-10 06:01:56 -07:00
{
if (index == 0)
return current;
index--;
}
2018-12-08 16:15:24 -08:00
#if EFI_UNIT_TEST
firmwareError(OBD_PCM_Processor_Fault, "getForUnitText: null");
#endif /* EFI_UNIT_TEST */
2015-07-10 06:01:56 -07:00
return NULL;
}
void EventQueue::clear(void) {
2019-09-22 05:22:35 -07:00
head = nullptr;
2015-07-10 06:01:56 -07:00
}