libcoap 4.3.5-develop-17c3fee
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-2025 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/*
28 * Support thread safe access into libcoap
29 *
30 * Locking at different component levels (i.e context and session) is
31 * problematic in that coap_process_io() needs to lock the context as
32 * it scans for all the sessions and then could lock the session being
33 * processed as well - but context needs to remain locked as a list is
34 * being scanned.
35 *
36 * Then if the session process needs to update context ( e.g. delayqueue),
37 * context needs to be locked. So, if coap_send() is done on a session,
38 * it has to be locked, but a retransmission of a PDU by coap_process_io()
39 * has the context already locked.
40 *
41 * However, when the context is going away (coap_free_context()), other
42 * threads may still be access the lock in what is now freed memory.
43 * A solution (by flagging being freed), worked, but still with a timing
44 * window wen the context was finally de-allocated. Coverity Scan did
45 * not like the solution.
46 *
47 * So the initial support for thread safe is done at global lock level
48 * using global_lock.
49 *
50 * Any public API call needs to potentially lock global_lock.
51 *
52 * If a public API needs thread safe protection, the coap_X() function
53 * locks the global_lock lock, calls the coap_X_lkd() function
54 * that does all the work and on return unlocks the global_lock before
55 * returning to the caller of coap_X(). These coap_X() functions
56 * need COAP_API in their definitions.
57 *
58 * Any internal libcoap calls that are to the public API coap_X() must call
59 * coap_X_lkd() if the calling code is already locked.
60 * [The compiler will throw out a deprecation warning against any internal
61 * libcoap call to a COAP_API labelled function]
62 *
63 * Any call-back into app space must be done by using the coap_lock_callback()
64 * (or coap_lock_callback_ret()) wrapper where the global_lock remains locked.
65 *
66 * Note:
67 * libcoap may call a handler, which may in turn call into libcoap, which may
68 * then call a handler. global_lock will remain locked thoughout this process
69 * by the same thread.
70 *
71 * Alternatively, coap_lock_callback_release() (or
72 * coap_lock_callback_ret_release()), is used where the global_lock is unlocked
73 * for the duration of the call-back. Used for things like a request
74 * handler which could be busy for some time.
75 *
76 * Note: On return from the call-back, the code has to be careful not to
77 * use memory locations that may have been updated in the call-back by
78 * calling a Public API.
79 *
80 * Any wait on select() or equivalent when a thread is waiting on an event
81 * must be preceded by unlock global_lock, and then global_lock re-locked after
82 * return;
83 *
84 * To check for recursive deadlock coding errors, COAP_THREAD_RECURSIVE_CHECK
85 * needs to be defined.
86 *
87 * If thread safe is not enabled, then locking of the global_lock does not take
88 * place.
89 */
90
91#if COAP_THREAD_SAFE
92
93# if COAP_THREAD_RECURSIVE_CHECK
94
95/*
96 * Locking, with deadlock detection
97 */
98typedef struct coap_lock_t {
99 coap_mutex_t mutex;
101 const char *lock_file;
102 unsigned int lock_line;
103 unsigned int unlock_line;
104 const char *unlock_file;
105 const char *callback_file;
106 unsigned int callback_line;
107 unsigned int in_callback;
108 unsigned int lock_count;
110
122void coap_lock_unlock_func(const char *file, int line);
123
137int coap_lock_lock_func(const char *file, int line);
138
155#define coap_lock_lock(failed) do { \
156 if (!coap_lock_lock_func(__FILE__, __LINE__)) { \
157 failed; \
158 } \
159 } while (0)
160
170#define coap_lock_unlock() do { \
171 coap_lock_unlock_func(__FILE__, __LINE__); \
172 } while (0)
173
183#define coap_lock_callback(func) do { \
184 coap_lock_check_locked(); \
185 global_lock.in_callback++; \
186 global_lock.callback_file = __FILE__; \
187 global_lock.callback_line = __LINE__; \
188 func; \
189 global_lock.in_callback--; \
190 } while (0)
191
203#define coap_lock_callback_ret(r,func) do { \
204 coap_lock_check_locked(); \
205 global_lock.in_callback++; \
206 global_lock.callback_file = __FILE__; \
207 global_lock.callback_line = __LINE__; \
208 (r) = func; \
209 global_lock.in_callback--; \
210 } while (0)
211
222#define coap_lock_callback_release(func,failed) do { \
223 coap_lock_check_locked(); \
224 coap_lock_unlock(); \
225 func; \
226 coap_lock_lock(failed); \
227 } while (0)
228
241#define coap_lock_callback_ret_release(r,func,failed) do { \
242 coap_lock_check_locked(); \
243 coap_lock_unlock(); \
244 (r) = func; \
245 coap_lock_lock(failed); \
246 } while (0)
247
248extern coap_lock_t global_lock;
249
250# else /* ! COAP_THREAD_RECURSIVE_CHECK */
251
252/*
253 * Locking, but no deadlock detection
254 */
255typedef struct coap_lock_t {
256 coap_mutex_t mutex;
258 uint32_t in_callback;
259 volatile uint32_t lock_count;
261
271void coap_lock_unlock_func(void);
272
283int coap_lock_lock_func(void);
284
301#define coap_lock_lock(failed) do { \
302 if (!coap_lock_lock_func()) { \
303 failed; \
304 } \
305 } while (0)
306
316#define coap_lock_unlock() do { \
317 coap_lock_unlock_func(); \
318 } while (0)
319
329#define coap_lock_callback(func) do { \
330 coap_lock_check_locked(); \
331 global_lock.in_callback++; \
332 func; \
333 global_lock.in_callback--; \
334 } while (0)
335
347#define coap_lock_callback_ret(r,func) do { \
348 coap_lock_check_locked(); \
349 global_lock.in_callback++; \
350 global_lock.in_callback++; \
351 (r) = func; \
352 global_lock.in_callback--; \
353 } while (0)
354
365#define coap_lock_callback_release(func,failed) do { \
366 coap_lock_check_locked(); \
367 coap_lock_unlock(); \
368 func; \
369 coap_lock_lock(failed); \
370 } while (0)
371
384#define coap_lock_callback_ret_release(r,func,failed) do { \
385 coap_lock_check_locked(); \
386 coap_lock_unlock(); \
387 (r) = func; \
388 coap_lock_lock(failed); \
389 } while (0)
390
391# endif /* ! COAP_THREAD_RECURSIVE_CHECK */
392
396#define coap_lock_init() do { \
397 memset(&global_lock.mutex, 0, sizeof(global_lock.mutex)); \
398 coap_mutex_init(&global_lock.mutex); \
399 } while (0)
400
404#define coap_lock_check_locked() do { \
405 assert(coap_thread_pid == global_lock.pid); \
406 } while (0)
407
420#define coap_lock_invert(alt_lock,failed) do { \
421 coap_lock_check_locked(); \
422 coap_lock_unlock(); \
423 alt_lock; \
424 coap_lock_lock(failed); \
425 } while (0)
426
427extern coap_lock_t global_lock;
428
429#else /* ! COAP_THREAD_SAFE */
430
431/*
432 * No locking - single thread
433 */
435
453#define coap_lock_lock(failed)
454
463#define coap_lock_unlock()
464
470#define coap_lock_init()
471
477#define coap_lock_check_locked() {}
478
490#define coap_lock_callback(func) func
491
505#define coap_lock_callback_ret(r,func) (r) = func
506
519#define coap_lock_callback_release(func,failed) func
520
536#define coap_lock_callback_ret_release(r,func,failed) (r) = func
537
552#define coap_lock_invert(alt_lock,failed) alt_lock
553
554#endif /* ! COAP_THREAD_SAFE */
555
558#endif /* COAP_THREADSAFE_INTERNAL_H_ */
int coap_mutex_t
#define coap_thread_pid_t
coap_mutex_t coap_lock_t