diff options
Diffstat (limited to 'quantum/deferred_exec.c')
-rw-r--r-- | quantum/deferred_exec.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/quantum/deferred_exec.c b/quantum/deferred_exec.c new file mode 100644 index 000000000..5b0a5b142 --- /dev/null +++ b/quantum/deferred_exec.c | |||
@@ -0,0 +1,152 @@ | |||
1 | // Copyright 2021 Nick Brassel (@tzarc) | ||
2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
3 | |||
4 | #include <stddef.h> | ||
5 | #include <timer.h> | ||
6 | #include <deferred_exec.h> | ||
7 | |||
8 | #ifndef MAX_DEFERRED_EXECUTORS | ||
9 | # define MAX_DEFERRED_EXECUTORS 8 | ||
10 | #endif | ||
11 | |||
12 | typedef struct deferred_executor_t { | ||
13 | deferred_token token; | ||
14 | uint32_t trigger_time; | ||
15 | deferred_exec_callback callback; | ||
16 | void * cb_arg; | ||
17 | } deferred_executor_t; | ||
18 | |||
19 | static deferred_token current_token = 0; | ||
20 | static uint32_t last_deferred_exec_check = 0; | ||
21 | static deferred_executor_t executors[MAX_DEFERRED_EXECUTORS] = {0}; | ||
22 | |||
23 | static inline bool token_can_be_used(deferred_token token) { | ||
24 | if (token == INVALID_DEFERRED_TOKEN) { | ||
25 | return false; | ||
26 | } | ||
27 | for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) { | ||
28 | if (executors[i].token == token) { | ||
29 | return false; | ||
30 | } | ||
31 | } | ||
32 | return true; | ||
33 | } | ||
34 | |||
35 | static inline deferred_token allocate_token(void) { | ||
36 | deferred_token first = ++current_token; | ||
37 | while (!token_can_be_used(current_token)) { | ||
38 | ++current_token; | ||
39 | if (current_token == first) { | ||
40 | // If we've looped back around to the first, everything is already allocated (yikes!). Need to exit with a failure. | ||
41 | return INVALID_DEFERRED_TOKEN; | ||
42 | } | ||
43 | } | ||
44 | return current_token; | ||
45 | } | ||
46 | |||
47 | deferred_token defer_exec(uint32_t delay_ms, deferred_exec_callback callback, void *cb_arg) { | ||
48 | // Ignore queueing if it's a zero-time delay, or invalid callback | ||
49 | if (delay_ms == 0 || !callback) { | ||
50 | return INVALID_DEFERRED_TOKEN; | ||
51 | } | ||
52 | |||
53 | // Find an unused slot and claim it | ||
54 | for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) { | ||
55 | deferred_executor_t *entry = &executors[i]; | ||
56 | if (entry->token == INVALID_DEFERRED_TOKEN) { | ||
57 | // Work out the new token value, dropping out if none were available | ||
58 | deferred_token token = allocate_token(); | ||
59 | if (token == INVALID_DEFERRED_TOKEN) { | ||
60 | return false; | ||
61 | } | ||
62 | |||
63 | // Set up the executor table entry | ||
64 | entry->token = current_token; | ||
65 | entry->trigger_time = timer_read32() + delay_ms; | ||
66 | entry->callback = callback; | ||
67 | entry->cb_arg = cb_arg; | ||
68 | return current_token; | ||
69 | } | ||
70 | } | ||
71 | |||
72 | // None available | ||
73 | return INVALID_DEFERRED_TOKEN; | ||
74 | } | ||
75 | |||
76 | bool extend_deferred_exec(deferred_token token, uint32_t delay_ms) { | ||
77 | // Ignore queueing if it's a zero-time delay, or the token is not valid | ||
78 | if (delay_ms == 0 || token == INVALID_DEFERRED_TOKEN) { | ||
79 | return false; | ||
80 | } | ||
81 | |||
82 | // Find the entry corresponding to the token | ||
83 | for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) { | ||
84 | deferred_executor_t *entry = &executors[i]; | ||
85 | if (entry->token == token) { | ||
86 | // Found it, extend the delay | ||
87 | entry->trigger_time = timer_read32() + delay_ms; | ||
88 | return true; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | // Not found | ||
93 | return false; | ||
94 | } | ||
95 | |||
96 | bool cancel_deferred_exec(deferred_token token) { | ||
97 | // Ignore request if the token is not valid | ||
98 | if (token == INVALID_DEFERRED_TOKEN) { | ||
99 | return false; | ||
100 | } | ||
101 | |||
102 | // Find the entry corresponding to the token | ||
103 | for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) { | ||
104 | deferred_executor_t *entry = &executors[i]; | ||
105 | if (entry->token == token) { | ||
106 | // Found it, cancel and clear the table entry | ||
107 | entry->token = INVALID_DEFERRED_TOKEN; | ||
108 | entry->trigger_time = 0; | ||
109 | entry->callback = NULL; | ||
110 | entry->cb_arg = NULL; | ||
111 | return true; | ||
112 | } | ||
113 | } | ||
114 | |||
115 | // Not found | ||
116 | return false; | ||
117 | } | ||
118 | |||
119 | void deferred_exec_task(void) { | ||
120 | uint32_t now = timer_read32(); | ||
121 | |||
122 | // Throttle only once per millisecond | ||
123 | if (((int32_t)TIMER_DIFF_32(now, last_deferred_exec_check)) > 0) { | ||
124 | last_deferred_exec_check = now; | ||
125 | |||
126 | // Run through each of the executors | ||
127 | for (int i = 0; i < MAX_DEFERRED_EXECUTORS; ++i) { | ||
128 | deferred_executor_t *entry = &executors[i]; | ||
129 | |||
130 | // Check if we're supposed to execute this entry | ||
131 | if (entry->token != INVALID_DEFERRED_TOKEN && ((int32_t)TIMER_DIFF_32(entry->trigger_time, now)) <= 0) { | ||
132 | // Invoke the callback and work work out if we should be requeued | ||
133 | uint32_t delay_ms = entry->callback(entry->trigger_time, entry->cb_arg); | ||
134 | |||
135 | // Update the trigger time if we have to repeat, otherwise clear it out | ||
136 | if (delay_ms > 0) { | ||
137 | // Intentionally add just the delay to the existing trigger time -- this ensures the next | ||
138 | // invocation is with respect to the previous trigger, rather than when it got to execution. Under | ||
139 | // normal circumstances this won't cause issue, but if another executor is invoked that takes a | ||
140 | // considerable length of time, then this ensures best-effort timing between invocations. | ||
141 | entry->trigger_time += delay_ms; | ||
142 | } else { | ||
143 | // If it was zero, then the callback is cancelling repeated execution. Free up the slot. | ||
144 | entry->token = INVALID_DEFERRED_TOKEN; | ||
145 | entry->trigger_time = 0; | ||
146 | entry->callback = NULL; | ||
147 | entry->cb_arg = NULL; | ||
148 | } | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | } | ||