rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
single_timer_executor.cpp
Go to the documentation of this file.
1/**
2 * @file SingleTimerExecutor.cpp
3 *
4 * This class combines the powers of a 1MHz hardware timer from microsecond_timer.cpp
5 * and pending events queue event_queue.cpp
6 *
7 * As of version 2.6.x, ChibiOS tick-based kernel is not capable of scheduling events
8 * with the level of precision we need, and realistically it should not.
9 *
10 * Update: actually newer ChibiOS has tickless mode and what we have here is pretty much the same thing :)
11 * open question if rusEfi should simply migrate to ChibiOS tickless scheduling (which would increase coupling with ChibiOS)
12 *
13 * See https://rusefi.com/forum/viewtopic.php?f=5&t=373&start=360#p30895
14 * for some performance data: with 'debug' firmware we spend about 5% of CPU in TIM5 handler which seem to be executed
15 * about 1500 times a second
16 *
17 * http://sourceforge.net/p/rusefi/tickets/24/
18 *
19 * @date: Apr 18, 2014
20 * @author Andrey Belomutskiy, (c) 2012-2020
21 */
22
23#include "pch.h"
24
25
27#include "efitime.h"
28
29#if EFI_SIGNAL_EXECUTOR_ONE_TIMER
30
31#include "microsecond_timer.h"
32#include "os_util.h"
33
35
37 efiAssertVoid(ObdCode::CUSTOM_ERR_6624, hasLotsOfRemainingStack(), "lowstck#2y");
38
40}
41
43 // 8us is roughly the cost of the interrupt + overhead of a single timer event
44 : queue(US2NT(8))
45{
46}
47
48void SingleTimerExecutor::schedule(const char *msg, scheduling_s* scheduling, efitick_t nt, action_s const& action) {
50
51#if EFI_ENABLE_ASSERTS
52 efidur_t deltaTimeNt = nt - getTimeNowNt();
53
54 if (deltaTimeNt >= TOO_FAR_INTO_FUTURE_NT) {
55 // we are trying to set callback for too far into the future. This does not look right at all
56 int32_t intDeltaTimeNt = (int32_t)deltaTimeNt;
57 firmwareError(ObdCode::RUNTIME_CRITICAL_TASK_TIMER_OVERFLOW, "schedule() too far: %ld %s", intDeltaTimeNt, msg);
58 return;
59 }
60#endif
61
63
64 // Lock for queue insertion - we may already be locked, but that's ok
65 chibios_rt::CriticalSectionLocker csl;
66
67 bool needToResetTimer = queue.insertTask(scheduling, nt, action);
68 if (!reentrantFlag) {
70 if (needToResetTimer) {
72 }
73 }
74}
75
77 // Lock for queue removal - we may already be locked, but that's ok
78 chibios_rt::CriticalSectionLocker csl;
79
80 queue.remove(scheduling);
81}
82
85
86 chibios_rt::CriticalSectionLocker csl;
87
90}
91
92/*
93 * this private method is executed under lock
94 */
97
99 /**
100 * Let's execute actions we should execute at this point.
101 * reentrantFlag takes care of the use case where the actions we are executing are scheduling
102 * further invocations
103 */
104 reentrantFlag = true;
105
106 /**
107 * in real life it could be that while we executing listeners time passes and it's already time to execute
108 * next listeners.
109 * TODO: add a counter & figure out a limit of iterations?
110 */
111
112 // starts at -1 because do..while will run a minimum of once
113 executeCounter = -1;
114
115 bool didExecute;
116 do {
117 efitick_t nowNt = getTimeNowNt();
118 didExecute = queue.executeOne(nowNt);
119
120 // if we're stuck in a loop executing lots of events, panic!
121 if (executeCounter++ == 500) {
122 firmwareError(ObdCode::CUSTOM_ERR_LOCK_ISSUE, "Maximum scheduling run length exceeded - CPU load too high");
123 }
124
125 } while (didExecute);
126
128
129 if (!isLocked()) {
130 firmwareError(ObdCode::CUSTOM_ERR_LOCK_ISSUE, "Someone has stolen my lock");
131 return;
132 }
133 reentrantFlag = false;
134}
135
136/**
137 * This method is always invoked under a lock
138 */
141
142 /**
143 * Let's grab fresh time value
144 */
145 efitick_t nowNt = getTimeNowNt();
146 expected<efitick_t> nextEventTimeNt = queue.getNextEventTime(nowNt);
147
148 if (!nextEventTimeNt) {
149 return; // no pending events in the queue
150 }
151
152 efiAssertVoid(ObdCode::CUSTOM_ERR_6625, nextEventTimeNt.Value > nowNt, "setTimer constraint");
153
154 setHardwareSchedulerTimer(nowNt, nextEventTimeNt.Value);
155}
156
160
172
173#endif /* EFI_SIGNAL_EXECUTOR_ONE_TIMER */
174
SingleTimerExecutor scheduler
Definition engine.h:271
TunerStudioOutputChannels outputChannels
Definition engine.h:109
bool insertTask(scheduling_s *scheduling, efitick_t timeX, action_s const &action)
expected< efitick_t > getNextEventTime(efitick_t nowUs) const
bool executeOne(efitick_t now)
void remove(scheduling_s *scheduling)
void schedule(const char *msg, scheduling_s *scheduling, efitick_t timeNt, action_s const &action) override
Schedule an action to be executed in the future.
void cancel(scheduling_s *scheduling) override
Cancel the specified scheduling_s so that, if currently scheduled, it does not execute.
static ExtiQueue< ExtiQueueEntry, 32 > queue
efitick_t getTimeNowNt()
Definition efitime.cpp:19
static EngineAccessor engine
Definition engine.h:413
Engine ___engine
static constexpr engine_configuration_s * engineConfiguration
void firmwareError(ObdCode code, const char *fmt,...)
static bool isLocked
Definition flash_int.cpp:22
void setHardwareSchedulerTimer(efitick_t nowNt, efitick_t setTimeNt)
void initMicrosecondTimer()
@ CUSTOM_ERR_6624
@ CUSTOM_ERR_LOCK_ISSUE
@ CUSTOM_ERR_6625
@ RUNTIME_CRITICAL_TASK_TIMER_OVERFLOW
@ SingleTimerExecutorDoExecute
@ SingleTimerExecutorScheduleByTimestamp
@ SingleTimerExecutorScheduleTimerCallback
efitick_t efidur_t
void globalTimerCallback()
void initSingleTimerExecutorHardware()
void executorStatistics()
uint32_t hwSetTimerDuration