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

209 lines
4.9 KiB
C++
Raw Normal View History

2014-08-29 07:52:33 -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
2015-01-12 15:04:10 -08:00
* @author Andrey Belomutskiy, (c) 2012-2015
2014-08-29 07:52:33 -07:00
*/
2014-11-18 18:05:41 -08:00
#include "main.h"
2014-08-29 07:52:33 -07:00
#include "event_queue.h"
#include "efitime.h"
2014-11-25 13:03:16 -08:00
#include "efilib2.h"
2014-08-29 07:52:33 -07:00
2015-04-29 08:05:04 -07:00
int maxHowFarOff = 0;
2015-04-17 18:04:22 -07:00
scheduling_s::scheduling_s() {
callback = NULL;
next = NULL;
2015-04-28 12:04:40 -07:00
param = NULL;
2015-04-17 18:04:22 -07:00
isScheduled = false;
}
2014-08-29 07:52:33 -07:00
EventQueue::EventQueue() {
head = NULL;
2014-11-07 22:05:46 -08:00
setLateDelay(100);
2014-08-29 07:52:33 -07:00
}
bool EventQueue::checkIfPending(scheduling_s *scheduling) {
return assertNotInList<scheduling_s>(head, scheduling);
}
2015-04-17 16:08:49 -07:00
/**
* @return true if inserted into the head of the list
*/
bool_t EventQueue::insertTask(scheduling_s *scheduling, uint64_t timeX, schfunc_t callback, void *param) {
2015-04-17 15:10:46 -07:00
#if EFI_UNIT_TEST
assertListIsSorted();
#endif
2015-04-17 16:08:49 -07:00
efiAssert(callback != NULL, "NULL callback", false);
2014-09-13 05:02:58 -07:00
2015-04-17 18:04:22 -07:00
if (scheduling->isScheduled)
2015-04-17 16:08:49 -07:00
return false;
2014-08-29 07:52:33 -07:00
2014-11-07 22:05:46 -08:00
scheduling->momentX = timeX;
2014-08-29 07:52:33 -07:00
scheduling->callback = callback;
scheduling->param = param;
2015-04-17 18:04:22 -07:00
scheduling->isScheduled = true;
2014-08-29 07:52:33 -07:00
2015-04-17 15:10:46 -07:00
if (head == NULL || timeX < head->momentX) {
LL_PREPEND(head, scheduling);
2015-04-17 16:08:49 -07:00
#if EFI_UNIT_TEST
assertListIsSorted();
#endif
return true;
2015-04-17 15:10:46 -07:00
} else {
scheduling_s *insertPosition = head;
while (insertPosition->next != NULL && insertPosition->next->momentX < timeX) {
insertPosition = insertPosition->next;
}
scheduling->next = insertPosition->next;
insertPosition->next = scheduling;
#if EFI_UNIT_TEST
2015-04-17 16:08:49 -07:00
assertListIsSorted();
2015-04-17 15:10:46 -07:00
#endif
2015-04-17 16:08:49 -07:00
return false;
}
2014-08-29 07:52:33 -07:00
}
/**
2014-11-07 22:05:46 -08:00
* On this layer it does not matter which units are used - us, ms ot nt.
2015-04-17 16:08:49 -07:00
* @return Get the timestamp of the soonest pending action, skipping all the actions in the past
2014-08-29 07:52:33 -07:00
*/
2014-11-07 22:05:46 -08:00
uint64_t EventQueue::getNextEventTime(uint64_t nowX) {
2014-08-29 07:52:33 -07:00
scheduling_s * current;
2014-11-07 22:05:46 -08:00
uint64_t nextTimeUs = EMPTY_QUEUE;
2014-08-29 07:52:33 -07:00
int counter = 0;
LL_FOREACH(head, current)
{
if (++counter > QUEUE_LENGTH_LIMIT) {
firmwareError("Is this list looped #2?");
return EMPTY_QUEUE;
}
2014-11-07 22:05:46 -08:00
if (current->momentX <= nowX) {
2014-09-13 18:02:52 -07:00
/**
2015-04-17 16:08:49 -07:00
* We are here if action timestamp is in the past
*
2014-09-13 18:02:52 -07:00
* looks like we end up here after 'writeconfig' (which freezes the firmware) - we are late
* for the next scheduled event
*/
2015-04-17 16:08:49 -07:00
uint64_t aBitInTheFuture = nowX + lateDelay;
return aBitInTheFuture;
2014-09-13 18:02:52 -07:00
} else {
2015-04-17 16:08:49 -07:00
return current->momentX;
2014-09-13 14:02:50 -07:00
}
2014-08-29 07:52:33 -07:00
}
2015-04-17 16:08:49 -07:00
return EMPTY_QUEUE;
2014-08-29 07:52:33 -07:00
}
2014-11-18 18:05:41 -08:00
// static scheduling_s * longScheduling;
2014-11-25 13:03:16 -08:00
uint32_t maxEventQueueTime = 0;
uint32_t lastEventQueueTime;
2014-11-18 18:05:41 -08:00
2014-08-29 07:52:33 -07:00
/**
* Invoke all pending actions prior to specified timestamp
2015-04-16 16:04:30 -07:00
* @return number of executed actions
2014-08-29 07:52:33 -07:00
*/
2015-04-16 16:04:30 -07:00
int EventQueue::executeAll(uint64_t now) {
2014-08-29 07:52:33 -07:00
scheduling_s * current, *tmp;
scheduling_s * executionList = NULL;
2015-04-16 09:05:50 -07:00
int listIterationCounter = 0;
int executionCounter = 0;
2014-08-29 07:52:33 -07:00
// we need safe iteration because we are removing elements inside the loop
LL_FOREACH_SAFE(head, current, tmp)
{
2015-04-16 09:05:50 -07:00
if (++listIterationCounter > QUEUE_LENGTH_LIMIT) {
2014-08-29 07:52:33 -07:00
firmwareError("Is this list looped?");
2014-09-14 14:03:05 -07:00
return false;
2014-08-29 07:52:33 -07:00
}
2014-11-07 22:05:46 -08:00
if (current->momentX <= now) {
2015-04-16 09:05:50 -07:00
executionCounter++;
2015-04-28 12:04:40 -07:00
efiAssert(head == current, "removing from head", -1);
//LL_DELETE(head, current);
head = head->next;
2014-08-29 07:52:33 -07:00
LL_PREPEND(executionList, current);
2015-04-17 16:08:49 -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;
2014-08-29 07:52:33 -07:00
}
}
2015-04-17 15:10:46 -07:00
#if EFI_UNIT_TEST
assertListIsSorted();
#endif
2014-08-29 07:52:33 -07:00
/*
* we need safe iteration here because 'callback' might change change 'current->next'
* while re-inserting it into the queue from within the callback
*/
LL_FOREACH_SAFE(executionList, current, tmp)
2014-09-14 14:03:05 -07:00
{
2014-11-25 13:03:16 -08:00
uint32_t before = GET_TIMESTAMP();
2015-04-17 18:04:22 -07:00
current->isScheduled = false;
2015-04-29 08:05:04 -07:00
int howFarOff = now - current->momentX;
maxHowFarOff = maxI(maxHowFarOff, howFarOff);
2014-08-29 07:52:33 -07:00
current->callback(current->param);
2015-04-28 12:04:40 -07:00
// even with overflow it's safe to subtract here
2014-11-25 13:03:16 -08:00
lastEventQueueTime = GET_TIMESTAMP() - before;
if (lastEventQueueTime > maxEventQueueTime)
maxEventQueueTime = lastEventQueueTime;
2014-11-18 18:05:41 -08:00
// if (cost > 2000) {
// longScheduling = current;
// cost++;
// }
2014-09-14 14:03:05 -07:00
}
2015-04-16 16:04:30 -07:00
return executionCounter;
2014-08-29 07:52:33 -07:00
}
int EventQueue::size(void) {
scheduling_s *tmp;
int result;
LL_COUNT(head, tmp, result);
return result;
}
2015-04-17 15:10:46 -07:00
#if EFI_UNIT_TEST
void EventQueue::assertListIsSorted() {
scheduling_s *current = head;
while (current != NULL && current->next != NULL) {
efiAssertVoid(current->momentX <= current->next->momentX, "list order");
current = current->next;
}
}
#endif
2014-11-07 22:05:46 -08:00
void EventQueue::setLateDelay(int value) {
lateDelay = value;
}
2015-04-17 08:04:27 -07:00
scheduling_s * EventQueue::getHead() {
return head;
}
2014-08-29 07:52:33 -07:00
scheduling_s *EventQueue::getForUnitText(int index) {
scheduling_s * current;
LL_FOREACH(head, current)
{
if (index == 0)
return current;
index--;
}
return NULL;
}
void EventQueue::clear(void) {
head = NULL;
}