libcoap 4.3.5-develop-4c7ce99
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. However, context is provided as a parameter should
49 * context level locking be subsequently used.
50 *
51 * Any public API call needs to potentially lock global_lock.
52 *
53 * If a public API needs thread safe protection, the coap_X() function
54 * locks the global_lock lock, calls the coap_X_lkd() function
55 * that does all the work and on return unlocks the global_lock before
56 * returning to the caller of coap_X(). These coap_X() functions
57 * need COAP_API in their definitions.
58 *
59 * Any internal libcoap calls that are to the public API coap_X() must call
60 * coap_X_lkd() if the calling code is already locked.
61 * [The compiler will throw out a deprecation warning against any internal
62 * libcoap call to a COAP_API labelled function]
63 *
64 * Any call-back into app space must be done by using the coap_lock_callback()
65 * (or coap_lock_callback_ret()) wrapper where the global_lock remains locked.
66 *
67 * Note:
68 * libcoap may call a handler, which may in turn call into libcoap, which may
69 * then call a handler. global_lock will remain locked thoughout this process
70 * by the same thread.
71 *
72 * Alternatively, coap_lock_callback_release() (or
73 * coap_lock_callback_ret_release()), is used where the global_lock is unlocked
74 * for the duration of the call-back. Used for things like a request
75 * handler which could be busy for some time.
76 *
77 * Note: On return from the call-back, the code has to be careful not to
78 * use memory locations that may have been updated in the call-back by
79 * calling a Public API.
80 *
81 * Any wait on select() or equivalent when a thread is waiting on an event
82 * must be preceded by unlock global_lock, and then global_lock re-locked after
83 * return;
84 *
85 * To check for recursive deadlock coding errors, COAP_THREAD_RECURSIVE_CHECK
86 * needs to be defined.
87 *
88 * If thread safe is not enabled, then locking of the global_lock does not take
89 * place.
90 */
91
92#if COAP_THREAD_SAFE
93
94# if COAP_THREAD_RECURSIVE_CHECK
95
96/*
97 * Locking, with deadlock detection
98 */
99typedef struct coap_lock_t {
100 coap_mutex_t mutex;
102 const char *lock_file;
103 unsigned int lock_line;
104 unsigned int unlock_line;
105 const char *unlock_file;
106 const char *callback_file;
107 unsigned int callback_line;
108 unsigned int in_callback;
109 unsigned int lock_count;
111
123void coap_lock_unlock_func(const char *file, int line);
124
138int coap_lock_lock_func(const char *file, int line);
139
156#define coap_lock_lock(c,failed) do { \
157 if (!coap_lock_lock_func(__FILE__, __LINE__)) { \
158 failed; \
159 } \
160 } while (0)
161
171#define coap_lock_unlock(c) do { \
172 coap_lock_unlock_func(__FILE__, __LINE__); \
173 } while (0)
174
185#define coap_lock_callback(c,func) do { \
186 coap_lock_check_locked(c); \
187 global_lock.in_callback++; \
188 global_lock.callback_file = __FILE__; \
189 global_lock.callback_line = __LINE__; \
190 func; \
191 global_lock.in_callback--; \
192 } while (0)
193
206#define coap_lock_callback_ret(r,c,func) do { \
207 coap_lock_check_locked(c); \
208 global_lock.in_callback++; \
209 global_lock.callback_file = __FILE__; \
210 global_lock.callback_line = __LINE__; \
211 (r) = func; \
212 global_lock.in_callback--; \
213 } while (0)
214
226#define coap_lock_callback_release(c,func,failed) do { \
227 coap_lock_check_locked(c); \
228 coap_lock_unlock(c); \
229 func; \
230 coap_lock_lock(c,failed); \
231 } while (0)
232
246#define coap_lock_callback_ret_release(r,c,func,failed) do { \
247 coap_lock_check_locked(c); \
248 coap_lock_unlock(c); \
249 (r) = func; \
250 coap_lock_lock(c,failed); \
251 } while (0)
252
253extern coap_lock_t global_lock;
254
255# else /* ! COAP_THREAD_RECURSIVE_CHECK */
256
257/*
258 * Locking, but no deadlock detection
259 */
260typedef struct coap_lock_t {
261 coap_mutex_t mutex;
263 uint32_t in_callback;
264 volatile uint32_t lock_count;
266
276void coap_lock_unlock_func(void);
277
288int coap_lock_lock_func(void);
289
306#define coap_lock_lock(c,failed) do { \
307 if (!coap_lock_lock_func()) { \
308 failed; \
309 } \
310 } while (0)
311
321#define coap_lock_unlock(c) do { \
322 coap_lock_unlock_func(); \
323 } while (0)
324
335#define coap_lock_callback(c,func) do { \
336 coap_lock_check_locked(c); \
337 global_lock.in_callback++; \
338 func; \
339 global_lock.in_callback--; \
340 } while (0)
341
354#define coap_lock_callback_ret(r,c,func) do { \
355 coap_lock_check_locked(c); \
356 global_lock.in_callback++; \
357 global_lock.in_callback++; \
358 (r) = func; \
359 global_lock.in_callback--; \
360 } while (0)
361
373#define coap_lock_callback_release(c,func,failed) do { \
374 coap_lock_check_locked(c); \
375 coap_lock_unlock(c); \
376 func; \
377 coap_lock_lock(c,failed); \
378 } while (0)
379
393#define coap_lock_callback_ret_release(r,c,func,failed) do { \
394 coap_lock_check_locked(c); \
395 coap_lock_unlock(c); \
396 (r) = func; \
397 coap_lock_lock(c,failed); \
398 } while (0)
399
400# endif /* ! COAP_THREAD_RECURSIVE_CHECK */
401
405#define coap_lock_init() do { \
406 memset(&global_lock.mutex, 0, sizeof(global_lock.mutex)); \
407 coap_mutex_init(&global_lock.mutex); \
408 } while (0)
409
413#define coap_lock_check_locked(c) do { \
414 assert(coap_thread_pid == global_lock.pid); \
415 } while (0)
416
430#define coap_lock_invert(c,alt_lock,failed) do { \
431 coap_lock_check_locked(c); \
432 coap_lock_unlock(c); \
433 alt_lock; \
434 coap_lock_lock(c,failed); \
435 } while (0)
436
437extern coap_lock_t global_lock;
438
439#else /* ! COAP_THREAD_SAFE */
440
441/*
442 * No locking - single thread
443 */
445
464#define coap_lock_lock(c,failed)
465
476#define coap_lock_unlock(c)
477
483#define coap_lock_init()
484
490#define coap_lock_check_locked(c) {}
491
504#define coap_lock_callback(c,func) func
505
520#define coap_lock_callback_ret(r,c,func) (r) = func
521
535#define coap_lock_callback_release(c,func,failed) func
536
553#define coap_lock_callback_ret_release(r,c,func,failed) (r) = func
554
570#define coap_lock_invert(c,alt_lock,failed) alt_lock
571
572#endif /* ! COAP_THREAD_SAFE */
573
576#endif /* COAP_THREADSAFE_INTERNAL_H_ */
int coap_mutex_t
#define coap_thread_pid_t
coap_mutex_t coap_lock_t