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
|
2020-01-13 18:57:43 -08:00
|
|
|
* @author Andrey Belomutskiy, (c) 2012-2020
|
2015-07-10 06:01:56 -07:00
|
|
|
*/
|
|
|
|
|
2018-09-16 19:26:57 -07:00
|
|
|
#include "global.h"
|
2019-07-03 18:48:04 -07:00
|
|
|
#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) {
|
2019-11-23 14:04:51 -08:00
|
|
|
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
|
|
|
|
*/
|
2020-01-06 21:41:18 -08:00
|
|
|
bool EventQueue::insertTask(scheduling_s *scheduling, efitime_t timeX, action_s action) {
|
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 */
|
2020-01-06 21:41:18 -08:00
|
|
|
efiAssert(CUSTOM_ERR_ASSERT, action.getCallback() != 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;
|
2020-01-06 21:41:18 -08:00
|
|
|
scheduling->action = action;
|
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
|
2019-11-23 14:04:51 -08:00
|
|
|
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;
|
2019-11-23 14:04:51 -08:00
|
|
|
while (insertPosition->nextScheduling_s != NULL && insertPosition->nextScheduling_s->momentX < timeX) {
|
|
|
|
insertPosition = insertPosition->nextScheduling_s;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2019-11-23 14:04:51 -08: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
|
|
|
|
*/
|
2019-06-08 06:51:36 -07:00
|
|
|
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) {
|
|
|
|
/**
|
2020-06-14 20:41:39 -07:00
|
|
|
* We are here if action timestamp is in the past. We should rarely be here since this 'getNextEventTime()' is
|
|
|
|
* always invoked by 'scheduleTimerCallback' which is always invoked right after 'executeAllPendingActions' - but still,
|
|
|
|
* for events which are really close to each other we would end up here.
|
2015-07-10 06:01:56 -07:00
|
|
|
*
|
|
|
|
* looks like we end up here after 'writeconfig' (which freezes the firmware) - we are late
|
|
|
|
* for the next scheduled event
|
2020-06-14 20:41:39 -07:00
|
|
|
*
|
2015-07-10 06:01:56 -07:00
|
|
|
*/
|
|
|
|
efitime_t aBitInTheFuture = nowX + lateDelay;
|
|
|
|
return aBitInTheFuture;
|
|
|
|
} else {
|
|
|
|
return head->momentX;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return EMPTY_QUEUE;
|
|
|
|
}
|
|
|
|
|
2017-05-21 07:25:35 -07:00
|
|
|
/**
|
|
|
|
* See also maxPrecisionCallbackDuration for total hw callback time
|
|
|
|
*/
|
|
|
|
uint32_t maxEventCallbackDuration = 0;
|
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);
|
|
|
|
|
2019-12-14 15:00:39 -08:00
|
|
|
int executionCounter = 0;
|
2019-10-11 17:43:21 -07:00
|
|
|
|
2019-12-14 15:00:39 -08:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
assertListIsSorted();
|
|
|
|
#endif
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-12-14 15:00:39 -08:00
|
|
|
while (true) {
|
|
|
|
// Read the head every time - a previously executed event could
|
|
|
|
// have inserted something new at the head
|
|
|
|
scheduling_s* current = head;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2019-12-14 15:00:39 -08:00
|
|
|
// Queue is empty - bail
|
|
|
|
if (!current) {
|
|
|
|
break;
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
2019-12-14 15:00:39 -08:00
|
|
|
|
|
|
|
// Only execute events that occured in the past.
|
|
|
|
// The list is sorted, so as soon as we see an event
|
|
|
|
// in the future, we're done.
|
|
|
|
if (current->momentX > now) {
|
2015-07-10 06:01:56 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-12-14 15:00:39 -08:00
|
|
|
executionCounter++;
|
|
|
|
|
|
|
|
// step the head forward, unlink this element, clear scheduled flag
|
|
|
|
head = current->nextScheduling_s;
|
|
|
|
current->nextScheduling_s = nullptr;
|
2015-07-10 06:01:56 -07:00
|
|
|
current->isScheduled = false;
|
2019-12-14 15:00:39 -08:00
|
|
|
|
2019-04-12 19:07:03 -07:00
|
|
|
#if EFI_UNIT_TEST
|
2019-11-23 17:36:40 -08:00
|
|
|
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
|
|
|
|
2019-12-14 15:00:39 -08:00
|
|
|
// Execute the current element
|
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
|
|
|
}
|
|
|
|
|
2019-12-14 15:00:39 -08:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
// (tests only) Ensure we didn't break anything
|
|
|
|
assertListIsSorted();
|
|
|
|
#endif
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
2019-12-14 15:00:39 -08:00
|
|
|
|
2015-07-10 06:01:56 -07:00
|
|
|
return executionCounter;
|
|
|
|
}
|
|
|
|
|
2019-06-08 06:51:36 -07:00
|
|
|
int EventQueue::size(void) const {
|
2015-07-10 06:01:56 -07:00
|
|
|
scheduling_s *tmp;
|
|
|
|
int result;
|
2019-11-23 14:04:51 -08:00
|
|
|
LL_COUNT2(head, tmp, result, nextScheduling_s);
|
2015-07-10 06:01:56 -07:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-06-08 06:51:36 -07:00
|
|
|
void EventQueue::assertListIsSorted() const {
|
2015-07-10 06:01:56 -07:00
|
|
|
scheduling_s *current = head;
|
2019-11-23 14:04:51 -08:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-11-23 14:04:51 -08:00
|
|
|
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
|
|
|
}
|