rusEFI
The most advanced open source ECU
Public Member Functions | Private Member Functions | Private Attributes
EventQueue Class Reference

#include <event_queue.h>

Collaboration diagram for EventQueue:
Collaboration graph
[legend]

Public Member Functions

 EventQueue (efidur_t lateDelay=0)
 
bool insertTask (scheduling_s *scheduling, efitick_t timeX, action_s action)
 
void remove (scheduling_s *scheduling)
 
int executeAll (efitick_t now)
 
bool executeOne (efitick_t now)
 
expected< efitick_t > getNextEventTime (efitick_t nowUs) const
 
void clear ()
 
int size () const
 
scheduling_sgetElementAtIndexForUnitText (int index)
 
scheduling_sgetHead ()
 
scheduling_sgetFreeScheduling ()
 
void tryReturnScheduling (scheduling_s *sched)
 

Private Member Functions

void assertListIsSorted () const
 

Private Attributes

scheduling_sm_head = nullptr
 
const efidur_t m_lateDelay
 
scheduling_sm_freelist = nullptr
 
scheduling_s m_pool [64]
 

Detailed Description

Execution sorted linked list

Definition at line 19 of file event_queue.h.

Constructor & Destructor Documentation

◆ EventQueue()

EventQueue::EventQueue ( efidur_t  lateDelay = 0)
explicit

Definition at line 23 of file event_queue.cpp.

24  : m_lateDelay(lateDelay)
25 {
26  for (size_t i = 0; i < efi::size(m_pool); i++) {
28  }
29 
30 #if EFI_PROD_CODE
32 #endif
33 }
const efidur_t m_lateDelay
Definition: event_queue.h:49
void tryReturnScheduling(scheduling_s *sched)
Definition: event_queue.cpp:50
scheduling_s m_pool[64]
Definition: event_queue.h:52
composite packet size
TunerStudioOutputChannels * getTunerStudioOutputChannels()
Definition: engine.cpp:600
Here is the call graph for this function:

Member Function Documentation

◆ assertListIsSorted()

void EventQueue::assertListIsSorted ( ) const
private

Definition at line 290 of file event_queue.cpp.

290  {
291 #if EFI_UNIT_TEST || EFI_SIMULATOR
292  int counter = 0;
293  scheduling_s *current = m_head;
294  while (current != NULL && current->nextScheduling_s != NULL) {
295  efiAssertVoid(ObdCode::CUSTOM_ERR_6623, current->getMomentNt() <= current->nextScheduling_s->getMomentNt(), "list order");
296  current = current->nextScheduling_s;
297  if (counter++ > 1'000'000'000)
298  criticalError("EventQueue: looks like a loop?!");
299  }
300 #endif // EFI_UNIT_TEST || EFI_SIMULATOR
301 }
scheduling_s * m_head
Definition: event_queue.h:48
@ CUSTOM_ERR_6623
scheduling_s * nextScheduling_s
Definition: scheduler.h:82
efitick_t getMomentNt() const
Definition: scheduler.h:64

Referenced by executeAll(), executeOne(), insertTask(), and remove().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ clear()

void EventQueue::clear ( )

Definition at line 321 of file event_queue.cpp.

321  {
322  // Flush the queue, resetting all scheduling_s as though we'd executed them
323  while(m_head) {
324  auto x = m_head;
325  // link next element to head
327 
328  // Reset this element
329  x->setMomentNt(0);
330  x->nextScheduling_s = nullptr;
331  x->action = {};
332  }
333 
334  m_head = nullptr;
335 }
void setMomentNt(efitick_t p_moment)
Definition: scheduler.h:72
Here is the call graph for this function:

◆ executeAll()

int EventQueue::executeAll ( efitick_t  now)

Invoke all pending actions prior to specified timestamp

Returns
number of executed actions

Definition at line 201 of file event_queue.cpp.

201  {
203 
204  int executionCounter = 0;
205 
207 
208  bool didExecute;
209  do {
210  didExecute = executeOne(now);
211  executionCounter += didExecute ? 1 : 0;
212  } while (didExecute);
213 
214  return executionCounter;
215 }
void assertListIsSorted() const
bool executeOne(efitick_t now)
@ EventQueueExecuteAll
Here is the call graph for this function:

◆ executeOne()

bool EventQueue::executeOne ( efitick_t  now)

Definition at line 217 of file event_queue.cpp.

217  {
218  // Read the head every time - a previously executed event could
219  // have inserted something new at the head
220  scheduling_s* current = m_head;
221 
222  // Queue is empty - bail
223  if (!current) {
224  return false;
225  }
226 
227  // If the next event is far in the future, we'll reschedule
228  // and execute it next time.
229  // We do this when the next event is close enough that the overhead of
230  // resetting the timer and scheduling an new interrupt is greater than just
231  // waiting for the time to arrive. On current CPUs, this is reasonable to set
232  // around 10 microseconds.
233  if (current->getMomentNt() > now + m_lateDelay) {
234  return false;
235  }
236 
237 #if EFI_UNIT_TEST
238 // efitick_t spinDuration = current->getMomentNt() - getTimeNowNt();
239 // if (spinDuration > 0) {
240 // throw std::runtime_error("Time Spin in unit test");
241 // }
242 #endif
243 
244  // near future - spin wait for the event to happen and avoid the
245  // overhead of rescheduling the timer.
246  // yes, that's a busy wait but that's what we need here
247  while (current->getMomentNt() > getTimeNowNt()) {
248 #if EFI_UNIT_TEST
249  // todo: remove this hack see https://github.com/rusefi/rusefi/issues/6457
250 extern bool unitTestBusyWaitHack;
251  if (unitTestBusyWaitHack) {
252  break;
253  }
254 #endif
255  UNIT_TEST_BUSY_WAIT_CALLBACK();
256  }
257 
258  // step the head forward, unlink this element, clear scheduled flag
259  m_head = current->nextScheduling_s;
260  current->nextScheduling_s = nullptr;
261 
262  // Grab the action but clear it in the event so we can reschedule from the action's execution
263  auto action = current->action;
264  current->action = {};
265 
266  tryReturnScheduling(current);
267  current = nullptr;
268 
269 #if EFI_DEFAILED_LOGGING
270  printf("QUEUE: execute current=%d param=%d\r\n", (uintptr_t)current, (uintptr_t)action.getArgument());
271 #endif
272 
273  // Execute the current element
274  {
276  action.execute();
277  }
278 
280  return true;
281 }
efitick_t getTimeNowNt()
Definition: efitime.cpp:19
@ EventQueueExecuteCallback
action_s action
Definition: scheduler.h:84
printf("\n")

Referenced by executeAll(), and SingleTimerExecutor::executeAllPendingActions().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getElementAtIndexForUnitText()

scheduling_s * EventQueue::getElementAtIndexForUnitText ( int  index)

Definition at line 308 of file event_queue.cpp.

308  {
309  scheduling_s * current;
310 
311  LL_FOREACH2(m_head, current, nextScheduling_s)
312  {
313  if (index == 0)
314  return current;
315  index--;
316  }
317 
318  return NULL;
319 }

◆ getFreeScheduling()

scheduling_s * EventQueue::getFreeScheduling ( )

Definition at line 35 of file event_queue.cpp.

35  {
36  auto retVal = m_freelist;
37 
38  if (retVal) {
39  m_freelist = retVal->nextScheduling_s;
40  retVal->nextScheduling_s = nullptr;
41 
42 #if EFI_PROD_CODE
44 #endif
45  }
46 
47  return retVal;
48 }
scheduling_s * m_freelist
Definition: event_queue.h:51

Referenced by insertTask().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getHead()

scheduling_s * EventQueue::getHead ( )

Definition at line 303 of file event_queue.cpp.

303  {
304  return m_head;
305 }

◆ getNextEventTime()

expected< efitick_t > EventQueue::getNextEventTime ( efitick_t  nowNt) const

On this layer it does not matter which units are used - us, ms ot nt.

This method is always invoked under a lock

Returns
Get the timestamp of the soonest pending action, skipping all the actions in the past

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.

looks like we end up here after 'writeconfig' (which freezes the firmware) - we are late for the next scheduled event

Definition at line 172 of file event_queue.cpp.

172  {
173  if (m_head) {
174  if (m_head->getMomentNt() <= nowNt) {
175  /**
176  * We are here if action timestamp is in the past. We should rarely be here since this 'getNextEventTime()' is
177  * always invoked by 'scheduleTimerCallback' which is always invoked right after 'executeAllPendingActions' - but still,
178  * for events which are really close to each other we would end up here.
179  *
180  * looks like we end up here after 'writeconfig' (which freezes the firmware) - we are late
181  * for the next scheduled event
182  */
183  return nowNt + m_lateDelay;
184  } else {
185  return m_head->getMomentNt();
186  }
187  }
188 
189  return unexpected;
190 }

Referenced by SingleTimerExecutor::scheduleTimerCallback().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ insertTask()

bool EventQueue::insertTask ( scheduling_s scheduling,
efitick_t  timeNt,
action_s  action 
)

O(size) - linear search in sorted linked list

Returns
true if inserted into the head of the list

Definition at line 65 of file event_queue.cpp.

65  {
67 
68  if (!scheduling) {
69  scheduling = getFreeScheduling();
70 
71  // If still null, the free list is empty and all schedulings in the pool have been expended.
72  if (!scheduling) {
73  // TODO: should we warn or error here?
74 // todo: look into why units tests fail here
75 #if EFI_PROD_CODE
76  criticalError("No slots in scheduling pool");
77 #endif
78  return false;
79  }
80  }
81 
83  efiAssert(ObdCode::CUSTOM_ERR_ASSERT, action.getCallback() != NULL, "NULL callback", false);
84 
85 // please note that simulator does not use this code at all - simulator uses signal_executor_sleep
86 
87  if (scheduling->action) {
88 #if EFI_UNIT_TEST
89  if (verboseMode) {
90  printf("Already scheduled was %d\r\n", (int)scheduling->getMomentNt());
91  printf("Already scheduled now %d\r\n", (int)timeNt);
92  }
93 #endif /* EFI_UNIT_TEST */
94  return false;
95  }
96 
97  scheduling->setMomentNt(timeNt);
98  scheduling->action = action;
99 
100  if (!m_head || timeNt < m_head->getMomentNt()) {
101  // here we insert into head of the linked list
102  LL_PREPEND2(m_head, scheduling, nextScheduling_s);
104  return true;
105  } else {
106  // here we know we are not in the head of the list, let's find the position - linear search
107  scheduling_s *insertPosition = m_head;
108  while (insertPosition->nextScheduling_s != NULL && insertPosition->nextScheduling_s->getMomentNt() < timeNt) {
109  insertPosition = insertPosition->nextScheduling_s;
110  }
111 
112  scheduling->nextScheduling_s = insertPosition->nextScheduling_s;
113  insertPosition->nextScheduling_s = scheduling;
115  return false;
116  }
117 }
scheduling_s * getFreeScheduling()
Definition: event_queue.cpp:35
schfunc_t getCallback() const
Definition: scheduler.cpp:15
static WrapAround62 timeNt
Definition: efitime.cpp:14
bool verboseMode
@ CUSTOM_ERR_ASSERT
@ EventQueueInsertTask

Referenced by SingleTimerExecutor::scheduleByTimestampNt().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ remove()

void EventQueue::remove ( scheduling_s scheduling)

Definition at line 119 of file event_queue.cpp.

119  {
121 
122  // Special case: event isn't scheduled, so don't cancel it
123  if (!scheduling->action) {
124  return;
125  }
126 
127  // Special case: empty list, nothing to do
128  if (!m_head) {
129  return;
130  }
131 
132  // Special case: is the item to remove at the head?
133  if (scheduling == m_head) {
135  scheduling->nextScheduling_s = nullptr;
136  scheduling->action = {};
137  } else {
138  auto prev = m_head; // keep track of the element before the one to remove, so we can link around it
139  auto current = prev->nextScheduling_s;
140 
141  // Find our element
142  while (current && current != scheduling) {
143  prev = current;
144  current = current->nextScheduling_s;
145  }
146 
147  // Walked off the end, this is an error since this *should* have been scheduled
148  if (!current) {
149  firmwareError(ObdCode::OBD_PCM_Processor_Fault, "EventQueue::remove didn't find element");
150  return;
151  }
152 
153  efiAssertVoid(ObdCode::OBD_PCM_Processor_Fault, current == scheduling, "current not equal to scheduling");
154 
155  // Link around the removed item
156  prev->nextScheduling_s = current->nextScheduling_s;
157 
158  // Clean the item to remove
159  current->nextScheduling_s = nullptr;
160  current->action = {};
161  }
162 
164 }
void firmwareError(ObdCode code, const char *fmt,...)
@ OBD_PCM_Processor_Fault

Referenced by SingleTimerExecutor::cancel().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ size()

int EventQueue::size ( ) const

Definition at line 283 of file event_queue.cpp.

283  {
284  scheduling_s *tmp;
285  int result;
286  LL_COUNT2(m_head, tmp, result, nextScheduling_s);
287  return result;
288 }

◆ tryReturnScheduling()

void EventQueue::tryReturnScheduling ( scheduling_s sched)

Definition at line 50 of file event_queue.cpp.

50  {
51  // Only return this scheduling to the free list if it's from the correct pool
52  if (sched >= &m_pool[0] && sched <= &m_pool[efi::size(m_pool) - 1]) {
54  m_freelist = sched;
55 
56 #if EFI_PROD_CODE
58 #endif
59  }
60 }

Referenced by EventQueue(), and executeOne().

Here is the call graph for this function:
Here is the caller graph for this function:

Field Documentation

◆ m_freelist

scheduling_s* EventQueue::m_freelist = nullptr
private

Definition at line 51 of file event_queue.h.

Referenced by getFreeScheduling(), and tryReturnScheduling().

◆ m_head

scheduling_s* EventQueue::m_head = nullptr
private

◆ m_lateDelay

const efidur_t EventQueue::m_lateDelay
private

Definition at line 49 of file event_queue.h.

Referenced by executeOne(), and getNextEventTime().

◆ m_pool

scheduling_s EventQueue::m_pool[64]
private

Definition at line 52 of file event_queue.h.

Referenced by EventQueue(), and tryReturnScheduling().


The documentation for this class was generated from the following files: