rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
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 const &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 29 of file event_queue.cpp.

30 : m_lateDelay(lateDelay)
31{
32 for (size_t i = 0; i < efi::size(m_pool); i++) {
34 }
35
36#if EFI_PROD_CODE
38#endif
39}
const efidur_t m_lateDelay
Definition event_queue.h:49
void tryReturnScheduling(scheduling_s *sched)
scheduling_s m_pool[64]
Definition event_queue.h:52
TunerStudioOutputChannels * getTunerStudioOutputChannels()
Definition engine.cpp:581
Here is the call graph for this function:

Member Function Documentation

◆ assertListIsSorted()

void EventQueue::assertListIsSorted ( ) const
private

Definition at line 302 of file event_queue.cpp.

302 {
303#if EFI_UNIT_TEST || EFI_SIMULATOR
304 int counter = 0;
305 scheduling_s *current = m_head;
306 while (current != NULL && current->nextScheduling_s != NULL) {
307 efiAssertVoid(ObdCode::CUSTOM_ERR_6623, current->getMomentNt() <= current->nextScheduling_s->getMomentNt(), "list order");
308 current = current->nextScheduling_s;
309 if (counter++ > 1'000'000'000)
310 criticalError("EventQueue: looks like a loop?!");
311 }
312#endif // EFI_UNIT_TEST || EFI_SIMULATOR
313}
scheduling_s * m_head
Definition event_queue.h:48
@ CUSTOM_ERR_6623
scheduling_s * nextScheduling_s
Definition scheduler.h:294
efitick_t getMomentNt() const
Definition scheduler.h:270

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 334 of file event_queue.cpp.

334 {
335 // Flush the queue, resetting all scheduling_s as though we'd executed them
336 while(m_head) {
337 auto x = m_head;
338 // link next element to head
340
341 // Reset this element
342 x->setMomentNt(0);
343 x->nextScheduling_s = nullptr;
344 x->action = {};
345 }
346
347 m_head = nullptr;
348}
void setMomentNt(efitick_t p_moment)
Definition scheduler.h:284
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 207 of file event_queue.cpp.

207 {
209
210 int executionCounter = 0;
211
213
214 bool didExecute;
215 do {
216 didExecute = executeOne(now);
217 executionCounter += didExecute ? 1 : 0;
218 } while (didExecute);
219
220 return executionCounter;
221}
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 223 of file event_queue.cpp.

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

320 {
321 scheduling_s * current;
322
323 LL_FOREACH2(m_head, current, nextScheduling_s)
324 {
325 if (index == 0) {
326 return current;
327 }
328 index--;
329 }
330
331 return NULL;
332}

◆ getFreeScheduling()

scheduling_s * EventQueue::getFreeScheduling ( )

Definition at line 41 of file event_queue.cpp.

41 {
42 auto retVal = m_freelist;
43
44 if (retVal) {
46 retVal->nextScheduling_s = nullptr;
47
48#if EFI_PROD_CODE
50#endif
51 }
52
53 return retVal;
54}
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 315 of file event_queue.cpp.

315 {
316 return m_head;
317}

◆ 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 178 of file event_queue.cpp.

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

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 const &  action 
)

O(size) - linear search in sorted linked list

Returns
true if inserted into the head of the list

Definition at line 71 of file event_queue.cpp.

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

Referenced by SingleTimerExecutor::schedule().

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 125 of file event_queue.cpp.

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

295 {
296 scheduling_s *tmp;
297 int result;
298 LL_COUNT2(m_head, tmp, result, nextScheduling_s);
299 return result;
300}

◆ tryReturnScheduling()

void EventQueue::tryReturnScheduling ( scheduling_s sched)

Definition at line 56 of file event_queue.cpp.

56 {
57 // Only return this scheduling to the free list if it's from the correct pool
58 if (sched >= &m_pool[0] && sched <= &m_pool[efi::size(m_pool) - 1]) {
60 m_freelist = sched;
61
62#if EFI_PROD_CODE
64#endif
65 }
66}

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: