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