libcoap 4.3.5-develop-e766f05
Loading...
Searching...
No Matches
coap_threadsafe_internal.h
Go to the documentation of this file.
1/*
2 * coap_threadsafe_internal.h -- Mapping of threadsafe functions
3 *
4 * Copyright (C) 2023-2026 Jon Shallow <supjps-libcoap@jpshallow.com>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * This file is part of the CoAP library libcoap. Please see README for terms
9 * of use.
10 */
11
24#ifndef COAP_THREADSAFE_INTERNAL_H_
25#define COAP_THREADSAFE_INTERNAL_H_
26
27#ifdef __cplusplus
28extern "C" {
29#endif
30
31/*
32 * Support thread safe access into libcoap
33 *
34 * Locking at different component levels (i.e context and session) is
35 * problematic in that coap_process_io() needs to lock the context as
36 * it scans for all the sessions and then could lock the session being
37 * processed as well - but context needs to remain locked as a list is
38 * being scanned.
39 *
40 * Then if the session process needs to update context ( e.g. delayqueue),
41 * context needs to be locked. So, if coap_send() is done on a session,
42 * it has to be locked, but a retransmission of a PDU by coap_process_io()
43 * has the context already locked.
44 *
45 * However, when the context is going away (coap_free_context()), other
46 * threads may still be access the lock in what is now freed memory.
47 * A solution (by flagging being freed), worked, but still with a timing
48 * window when the context was finally de-allocated. Coverity Scan did
49 * not like the solution.
50 *
51 * So the initial support for thread safe is done at global lock level
52 * using global_lock.
53 *
54 * Any public API call needs to potentially lock global_lock.
55 *
56 * If a public API needs thread safe protection, the coap_X() function
57 * locks the global_lock lock, calls the coap_X_lkd() function
58 * that does all the work and on return unlocks the global_lock before
59 * returning to the caller of coap_X(). These coap_X() functions
60 * need COAP_API in their definitions.
61 *
62 * Any internal libcoap calls that are to the public API coap_X() must call
63 * coap_X_lkd() if the calling code is already locked.
64 * [The compiler will throw out a deprecation warning against any internal
65 * libcoap call to a COAP_API labelled function]
66 *
67 * Any call-back into app space must be done by using the coap_lock_callback()
68 * (or coap_lock_callback_ret()) wrapper where the global_lock remains locked.
69 *
70 * Note:
71 * libcoap may call a handler, which may in turn call into libcoap, which may
72 * then call a handler. global_lock will remain locked thoughout this process
73 * by the same thread.
74 *
75 * Alternatively, coap_lock_callback_release() (or
76 * coap_lock_callback_ret_release()), is used where the global_lock is unlocked
77 * for the duration of the call-back. Used for things like a request
78 * handler which could be busy for some time.
79 *
80 * Note: On return from the call-back, the code has to be careful not to
81 * use memory locations that may have been updated in the call-back by
82 * calling a Public API.
83 *
84 * Any wait on select() or equivalent when a thread is waiting on an event
85 * must be preceded by unlock global_lock, and then global_lock re-locked after
86 * return;
87 *
88 * To check for recursive deadlock coding errors, COAP_THREAD_RECURSIVE_CHECK
89 * needs to be defined.
90 *
91 * If thread safe is not enabled, then locking of the global_lock does not take
92 * place.
93 */
94
95#if COAP_THREAD_SAFE
96
97#ifndef COAP_THREAD_KILL_SIG
98#define COAP_THREAD_KILL_SIG SIGINT
99#endif /* COAP_THREAD_KILL_SIG */
100
101# if COAP_THREAD_RECURSIVE_CHECK
102
103/*
104 * Locking, with deadlock detection
105 */
106typedef struct coap_lock_t {
107 coap_mutex_t mutex;
109 const char *lock_file;
110 unsigned int lock_line;
111 unsigned int unlock_line;
112 const char *unlock_file;
113 const char *callback_file;
114 unsigned int callback_line;
115 unsigned int in_callback;
116 unsigned int lock_count;
118
131void coap_lock_unlock_func(coap_lock_t *lock, const char *file, int line);
132
147int coap_lock_lock_func(coap_lock_t *lock, const char *file, int line);
148
164#define coap_lock_lock(failed) do { \
165 if (!coap_lock_lock_func(&global_lock, __FILE__, __LINE__)) { \
166 failed; \
167 } \
168 } while (0)
169
177#define coap_lock_unlock() do { \
178 coap_lock_unlock_func(&global_lock, __FILE__, __LINE__); \
179 } while (0)
180
197#define coap_lock_specific_lock(lock, failed) do { \
198 if (!coap_lock_lock_func(lock, __FILE__, __LINE__)) { \
199 failed; \
200 } \
201 } while (0)
202
213#define coap_lock_specific_unlock(lock) do { \
214 coap_lock_unlock_func(lock, __FILE__, __LINE__); \
215 } while (0)
216
226#define coap_lock_callback(func) do { \
227 coap_lock_check_locked(); \
228 global_lock.in_callback++; \
229 global_lock.callback_file = __FILE__; \
230 global_lock.callback_line = __LINE__; \
231 func; \
232 global_lock.in_callback--; \
233 } while (0)
234
246#define coap_lock_callback_ret(r,func) do { \
247 coap_lock_check_locked(); \
248 global_lock.in_callback++; \
249 global_lock.callback_file = __FILE__; \
250 global_lock.callback_line = __LINE__; \
251 (r) = func; \
252 global_lock.in_callback--; \
253 } while (0)
254
265#define coap_lock_callback_release(func,failed) do { \
266 coap_lock_check_locked(); \
267 coap_lock_unlock(); \
268 func; \
269 coap_lock_lock(failed); \
270 } while (0)
271
284#define coap_lock_callback_ret_release(r,func,failed) do { \
285 coap_lock_check_locked(); \
286 coap_lock_unlock(); \
287 (r) = func; \
288 coap_lock_lock(failed); \
289 } while (0)
290
304#define coap_lock_specific_callback_release(lock,func,failed) do { \
305 coap_lock_check_locked(); \
306 coap_lock_unlock(); \
307 coap_lock_specific_lock(lock, failed); \
308 (lock)->in_callback++; \
309 (lock)->callback_file = __FILE__; \
310 (lock)->callback_line = __LINE__; \
311 func; \
312 (lock)->in_callback--; \
313 coap_lock_specific_unlock(lock); \
314 coap_lock_lock(failed); \
315 } while (0)
316
317extern coap_lock_t global_lock;
318
319# else /* ! COAP_THREAD_RECURSIVE_CHECK */
320
321/*
322 * Locking, but no deadlock detection
323 */
324typedef struct coap_lock_t {
325 coap_mutex_t mutex;
327 uint32_t in_callback;
328 volatile uint32_t lock_count;
330
342void coap_lock_unlock_func(coap_lock_t *lock);
343
356int coap_lock_lock_func(coap_lock_t *lock);
357
373#define coap_lock_lock(failed) do { \
374 if (!coap_lock_lock_func(&global_lock)) { \
375 failed; \
376 } \
377 } while (0)
378
395#define coap_lock_specific_lock(lock, failed) do { \
396 if (!coap_lock_lock_func(lock)) { \
397 failed; \
398 } \
399 } while (0)
400
408#define coap_lock_unlock() do { \
409 coap_lock_unlock_func(&global_lock); \
410 } while (0)
411
422#define coap_lock_specific_unlock(lock) do { \
423 coap_lock_unlock_func(lock); \
424 } while (0)
425
435#define coap_lock_callback(func) do { \
436 coap_lock_check_locked(); \
437 global_lock.in_callback++; \
438 func; \
439 global_lock.in_callback--; \
440 } while (0)
441
453#define coap_lock_callback_ret(r,func) do { \
454 coap_lock_check_locked(); \
455 global_lock.in_callback++; \
456 (r) = func; \
457 global_lock.in_callback--; \
458 } while (0)
459
470#define coap_lock_callback_release(func,failed) do { \
471 coap_lock_check_locked(); \
472 coap_lock_unlock(); \
473 func; \
474 coap_lock_lock(failed); \
475 } while (0)
476
490#define coap_lock_specific_callback_release(lock,func,failed) do { \
491 coap_lock_check_locked(); \
492 coap_lock_unlock(); \
493 coap_lock_specific_lock(lock, failed); \
494 (lock)->in_callback++; \
495 func; \
496 (lock)->in_callback--; \
497 coap_lock_specific_unlock(lock); \
498 coap_lock_lock(failed); \
499 } while (0)
500
513#define coap_lock_callback_ret_release(r,func,failed) do { \
514 coap_lock_check_locked(); \
515 coap_lock_unlock(); \
516 (r) = func; \
517 coap_lock_lock(failed); \
518 } while (0)
519
520# endif /* ! COAP_THREAD_RECURSIVE_CHECK */
521
525#define coap_lock_init(lock) do { \
526 memset(&(lock)->mutex, 0, sizeof((lock)->mutex)); \
527 coap_mutex_init(&(lock)->mutex); \
528 } while (0)
529
533#define coap_lock_check_locked() do { \
534 assert(coap_thread_pid == global_lock.pid); \
535 } while (0)
536
549#define coap_lock_invert(alt_lock,failed) do { \
550 coap_lock_check_locked(); \
551 coap_lock_unlock(); \
552 alt_lock; \
553 coap_lock_lock(failed); \
554 } while (0)
555
556extern coap_lock_t global_lock;
557
558#else /* ! COAP_THREAD_SAFE */
559
560/*
561 * No locking - single thread
562 */
564
582#define coap_lock_lock(failed)
583
602#define coap_lock_specific_lock(lock,failed)
603
612#define coap_lock_unlock()
613
624#define coap_lock_specific_unlock(lock)
625
631#define coap_lock_init(lock)
632
638#define coap_lock_check_locked() {}
639
651#define coap_lock_callback(func) func
652
666#define coap_lock_callback_ret(r,func) (r) = func
667
680#define coap_lock_callback_release(func,failed) func
681
692#define coap_lock_specific_callback_release(lock,func,failed) func
693
709#define coap_lock_callback_ret_release(r,func,failed) (r) = func
710
725#define coap_lock_invert(alt_lock,failed) alt_lock
726
727#endif /* ! COAP_THREAD_SAFE */
728
731#ifdef __cplusplus
732}
733#endif
734
735#endif /* COAP_THREADSAFE_INTERNAL_H_ */
int coap_mutex_t
#define coap_thread_pid_t
coap_mutex_t coap_lock_t