Lely core libraries 2.3.4
stop.c
Go to the documentation of this file.
1
24#include "util.h"
25
26#if !LELY_NO_MALLOC
27
28#if !LELY_NO_THREADS
29#include <lely/libc/stdatomic.h>
30#include <lely/libc/threads.h>
31#endif
32#include <lely/util/errnum.h>
33#include <lely/util/stop.h>
34#include <lely/util/util.h>
35
36#include <assert.h>
37#include <stdlib.h>
38
40struct stop_token {
46#if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__)))
47 size_t refcnt;
48#elif _WIN64 && !defined(__MINGW32__)
49 volatile LONGLONG refcnt;
50#elif _WIN32 && !defined(__MINGW32__)
51 volatile LONG refcnt;
52#else
53 atomic_size_t refcnt;
54#endif
55#if !LELY_NO_THREADS
57 mtx_t mtx;
59 cnd_t cond;
60#endif
62 struct sllist queue;
64 struct stop_func *func;
65};
66
67static stop_token_t *stop_token_init(stop_token_t *token);
68static void stop_token_fini(stop_token_t *token);
69
76#if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__)))
77 size_t value;
78#elif _WIN64 && !defined(__MINGW32__)
79 volatile LONGLONG value;
80#elif _WIN32 && !defined(__MINGW32__)
81 volatile LONG value;
82#else
83 atomic_size_t value;
84#endif
85#if !LELY_NO_THREADS
87 thrd_t thr;
88#endif
91};
92
93static inline stop_source_t *stop_source_from_token(const stop_token_t *token);
94
95static void *stop_source_alloc(void);
96static void stop_source_free(void *ptr);
97
98static stop_source_t *stop_source_init(stop_source_t *source);
99static void stop_source_fini(stop_source_t *source);
100
101static void stop_source_destroy(stop_source_t *source);
102
105{
106 if (token)
107#if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__)))
108 token->refcnt++;
109#elif _WIN64 && !defined(__MINGW32__)
110 InterlockedIncrementNoFence64(&token->refcnt);
111#elif _WIN32 && !defined(__MINGW32__)
112 InterlockedIncrementNoFence(&token->refcnt);
113#else
114 atomic_fetch_add_explicit(
115 &token->refcnt, 1, memory_order_relaxed);
116#endif
117 return token;
118}
119
120void
122{
123 if (!token)
124 return;
125#if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__)))
126 if (!--token->refcnt) {
127#elif _WIN64 && !defined(__MINGW32__)
128 if (!InterlockedDecrementRelease64(&token->refcnt)) {
129 MemoryBarrier();
130#elif _WIN32 && !defined(__MINGW32__)
131 if (!InterlockedDecrementRelease(&token->refcnt)) {
132 MemoryBarrier();
133#else
134 if (atomic_fetch_sub_explicit(&token->refcnt, 1, memory_order_release)
135 == 1) {
136 atomic_thread_fence(memory_order_acquire);
137#endif
138 stop_source_destroy(stop_source_from_token(token));
139 }
140}
141
142int
144{
145 return stop_source_stop_requested(stop_source_from_token(token));
146}
147
148int
150{
151 const stop_source_t *source = stop_source_from_token(token);
152
153#if LELY_NO_THREADS || LELY_NO_ATOMICS
154 size_t value = source->value;
155#else
156 size_t value = atomic_load_explicit(
157 (atomic_size_t *)&source->value, memory_order_relaxed);
158#endif
159 return value != 0;
160}
161
162int
164{
165 stop_source_t *source = stop_source_from_token(token);
166 assert(func);
167
168#if LELY_NO_THREADS || LELY_NO_ATOMICS
169 size_t value = source->value;
170#else
171 size_t value = atomic_load_explicit(
172 (atomic_size_t *)&source->value, memory_order_relaxed);
173#endif
174 // If no stop request has been issued, add the callback to the queue.
175 if (!(value & 1)) {
176#if !LELY_NO_THREADS
177 mtx_lock(&token->mtx);
178 // Check again, in case a stop request was issued before we
179 // acquired the lock.
180 if (!stop_token_stop_requested(token)) {
181#endif
182 sllist_push_front(&token->queue, &func->_node);
183#if !LELY_NO_THREADS
184 mtx_unlock(&token->mtx);
185#endif
186 return 0;
187#if !LELY_NO_THREADS
188 }
189 mtx_unlock(&token->mtx);
190#endif
191 }
192
193 // A stop request was issued, so invoke the callback immediately.
194 func->func(func);
195 return 1;
196}
197
198void
200{
201 assert(token);
202 assert(func);
203
204#if !LELY_NO_THREADS
205 mtx_lock(&token->mtx);
206#endif
207 if (func == token->func) {
208#if !LELY_NO_THREADS
209 stop_source_t *source = stop_source_from_token(token);
210 // Only wait for the callback to complete if it is running on
211 // another thread.
212 if (!thrd_equal(thrd_current(), source->thr)) {
213 do
214 cnd_wait(&token->cond, &token->mtx);
215 while (func == token->func);
216 }
217#endif
218 } else {
219 sllist_remove(&token->queue, &func->_node);
220 }
221#if !LELY_NO_THREADS
222 mtx_unlock(&token->mtx);
223#endif
224}
225
228{
229 int errc = 0;
230
231 stop_source_t *source = stop_source_alloc();
232 if (!source) {
233 errc = get_errc();
234 goto error_alloc;
235 }
236
237 stop_source_t *tmp = stop_source_init(source);
238 if (!tmp) {
239 errc = get_errc();
240 goto error_init;
241 }
242 source = tmp;
243
244 return source;
245
246error_init:
247 stop_source_free(source);
248error_alloc:
249 set_errc(errc);
250 return NULL;
251}
252
255{
256 if (source) {
257 stop_token_acquire(&source->token);
258#if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__)))
259 source->value += 2;
260#elif _WIN64 && !defined(__MINGW32__)
261 InterlockedAddNoFence64(&source->value, 2);
262#elif _WIN32 && !defined(__MINGW32__)
263 InterlockedaddNoFence(&source->value, 2);
264#else
265 atomic_fetch_add_explicit(
266 &source->value, 2, memory_order_relaxed);
267#endif
268 }
269 return source;
270}
271
272void
274{
275 if (source) {
276#if LELY_NO_THREADS
277 source->value -= 2;
278#elif _WIN64 && !defined(__MINGW32__)
279 InterlockedAddRelease64(&source->value, -2);
280#elif _WIN32 && !defined(__MINGW32__)
281 InterlockedAddRelease(&source->value, -2);
282#else
283 atomic_fetch_sub_explicit(
284 &source->value, 2, memory_order_release);
285#endif
286 stop_token_release(&source->token);
287 }
288}
289
290int
292{
293 assert(source);
294 stop_token_t *token = &source->token;
295
296 // Perform a quick check to see if a stop request has already been
297 // issued.
298#if LELY_NO_THREADS || LELY_NO_ATOMICS
299 if (source->value & 1)
300#else
301 if (atomic_load_explicit(&source->value, memory_order_relaxed) & 1)
302#endif
303 return 0;
304
305#if !LELY_NO_THREADS
306 // Register the stop request while holding the lock.
307 mtx_lock(&token->mtx);
308#endif
309#if LELY_NO_THREADS || (LELY_NO_ATOMICS && (!_WIN32 || defined(__MINGW32__)))
310 source->value |= 1;
311#else
312#if _WIN64 && !defined(__MINGW32__)
313 LONGLONG value = InterlockedOrNoFence64(&source->value, 1);
314#elif _WIN32 && !defined(__MINGW32__)
315 LONG value = InterlockedOrNoFence(&source->value, 1);
316#else
317 size_t value = atomic_fetch_or_explicit(
318 &source->value, 1, memory_order_release);
319#endif
320 // Check again, in case a stop request was issued before we acquired the
321 // lock.
322 if (value & 1) {
323#if !LELY_NO_THREADS
324 mtx_unlock(&token->mtx);
325#endif
326 return 0;
327 }
328#endif
329
330#if !LELY_NO_THREADS
331 // Record the identifier of the calling thread, to prevent a deadlock in
332 // stop_token_remove().
333 source->thr = thrd_current();
334#endif
335
336 // Invoke all registered callbacks.
337 struct slnode *node;
338 while ((node = sllist_pop_front(&token->queue))) {
339 struct stop_func *func =
340 structof(node, struct stop_func, _node);
341 // Store a pointer to the currently running callback.
342 token->func = func;
343#if !LELY_NO_THREADS
344 mtx_unlock(&token->mtx);
345#endif
346 assert(func->func);
347 func->func(func);
348#if !LELY_NO_THREADS
349 mtx_lock(&token->mtx);
350#endif
351 // Signal stop_token_remove() that the callback has been
352 // completed.
353 token->func = NULL;
354#if !LELY_NO_THREADS
355 cnd_signal(&token->cond);
356#endif
357 }
358#if !LELY_NO_THREADS
359 mtx_unlock(&token->mtx);
360#endif
361
362 return 1;
363}
364
365int
367{
368 assert(source);
369
370#if LELY_NO_THREADS || LELY_NO_ATOMICS
371 return source->value & 1;
372#else
373 // clang-format off
374 return atomic_load_explicit((atomic_size_t *)&source->value,
375 memory_order_acquire) & 1;
376 // clang-format on
377#endif
378}
379
382{
383 return stop_token_acquire(source ? &source->token : NULL);
384}
385
386static stop_token_t *
387stop_token_init(stop_token_t *token)
388{
389 assert(token);
390
391#if !LELY_NO_THREADS
392 int errc = 0;
393#endif
394
395#if LELY_NO_THREADS || LELY_NO_ATOMICS || (_WIN32 && !defined(__MINGW32__))
396 token->refcnt = 1;
397#else
398 atomic_init(&token->refcnt, 1);
399#endif
400
401#if !LELY_NO_THREADS
402 if (mtx_init(&token->mtx, mtx_plain) != thrd_success) {
403 errc = get_errc();
404 goto error_init_mtx;
405 }
406
407 if (cnd_init(&token->cond) != thrd_success) {
408 errc = get_errc();
409 goto error_init_cond;
410 }
411#endif
412
413 sllist_init(&token->queue);
414 token->func = NULL;
415
416 return token;
417
418#if !LELY_NO_THREADS
419 // cnd_destroy(&token->cond);
420error_init_cond:
421 mtx_destroy(&token->mtx);
422error_init_mtx:
423 set_errc(errc);
424 return NULL;
425#endif
426}
427
428static void
429stop_token_fini(stop_token_t *token)
430{
431#if LELY_NO_THREADS
432 (void)token;
433#else
434 assert(token);
435
436 cnd_destroy(&token->cond);
437 mtx_destroy(&token->mtx);
438#endif
439}
440
441static inline stop_source_t *
442stop_source_from_token(const stop_token_t *token)
443{
444 assert(token);
445
446 return structof(token, stop_source_t, token);
447}
448
449static void *
450stop_source_alloc(void)
451{
452 void *ptr = malloc(sizeof(stop_source_t));
453#if !LELY_NO_ERRNO
454 if (!ptr)
455 set_errc(errno2c(errno));
456#endif
457 return ptr;
458}
459
460static void
461stop_source_free(void *ptr)
462{
463 free(ptr);
464}
465
466static stop_source_t *
467stop_source_init(stop_source_t *source)
468{
469 assert(source);
470
471#if LELY_NO_THREADS || LELY_NO_ATOMICS || (_WIN32 && !defined(__MINGW32__))
472 source->value = 2;
473#else
474 atomic_init(&source->value, 2);
475#endif
476
477 if (!stop_token_init(&source->token))
478 return NULL;
479
480 return source;
481}
482
483static void
484stop_source_fini(stop_source_t *source)
485{
486 assert(source);
487
488 stop_token_fini(&source->token);
489}
490
491static void
492stop_source_destroy(stop_source_t *source)
493{
494 if (source) {
495 stop_source_fini(source);
496 stop_source_free(source);
497 }
498}
499
500#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 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
void sllist_push_front(struct sllist *list, struct slnode *node)
Pushes a node to the front of a singly-linked list.
Definition sllist.h:221
struct slnode * sllist_remove(struct sllist *list, struct slnode *node)
Removes a node from a singly-linked list.
Definition sllist.c:46
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 utilities library.
This header file is part of the C11 and POSIX compatibility library; it includes <stdatomic....
This header file is part of the C11 and POSIX compatibility library; it includes <stdlib....
int stop_token_insert(stop_token_t *token, struct stop_func *func)
Registers a callback function with the specified stop token.
Definition stop.c:163
void stop_token_release(stop_token_t *token)
Releases a reference to a stop token.
Definition stop.c:121
int stop_source_request_stop(stop_source_t *source)
Issues a stop request to the stop-state associated with the specified stop source,...
Definition stop.c:291
stop_token_t * stop_source_get_token(stop_source_t *source)
Returns (a reference to) a stop token associated with the specified stop source's stop-state.
Definition stop.c:381
stop_source_t * stop_source_acquire(stop_source_t *source)
Acquires a reference to a stop source.
Definition stop.c:254
void stop_source_release(stop_source_t *source)
Releases a reference to a stop source.
Definition stop.c:273
int stop_source_stop_requested(const stop_source_t *source)
Checks if the stop-state associated with the specified stop source has received a stop request.
Definition stop.c:366
void stop_token_remove(stop_token_t *token, struct stop_func *func)
Deregisters a callback function from the specified stop token.
Definition stop.c:199
int stop_token_stop_possible(const stop_token_t *token)
Checks if the stop-state associated with the specified stop token has received a stop request,...
Definition stop.c:149
stop_source_t * stop_source_create(void)
Creates a stop source with a new stop-state.
Definition stop.c:227
int stop_token_stop_requested(const stop_token_t *token)
Checks if the stop-state associated with the specified stop token has received a stop request.
Definition stop.c:143
stop_token_t * stop_token_acquire(stop_token_t *token)
Acquires a reference to a stop token.
Definition stop.c:104
This header file is part of the utilities library; it contains the stop token declarations.
A singly-linked list.
Definition sllist.h:52
A node in a singly-linked list.
Definition sllist.h:40
An object providing the means to register a callback function with an stop_token_t object.
Definition stop.h:51
void(* func)(struct stop_func *func)
The function to be invoked when a stop request is issued.
Definition stop.h:53
A stop source.
Definition stop.c:71
thrd_t thr
The identifier of the thread invoking stop_source_request_stop().
Definition stop.c:87
struct stop_token token
The stop token associated with this source.
Definition stop.c:90
atomic_size_t value
Twice the number of references to this source, plus a single (least significant) bit indicating wheth...
Definition stop.c:83
A stop token.
Definition stop.c:40
atomic_size_t refcnt
The number of references to this token or its associated source.
Definition stop.c:53
cnd_t cond
The condition variable signaled once func completes.
Definition stop.c:59
struct sllist queue
The queue of callback functions registered with this token.
Definition stop.c:62
struct stop_func * func
A pointer to the currently running callback.
Definition stop.c:64
This header file is part of the C11 and POSIX compatibility library; it includes <threads....
int cnd_init(cnd_t *cond)
Creates a condition variable.
int thrd_equal(thrd_t thr0, thrd_t thr1)
Determines whether the thread identified by thr0 refers to the thread identified by thr1.
int mtx_init(mtx_t *mtx, int type)
Creates a mutex object with properties indicated by type, which must have one of the four values:
int mtx_lock(mtx_t *mtx)
Blocks until it locks the mutex at mtx.
void cnd_destroy(cnd_t *cond)
Releases all resources used by the condition variable at cond.
thrd_t thrd_current(void)
Identifies the thread that called it.
int cnd_wait(cnd_t *cond, mtx_t *mtx)
Atomically unlocks the mutex at mtx and endeavors to block until the condition variable at cond is si...
@ thrd_success
Indicates that the requested operation succeeded.
Definition threads.h:121
int mtx_unlock(mtx_t *mtx)
Unlocks the mutex at mtx.
void mtx_destroy(mtx_t *mtx)
Releases any resources used by the mutex at mtx.
int cnd_signal(cnd_t *cond)
Unblocks one of the threads that are blocked on the condition variable at cond at the time of the cal...
@ mtx_plain
A mutex type that supports neither timeout nor test and return.
Definition threads.h:109