-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtask.c
314 lines (281 loc) · 8.17 KB
/
task.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
/*
* task.c
*
* Created: 10/1/2013 9:08:50 PM
* Author: Greg Cook
*/
#include <stdlib.h>
#include <util/atomic.h>
#include "task.h"
#include "systick.h"
static task_t * task_timer_array[MAX_QUEUE];
static heap_t task_timer_queue;
static list_t task_process_queue;
static list_t task_dynamic_free;
static task_t task_dynamic_array[TASK_ALLOC_COUNT];
static const uint8_t task_alloc_count = TASK_ALLOC_COUNT;
/************************************************************************/
/* Task/TaskQueue Support Functions */
/************************************************************************/
/**
* Get the key that the timer queue uses for sorting the heap
*
* @param v Data element passed into the heap
* @return heap key
*/
static inline heap_key_t task_timer_get_key(const void *v) {
return ((task_t*)v)->ticks;
}
/************************************************************************/
/* Task Interface Functions */
/************************************************************************/
/**
* Initialize task object
*
* @param task Task object to initialize
* @param callback Array of task slice callback functions
* @param fdata Data for callback functions to use
* @return void
* @note a pointer to the task object is passed to the callback function
* and the task object has a pointer to fdata if any is set
*/
static void task_init(task_t *task, const task_slice_callback_fp * const callback, void *fdata) {
List.init(task_list_node(task));
task->slices = callback;
task->fdata = fdata;
task->enabled = true;
task->slice_idx = 0;
}
/**
* Allocate task structure from "dynamic" list
*
* @param void
* @return empty task struct or NULL if there are none available
* @note There are a limited number of pre-allocated elements available and it is up to the user
* to be aware
*/
static task_t *task_allocate(void) {
task_t *result = NULL;
if (!List.isEmpty(&task_dynamic_free)) {
list_t *lnode = List.removeFront(&task_dynamic_free);
result = task_list_entry(lnode);
}
return result;
}
/**
* Free a task "dynamically" back to the system for re-allocation
*
* @param task The task to be freed
* @return void
* @note It is possible, although unintended to use this interface with statically allocated
* nodes.
*/
static void task_delete(task_t *task) {
list_t *lnode = task_list_node(task);
List.init(lnode); // make sure task is not currently hanging out in some other list
// wipe data in task structure
task->enabled = false;
task->slices = NULL;
task->fdata = NULL;
task->start_ticks = 0;
task->ticks = 0;
List.addAtRear(&task_dynamic_free, lnode);
}
/**
* Create a new task, allocating storage from from the pre-allocated storage
*
* @param callback Array of task slice callback functions
* @param fdata Data for callbacks to use
* @param start_ticks Periodic scheduling interval
* @param en Enable
* @return task object or NULL if no storage is available
*/
static task_t *task_new(const task_slice_callback_fp * const callback, void *fdata, tick_t start_ticks, bool en) {
task_t *t = task_allocate();
if (t) {
task_init(t, callback, fdata);
t->start_ticks = start_ticks;
t->ticks = 0;
t->enabled = en;
}
return t;
}
/**
* Set periodic scheduling interval
*
* The task timer will count down one tick each systick until it reaches zero
* and then dispatch the task
* @param t task object pointer
* @param ticks tick count
* @return void
*/
static inline void task_set_ticks(task_t *t, tick_t ticks) {
t->start_ticks = ticks;
}
/**
* Enable task
* @param t Task to enable
* @return void
*/
static inline void task_enable(task_t *t) { t->enabled = true; }
/**
* Disable task
* @param t Task to disable
* @return void
*/
static inline void task_disable(task_t *t) { t->enabled = false; }
/**
* Schedule task
* @param t Task to schedule
* @return void
*/
static inline void task_schedule(task_t *task, task_sched_t sched) {
TaskQueue.enqueue(task, sched);
}
/**
* Public Interface for Task Class
*/
const task_class_t const Task = {
.init = task_init,
.new = task_new,
.delete = task_delete,
.set_ticks = task_set_ticks,
.enable = task_enable,
.disable = task_disable,
.schedule = task_schedule
};
/************************************************************************/
/* Task Queue Interface Functions */
/************************************************************************/
/**
* Initialize pre-allocated task objects
* @param void
* @return void
*/
static inline void task_queue_init(void) {
static uint8_t task_initialized = 0;
if (task_initialized) return;
int8_t i;
for (i = 0; i < task_alloc_count; ++i) {
list_t *lnode = task_list_node(&task_dynamic_array[i]);
List.addAtRear(&task_dynamic_free, lnode);
}
}
/**
* Schedule task
*
* A task is either scheduled immediately, or on the timer queue
*
* @param t Task to schedule
* @param sched Scheduling algorithm to use
*/
static inline void task_queue_enqueue(task_t *t, task_sched_t sched) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
if (sched == TASK_SCHED_IMMED)
List.addAtRear(&task_process_queue, task_list_node(t));
else {
t->ticks = t->start_ticks + systick_get();
Heap.insert(&task_timer_queue, t);
}
}
}
/**
* Get the next task atomically from the immediate queue
*
* @param void
* @retval next task to run or NULL if none are available
*/
static inline task_t *task_queue_dequeue(void) {
task_t *result = NULL;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
result = task_list_entry(List.removeFront(&task_process_queue));
}
return result;
}
/**
* Callback for use in the systick ISR
*
* This function decrements tick counts and moves tasks from the timer queue
* to the process queue when appropriate.
* @param void
* @return void
*/
static void task_queue_timer_callback(void) {
task_t *task = Heap.head(&task_timer_queue);
tick_t systicks = systick_get();
while (task && (systicks > task->ticks)) {
task = Heap.remove_head(&task_timer_queue);
List.addAtRear(&task_process_queue, task_list_node(task));
task = Heap.head(&task_timer_queue);
}
}
/**
* Dispatch the next task from the scheduler and reschedule it
*
* This is the main callback handler for the task scheduler
*
* @param void
* @return void
*/
static void task_queue_process_callback(void) {
task_t *next_task = TaskQueue.dequeue();
if (next_task) {
task_slice_result_t result = {0, TASK_END};
if (next_task->enabled) {
NONATOMIC_BLOCK(NONATOMIC_RESTORESTATE) {
result = next_task->slices[next_task->slice_idx](next_task);
}
next_task->slice_idx = result.next;
}
switch(result.sched) {
case TASK_SCHED_IMMED:
/* fallthrough */
case TASK_RESCHED:
TaskQueue.enqueue(next_task, result.sched);
break;
case TASK_SCHED: // Not a valid return value, use RESCHED
/* fallthrough */
case TASK_END:
/* fallthrough */
case TASK_WAIT:
break;
default:
// error of some sort
break;
}
}
}
/**
* Public Interface for the TaskQueue Class
*/
task_queue_class_t TaskQueue = {
.init = task_queue_init,
.enqueue = task_queue_enqueue,
.dequeue = task_queue_dequeue,
.timer_callback = task_queue_timer_callback,
.process_callback = task_queue_process_callback
};
/**
* Initialize the scheduler queues
* @param void
* @return void
*/
void scheduler_init(void) {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
Heap.init(&task_timer_queue, HEAP_MIN, MAX_QUEUE, task_timer_array, task_timer_get_key);
List.init(&task_process_queue);
List.init(&task_dynamic_free);
}
task_queue_init();
}
/**
* Scheduler main event loop
* @param void
* @return never returns
*/
void scheduler_run(void) {
sei();
while(true) {
task_queue_process_callback();
}
}