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 
40 struct 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;
60 #endif
62  struct sllist queue;
64  struct stop_func *func;
65 };
66 
67 static stop_token_t *stop_token_init(stop_token_t *token);
68 static void stop_token_fini(stop_token_t *token);
69 
71 struct stop_source {
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
88 #endif
90  struct stop_token token;
91 };
92 
93 static inline stop_source_t *stop_source_from_token(const stop_token_t *token);
94 
95 static void *stop_source_alloc(void);
96 static void stop_source_free(void *ptr);
97 
98 static stop_source_t *stop_source_init(stop_source_t *source);
99 static void stop_source_fini(stop_source_t *source);
100 
101 static void stop_source_destroy(stop_source_t *source);
102 
103 stop_token_t *
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
115  &token->refcnt, 1, memory_order_relaxed);
116 #endif
117  return token;
118 }
119 
120 void
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
135  == 1) {
137 #endif
138  stop_source_destroy(stop_source_from_token(token));
139  }
140 }
141 
142 int
144 {
145  return stop_source_stop_requested(stop_source_from_token(token));
146 }
147 
148 int
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 
162 int
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 
198 void
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 
246 error_init:
247  stop_source_free(source);
248 error_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
266  &source->value, 2, memory_order_relaxed);
267 #endif
268  }
269  return source;
270 }
271 
272 void
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
284  &source->value, 2, memory_order_release);
285 #endif
286  stop_token_release(&source->token);
287  }
288 }
289 
290 int
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
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 
365 int
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,
376  // clang-format on
377 #endif
378 }
379 
380 stop_token_t *
382 {
383  return stop_token_acquire(source ? &source->token : NULL);
384 }
385 
386 static stop_token_t *
387 stop_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);
420 error_init_cond:
421  mtx_destroy(&token->mtx);
422 error_init_mtx:
423  set_errc(errc);
424  return NULL;
425 #endif
426 }
427 
428 static void
429 stop_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 
441 static inline stop_source_t *
442 stop_source_from_token(const stop_token_t *token)
443 {
444  assert(token);
445 
446  return structof(token, stop_source_t, token);
447 }
448 
449 static void *
450 stop_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 
460 static void
461 stop_source_free(void *ptr)
462 {
463  free(ptr);
464 }
465 
466 static stop_source_t *
467 stop_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 
483 static void
484 stop_source_fini(stop_source_t *source)
485 {
486  assert(source);
487 
488  stop_token_fini(&source->token);
489 }
490 
491 static void
492 stop_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....
@ memory_order_release
A store operation performs a release operation on the affected memory location.
Definition: stdatomic.h:158
@ memory_order_relaxed
No operation orders memory.
Definition: stdatomic.h:131
@ memory_order_acquire
A load operation performs an acquire operation on the affected memory location.
Definition: stdatomic.h:149
#define atomic_fetch_add_explicit(object, operand, order)
Atomically replaces the value at object with *object + operand.
Definition: stdatomic.h:480
#define atomic_load_explicit(object, order)
Atomically returns the value at object.
Definition: stdatomic.h:344
#define atomic_fetch_or_explicit(object, operand, order)
Atomically replaces the value at object with *object | operand.
Definition: stdatomic.h:528
void atomic_thread_fence(memory_order order)
Inserts a fence with semantics according to order.
Definition: stdatomic.h:617
#define atomic_init(obj, value)
Initializes the atomic object at obj with the value value.
Definition: stdatomic.h:222
#define atomic_fetch_sub_explicit(object, operand, order)
Atomically replaces the value at object with *object - operand.
Definition: stdatomic.h:504
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
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
stop_source_t * stop_source_acquire(stop_source_t *source)
Acquires a reference to a stop source.
Definition: stop.c:254
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
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
stop_source_t * stop_source_create(void)
Creates a stop source with a new stop-state.
Definition: stop.c:227
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.
pthread_t thrd_t
A complete object type that holds an identifier for a thread.
Definition: threads.h:85
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.
pthread_cond_t cnd_t
A complete object type that holds an identifier for a condition variable.
Definition: threads.h:78
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.
pthread_mutex_t mtx_t
A complete object type that holds an identifier for a mutex.
Definition: threads.h:102
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