Lely core libraries  2.3.4
thrd_loop.c
Go to the documentation of this file.
1 
24 #include "ev.h"
25 #include <lely/ev/exec.h>
26 #include <lely/ev/task.h>
27 #include <lely/ev/thrd_loop.h>
28 
29 #include <assert.h>
30 #include <stdint.h>
31 
32 static void ev_thrd_loop_exec_on_task_init(ev_exec_t *exec);
33 static void ev_thrd_loop_exec_on_task_fini(ev_exec_t *exec);
34 static int ev_thrd_loop_exec_dispatch(ev_exec_t *exec, struct ev_task *task);
35 static void ev_thrd_loop_exec_defer(ev_exec_t *exec, struct ev_task *task);
36 static size_t ev_thrd_loop_exec_abort(ev_exec_t *exec, struct ev_task *task);
37 static void ev_thrd_loop_exec_run(ev_exec_t *exec, struct ev_task *task);
38 
39 // clang-format off
40 static const struct ev_exec_vtbl ev_thrd_loop_exec_vtbl = {
41  &ev_thrd_loop_exec_on_task_init,
42  &ev_thrd_loop_exec_on_task_fini,
43  &ev_thrd_loop_exec_dispatch,
44  &ev_thrd_loop_exec_defer,
45  &ev_thrd_loop_exec_defer,
46  &ev_thrd_loop_exec_abort,
47  &ev_thrd_loop_exec_run
48 };
49 // clang-format on
50 
52 struct ev_thrd_loop {
54  struct sllist queue;
61  size_t ntasks;
63  int stopped;
65  int running;
66 };
67 
69 static struct ev_thrd_loop *ev_thrd_loop(void);
70 
71 static void ev_thrd_loop_on_task_init(struct ev_thrd_loop *loop);
72 static void ev_thrd_loop_on_task_fini(struct ev_thrd_loop *loop);
73 
74 ev_exec_t *
76 {
77  static ev_exec_t ev_thrd_loop_exec = &ev_thrd_loop_exec_vtbl;
78 
79  return &ev_thrd_loop_exec;
80 }
81 
82 void
84 {
85  ev_thrd_loop()->stopped = 1;
86 }
87 
88 int
90 {
91  return ev_thrd_loop()->stopped;
92 }
93 
94 void
96 {
97  ev_thrd_loop()->stopped = 0;
98 }
99 
100 size_t
102 {
103  size_t n = 0;
104  while (ev_thrd_loop_run_one())
105  n += n < SIZE_MAX;
106  return n;
107 }
108 
109 size_t
111 {
112  struct ev_thrd_loop *loop = ev_thrd_loop();
113 
114  if (loop->stopped)
115  return 0;
116 
117  struct ev_task *task =
119  if (!task) {
120  if (!loop->ntasks)
121  loop->stopped = 1;
122  return 0;
123  }
124 
125  assert(task->exec);
126  ev_exec_run(task->exec, task);
127 
128  ev_thrd_loop_on_task_fini(ev_thrd_loop());
129 
130  return 1;
131 }
132 
133 static void
134 ev_thrd_loop_exec_on_task_init(ev_exec_t *exec)
135 {
136  assert(exec == ev_thrd_loop_get_exec());
137  (void)exec;
138 
139  ev_thrd_loop_on_task_init(ev_thrd_loop());
140 }
141 
142 static void
143 ev_thrd_loop_exec_on_task_fini(ev_exec_t *exec)
144 {
145  assert(exec == ev_thrd_loop_get_exec());
146  (void)exec;
147 
148  ev_thrd_loop_on_task_fini(ev_thrd_loop());
149 }
150 
151 static int
152 ev_thrd_loop_exec_dispatch(ev_exec_t *exec, struct ev_task *task)
153 {
154  assert(exec == ev_thrd_loop_get_exec());
155  assert(task);
156 
157  if (!task->exec)
158  task->exec = exec;
159  assert(task->exec == exec);
160 
161  struct ev_thrd_loop *loop = ev_thrd_loop();
162  ev_thrd_loop_on_task_init(loop);
163  if (loop->running) {
164  ev_thrd_loop_exec_run(exec, task);
165  ev_thrd_loop_on_task_fini(loop);
166  return 1;
167  } else {
168  sllist_push_back(&loop->queue, &task->_node);
169  return 0;
170  }
171 }
172 
173 static void
174 ev_thrd_loop_exec_defer(ev_exec_t *exec, struct ev_task *task)
175 {
176  assert(exec == ev_thrd_loop_get_exec());
177  assert(task);
178 
179  if (!task->exec)
180  task->exec = exec;
181  assert(task->exec == exec);
182 
183  struct ev_thrd_loop *loop = ev_thrd_loop();
184  ev_thrd_loop_on_task_init(loop);
185  sllist_push_back(&loop->queue, &task->_node);
186 }
187 
188 static size_t
189 ev_thrd_loop_exec_abort(ev_exec_t *exec, struct ev_task *task)
190 {
191  assert(exec == ev_thrd_loop_get_exec());
192  (void)exec;
193 
194  struct ev_thrd_loop *loop = ev_thrd_loop();
195 
196  size_t n = 0;
197  if (!task) {
198  while (sllist_pop_front(&loop->queue)) {
199  ev_thrd_loop_on_task_fini(loop);
200  n += n < SIZE_MAX;
201  }
202  } else if (sllist_remove(&loop->queue, &task->_node)) {
203  ev_thrd_loop_on_task_fini(loop);
204  n++;
205  }
206  return n;
207 }
208 
209 static void
210 ev_thrd_loop_exec_run(ev_exec_t *exec, struct ev_task *task)
211 {
212  assert(exec == ev_thrd_loop_get_exec());
213  assert(task);
214 
215  if (!task->exec)
216  task->exec = exec;
217  assert(task->exec == exec);
218 
219  struct ev_thrd_loop *loop = ev_thrd_loop();
220 
221  if (task->func) {
222  int running = loop->running;
223  loop->running = 1;
224 
225  task->func(task);
226 
227  // cppcheck-suppress redundantAssignment
228  loop->running = running;
229  }
230 }
231 
232 static struct ev_thrd_loop *
234 {
235 #if LELY_NO_THREADS
236  static struct ev_thrd_loop *loop;
237 #else
238  static _Thread_local struct ev_thrd_loop *loop;
239 #endif
240  if (!loop) {
241 #if LELY_NO_THREADS
242  static struct ev_thrd_loop loop_;
243 #else
244  static _Thread_local struct ev_thrd_loop loop_;
245 #endif
246  sllist_init(&loop_.queue);
247  loop_.ntasks = 0;
248  loop_.stopped = 0;
249  loop_.running = 0;
250  loop = &loop_;
251  }
252  return loop;
253 }
254 
255 static void
256 ev_thrd_loop_on_task_init(struct ev_thrd_loop *loop)
257 {
258  assert(loop);
259 
260  loop->ntasks++;
261 }
262 
263 static void
264 ev_thrd_loop_on_task_fini(struct ev_thrd_loop *loop)
265 {
266  assert(loop);
267  assert(loop->ntasks);
268 
269  if (!--loop->ntasks)
270  loop->stopped = 1;
271 }
ev_thrd_loop_stopped
int ev_thrd_loop_stopped(void)
Returns 1 if the thread-local event loop is stopped, and 0 if not.
Definition: thrd_loop.c:89
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
ev_thrd_loop_restart
void ev_thrd_loop_restart(void)
Restarts a thread-local event loop.
Definition: thrd_loop.c:95
ev_thrd_loop
A thread-local event loop.
Definition: thrd_loop.c:52
ev_thrd_loop_get_exec
ev_exec_t * ev_thrd_loop_get_exec(void)
Returns a pointer to the executor corresponding to the thread-local event loop.
Definition: thrd_loop.c:75
ev_thrd_loop::ntasks
size_t ntasks
The number of pending tasks.
Definition: thrd_loop.c:61
exec.h
ev_thrd_loop_stop
void ev_thrd_loop_stop(void)
Stops the thread-local event loop.
Definition: thrd_loop.c:83
ev_task::func
ev_task_func_t * func
The function to be invoked when the task is run.
Definition: task.h:45
ev_thrd_loop_run_one
size_t ev_thrd_loop_run_one(void)
If the thread-local event loop is not stopped, executes the first task submitted to it,...
Definition: thrd_loop.c:110
ev_exec_run
void ev_exec_run(ev_exec_t *exec, struct ev_task *task)
Invokes the task function in *task as if the task is being executed by *exec.
Definition: exec.h:142
ev_exec_vtbl
Definition: exec.h:37
ev_thrd_loop::running
int running
A flag specifying whether ev_exec_run() is running on this thread.
Definition: thrd_loop.c:65
stdint.h
sllist_init
void sllist_init(struct sllist *list)
Initializes a singly-linked list.
Definition: sllist.h:194
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:243
ev_thrd_loop
static struct ev_thrd_loop * ev_thrd_loop(void)
Returns a pointer to the thread-local event loop.
Definition: thrd_loop.c:233
sllist
A singly-linked list.
Definition: sllist.h:52
ev_thrd_loop_run
size_t ev_thrd_loop_run(void)
If the thread-local event loop is not stopped, run all available tasks.
Definition: thrd_loop.c:101
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:249
ev_thrd_loop::stopped
int stopped
A flag specifying whether the event loop is stopped.
Definition: thrd_loop.c:63
ev_thrd_loop::queue
struct sllist queue
The queue of pending tasks.
Definition: thrd_loop.c:54
thrd_loop.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:232
ev_task::exec
ev_exec_t * exec
A pointer to the executor to which the task is (to be) submitted.
Definition: task.h:43