Lely core libraries 2.3.4
std_exec.c
Go to the documentation of this file.
1
24#include "ev.h"
25
26#if !LELY_NO_MALLOC
27
28#include <lely/ev/exec.h>
29#include <lely/ev/std_exec.h>
30#include <lely/ev/task.h>
31#include <lely/util/errnum.h>
32#include <lely/util/util.h>
33
34#include <assert.h>
35#include <stdint.h>
36#include <stdlib.h>
37
38static void ev_std_exec_on_task_init(ev_exec_t *exec);
39static void ev_std_exec_on_task_fini(ev_exec_t *exec);
40static int ev_std_exec_dispatch(ev_exec_t *exec, struct ev_task *task);
41static void ev_std_exec_post(ev_exec_t *exec, struct ev_task *task);
42static void ev_std_exec_defer(ev_exec_t *exec, struct ev_task *task);
43static size_t ev_std_exec_abort(ev_exec_t *exec, struct ev_task *task);
44static void ev_std_exec_run(ev_exec_t *exec, struct ev_task *task);
45
46// clang-format off
47static const struct ev_exec_vtbl ev_std_exec_vtbl = {
48 &ev_std_exec_on_task_init,
49 &ev_std_exec_on_task_fini,
50 &ev_std_exec_dispatch,
51 &ev_std_exec_post,
52 &ev_std_exec_defer,
53 &ev_std_exec_abort,
54 &ev_std_exec_run
55};
56// clang-format on
57
58static inline struct ev_std_exec *ev_std_exec_from_exec(const ev_exec_t *exec);
59
61 struct ev_task_node *next;
62 struct sllist *queue;
63};
64
66 struct ev_exec_node *next;
67 ev_exec_t *exec;
68 struct ev_task_node *queue;
69};
70
71#if LELY_NO_THREADS
72static struct ev_exec_node *ev_exec_list;
73#else
74static _Thread_local struct ev_exec_node *ev_exec_list;
75#endif
76
77static struct ev_exec_node **ev_exec_find(
78 const ev_exec_t *exec, struct ev_exec_node **pnode);
79
80void *
81ev_std_exec_alloc(void)
82{
83 struct ev_std_exec *exec = malloc(sizeof(*exec));
84#if !LELY_NO_ERRNO
85 if (!exec)
86 set_errc(errno2c(errno));
87#endif
88 // cppcheck-suppress memleak symbolName=exec
89 return exec ? &exec->exec_vptr : NULL;
90}
91
92void
93ev_std_exec_free(void *ptr)
94{
95 if (ptr)
96 free(ev_std_exec_from_exec(ptr));
97}
98
100ev_std_exec_init(ev_exec_t *exec_, ev_std_exec_impl_t *impl)
101{
102 struct ev_std_exec *exec = ev_std_exec_from_exec(exec_);
103 assert(impl);
104
105 exec->exec_vptr = &ev_std_exec_vtbl;
106
107 exec->impl = impl;
108
109 return exec_;
110}
111
112void
113ev_std_exec_fini(ev_exec_t *exec)
114{
115 (void)exec;
116}
117
118ev_exec_t *
119ev_std_exec_create(ev_std_exec_impl_t *impl)
120{
121 int errc = 0;
122
123 ev_exec_t *exec = ev_std_exec_alloc();
124 if (!exec) {
125 errc = get_errc();
126 goto error_alloc;
127 }
128
129 ev_exec_t *tmp = ev_std_exec_init(exec, impl);
130 if (!tmp) {
131 errc = get_errc();
132 goto error_init;
133 }
134 exec = tmp;
135
136 return exec;
137
138error_init:
139 ev_std_exec_free((void *)exec);
140error_alloc:
141 set_errc(errc);
142 return NULL;
143}
144
145void
146ev_std_exec_destroy(ev_exec_t *exec)
147{
148 if (exec) {
149 ev_std_exec_fini(exec);
150 ev_std_exec_free((void *)exec);
151 }
152}
153
154static void
155ev_std_exec_on_task_init(ev_exec_t *exec_)
156{
157 struct ev_std_exec *exec = ev_std_exec_from_exec(exec_);
158 assert(exec->impl);
159 assert((*exec->impl)->on_task_init);
160
161 (*exec->impl)->on_task_init(exec->impl);
162}
163
164static void
165ev_std_exec_on_task_fini(ev_exec_t *exec_)
166{
167 struct ev_std_exec *exec = ev_std_exec_from_exec(exec_);
168 assert(exec->impl);
169 assert((*exec->impl)->on_task_fini);
170
171 (*exec->impl)->on_task_fini(exec->impl);
172}
173
174static int
175ev_std_exec_dispatch(ev_exec_t *exec, struct ev_task *task)
176{
177 assert(task);
178 assert(!task->exec || task->exec == exec);
179
180 struct ev_exec_node **pnode = ev_exec_find(exec, &ev_exec_list);
181 if (!*pnode) {
182 ev_std_exec_post(exec, task);
183 return 0;
184 }
185
186 if (!task->exec)
187 task->exec = exec;
188
189 if (task->func)
190 task->func(task);
191
192 return 1;
193}
194
195static void
196ev_std_exec_post(ev_exec_t *exec_, struct ev_task *task)
197{
198 struct ev_std_exec *exec = ev_std_exec_from_exec(exec_);
199 assert(exec->impl);
200 assert((*exec->impl)->post);
201 assert(task);
202 assert(!task->exec || task->exec == exec_);
203
204 if (!task->exec)
205 task->exec = exec_;
206
207 (*exec->impl)->post(exec->impl, task);
208}
209
210static void
211ev_std_exec_defer(ev_exec_t *exec, struct ev_task *task)
212{
213 assert(exec);
214 assert(task);
215 assert(!task->exec || task->exec == exec);
216
217 struct ev_exec_node **pnode = ev_exec_find(exec, &ev_exec_list);
218 if (!*pnode) {
219 ev_std_exec_post(exec, task);
220 return;
221 }
222
223 if (!task->exec)
224 task->exec = exec;
225
226 ev_std_exec_on_task_init(exec);
227 assert((*pnode)->queue);
228 assert((*pnode)->queue->queue);
229 sllist_push_back((*pnode)->queue->queue, &task->_node);
230}
231
232static size_t
233ev_std_exec_abort(ev_exec_t *exec_, struct ev_task *task)
234{
235 struct ev_std_exec *exec = ev_std_exec_from_exec(exec_);
236 assert(exec->impl);
237 assert((*exec->impl)->abort);
238
239 size_t nnode = 0;
240 size_t nimpl = 0;
241
242 struct ev_exec_node **pnode = ev_exec_find(exec_, &ev_exec_list);
243 if (*pnode) {
244 assert((*pnode)->queue);
245 struct sllist *queue = (*pnode)->queue->queue;
246 assert(queue);
247 if (!task) {
248 while (sllist_pop_front(queue)) {
249 ev_std_exec_on_task_fini(exec_);
250 nnode += nnode < SIZE_MAX;
251 }
252 } else if (sllist_remove(queue, &task->_node)) {
253 ev_std_exec_on_task_fini(exec_);
254 nnode++;
255 }
256 }
257
258 if (!task || !nnode)
259 nimpl = (*exec->impl)->abort(exec->impl, task);
260
261 return nimpl < SIZE_MAX - nnode ? nnode + nimpl : SIZE_MAX;
262}
263
264static void
265ev_std_exec_run(ev_exec_t *exec_, struct ev_task *task)
266{
267 struct ev_std_exec *exec = ev_std_exec_from_exec(exec_);
268 assert(exec->impl);
269 assert((*exec->impl)->post);
270 assert(task);
271 assert(!task->exec || task->exec == exec_);
272
273 if (!task->exec)
274 task->exec = exec_;
275
276 if (task->func) {
277 struct ev_exec_node **plist = &ev_exec_list;
278
279 struct sllist queue;
280 sllist_init(&queue);
281 struct ev_task_node queue_node = { NULL, &queue };
282 struct ev_exec_node exec_node = { NULL, exec_, &queue_node };
283
284 struct ev_exec_node **pnode = ev_exec_find(exec_, plist);
285 if (*pnode) {
286 queue_node.next = (*pnode)->queue;
287 (*pnode)->queue = &queue_node;
288 } else {
289 exec_node.next = *plist;
290 *plist = &exec_node;
291 }
292
293 task->func(task);
294
295 pnode = ev_exec_find(exec_, plist);
296 assert(*pnode);
297 assert((*pnode)->queue == &queue_node);
298 if ((*pnode)->queue->next) {
299 (*pnode)->queue = (*pnode)->queue->next;
300 } else {
301 *pnode = (*pnode)->next;
302 }
303
304 while ((task = ev_task_from_node(sllist_pop_front(&queue)))) {
305 assert(task->exec == exec_);
306 (*exec->impl)->post(exec->impl, task);
307 ev_std_exec_on_task_fini(exec_);
308 }
309 }
310}
311
312static inline struct ev_std_exec *
313ev_std_exec_from_exec(const ev_exec_t *exec)
314{
315 assert(exec);
316
317 return structof(exec, struct ev_std_exec, exec_vptr);
318}
319
320static struct ev_exec_node **
321ev_exec_find(const ev_exec_t *exec, struct ev_exec_node **pnode)
322{
323 while (*pnode && (*pnode)->exec != exec)
324 pnode = &(*pnode)->next;
325 return pnode;
326}
327
328#endif // !LELY_NO_MALLOC
This header file is part of the utilities library; it contains the native and platform-independent er...
int get_errc(void)
Returns the last (thread-specific) native error code set by a system call or library function.
Definition: errnum.c:932
void set_errc(int errc)
Sets the current (thread-specific) native error code to errc.
Definition: errnum.c:944
int errno2c(int errnum)
Transforms a standard C error number to a native error code.
Definition: errnum.c:46
This header file is part of the event library; it contains the abstract task executor interface.
#define _Thread_local
An object whose identifier is declared with the storage-class specifier _Thread_local has thread stor...
Definition: features.h:249
const struct ev_exec_vtbl *const ev_exec_t
An abstract task executor.
Definition: ev.h:29
This is the public header file of the utilities library.
#define structof(ptr, type, member)
Obtains the address of a structure from the address of one of its members.
Definition: util.h:93
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
Definition: sllist.h:194
struct slnode * sllist_remove(struct sllist *list, struct slnode *node)
Removes a node from a singly-linked list.
Definition: sllist.c:46
void sllist_push_back(struct sllist *list, struct slnode *node)
Pushes a node to the back of a singly-linked list.
Definition: sllist.h:232
struct slnode * sllist_pop_front(struct sllist *list)
Pops a node from the front of a singly-linked list.
Definition: sllist.h:243
This is the internal header file of the event library.
This header file is part of the event library; it contains the standard executor declarations.
This header file is part of the C11 and POSIX compatibility library; it includes <stdint....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
An executable task.
Definition: task.h:41
ev_task_func_t * func
The function to be invoked when the task is run.
Definition: task.h:45
ev_exec_t * exec
A pointer to the executor to which the task is (to be) submitted.
Definition: task.h:43
A node in a pairing heap.
Definition: pheap.h:52
struct pnode * next
A pointer to the next sibling node.
Definition: pheap.h:62
A singly-linked list.
Definition: sllist.h:52
This header file is part of the event library; it contains the task declarations.
struct ev_task * ev_task_from_node(struct slnode *node)
Converts a pointer to a node in a queue to the address of the task containing the node.
Definition: task.c:32