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 
38 static void ev_std_exec_on_task_init(ev_exec_t *exec);
39 static void ev_std_exec_on_task_fini(ev_exec_t *exec);
40 static int ev_std_exec_dispatch(ev_exec_t *exec, struct ev_task *task);
41 static void ev_std_exec_post(ev_exec_t *exec, struct ev_task *task);
42 static void ev_std_exec_defer(ev_exec_t *exec, struct ev_task *task);
43 static size_t ev_std_exec_abort(ev_exec_t *exec, struct ev_task *task);
44 static void ev_std_exec_run(ev_exec_t *exec, struct ev_task *task);
45 
46 // clang-format off
47 static 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 
58 static inline struct ev_std_exec *ev_std_exec_from_exec(const ev_exec_t *exec);
59 
60 struct ev_task_node {
61  struct ev_task_node *next;
62  struct sllist *queue;
63 };
64 
65 struct ev_exec_node {
66  struct ev_exec_node *next;
67  ev_exec_t *exec;
68  struct ev_task_node *queue;
69 };
70 
71 #if LELY_NO_THREADS
72 static struct ev_exec_node *ev_exec_list;
73 #else
74 static _Thread_local struct ev_exec_node *ev_exec_list;
75 #endif
76 
77 static struct ev_exec_node **ev_exec_find(
78  const ev_exec_t *exec, struct ev_exec_node **pnode);
79 
80 void *
81 ev_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 
92 void
93 ev_std_exec_free(void *ptr)
94 {
95  if (ptr)
96  free(ev_std_exec_from_exec(ptr));
97 }
98 
99 ev_exec_t *
100 ev_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 
112 void
113 ev_std_exec_fini(ev_exec_t *exec)
114 {
115  (void)exec;
116 }
117 
118 ev_exec_t *
119 ev_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 
138 error_init:
139  ev_std_exec_free((void *)exec);
140 error_alloc:
141  set_errc(errc);
142  return NULL;
143 }
144 
145 void
146 ev_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 
154 static void
155 ev_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 
164 static void
165 ev_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 
174 static int
175 ev_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 
195 static void
196 ev_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 
210 static void
211 ev_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 
232 static size_t
233 ev_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 
264 static void
265 ev_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 
312 static inline struct ev_std_exec *
313 ev_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 
320 static struct ev_exec_node **
321 ev_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