libcoap 4.3.5b
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
16
23
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
124void coap_lock_unlock_func(coap_lock_t *lock, const char *file, int line);
125
140int coap_lock_lock_func(coap_lock_t *lock, const char *file, int line);
141
158#define coap_lock_lock(c,failed) do { \
159 if (!coap_lock_lock_func(&global_lock, __FILE__, __LINE__)) { \
160 failed; \
161 } \
162 } while (0)
163
173#define coap_lock_unlock(c) do { \
174 coap_lock_unlock_func(&global_lock, __FILE__, __LINE__); \
175 } while (0)
176
193#define coap_lock_specific_lock(lock, failed) do { \
194 if (!coap_lock_lock_func(lock, __FILE__, __LINE__)) { \
195 failed; \
196 } \
197 } while (0)
198
209#define coap_lock_specific_unlock(lock) do { \
210 coap_lock_unlock_func(lock, __FILE__, __LINE__); \
211 } while (0)
212
223#define coap_lock_callback(c,func) do { \
224 coap_lock_check_locked(c); \
225 global_lock.in_callback++; \
226 global_lock.callback_file = __FILE__; \
227 global_lock.callback_line = __LINE__; \
228 func; \
229 global_lock.in_callback--; \
230 } while (0)
231
244#define coap_lock_callback_ret(r,c,func) do { \
245 coap_lock_check_locked(c); \
246 global_lock.in_callback++; \
247 global_lock.callback_file = __FILE__; \
248 global_lock.callback_line = __LINE__; \
249 (r) = func; \
250 global_lock.in_callback--; \
251 } while (0)
252
264#define coap_lock_callback_release(c,func,failed) do { \
265 coap_lock_check_locked(c); \
266 coap_lock_unlock(c); \
267 func; \
268 coap_lock_lock(c,failed); \
269 } while (0)
270
284#define coap_lock_callback_ret_release(r,c,func,failed) do { \
285 coap_lock_check_locked(c); \
286 coap_lock_unlock(c); \
287 (r) = func; \
288 coap_lock_lock(c,failed); \
289 } while (0)
290
304#define coap_lock_specific_callback_release(lock,func,failed) do { \
305 coap_lock_check_locked(NULL); \
306 coap_lock_unlock(NULL); \
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(NULL, 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
374#define coap_lock_lock(c,failed) do { \
375 if (!coap_lock_lock_func(&global_lock)) { \
376 failed; \
377 } \
378 } while (0)
379
396#define coap_lock_specific_lock(lock, failed) do { \
397 if (!coap_lock_lock_func(lock)) { \
398 failed; \
399 } \
400 } while (0)
401
411#define coap_lock_unlock(c) do { \
412 coap_lock_unlock_func(&global_lock); \
413 } while (0)
414
425#define coap_lock_specific_unlock(lock) do { \
426 coap_lock_unlock_func(lock); \
427 } while (0)
428
439#define coap_lock_callback(c,func) do { \
440 coap_lock_check_locked(c); \
441 global_lock.in_callback++; \
442 func; \
443 global_lock.in_callback--; \
444 } while (0)
445
458#define coap_lock_callback_ret(r,c,func) do { \
459 coap_lock_check_locked(c); \
460 global_lock.in_callback++; \
461 (r) = func; \
462 global_lock.in_callback--; \
463 } while (0)
464
476#define coap_lock_callback_release(c,func,failed) do { \
477 coap_lock_check_locked(c); \
478 coap_lock_unlock(c); \
479 func; \
480 coap_lock_lock(c,failed); \
481 } while (0)
482
496#define coap_lock_specific_callback_release(lock,func,failed) do { \
497 coap_lock_check_locked(NULL); \
498 coap_lock_unlock(NULL); \
499 coap_lock_specific_lock(lock, failed); \
500 (lock)->in_callback++; \
501 func; \
502 (lock)->in_callback--; \
503 coap_lock_specific_unlock(lock); \
504 coap_lock_lock(NULL,failed); \
505 } while (0)
506
520#define coap_lock_callback_ret_release(r,c,func,failed) do { \
521 coap_lock_check_locked(c); \
522 coap_lock_unlock(c); \
523 (r) = func; \
524 coap_lock_lock(c,failed); \
525 } while (0)
526
527# endif /* ! COAP_THREAD_RECURSIVE_CHECK */
528
532#define coap_lock_init(lock) do { \
533 memset(&(lock)->mutex, 0, sizeof((lock)->mutex)); \
534 coap_mutex_init(&(lock)->mutex); \
535 } while (0)
536
540#define coap_lock_check_locked(c) do { \
541 assert(coap_thread_pid == global_lock.pid); \
542 } while (0)
543
557#define coap_lock_invert(c,alt_lock,failed) do { \
558 coap_lock_check_locked(c); \
559 coap_lock_unlock(c); \
560 alt_lock; \
561 coap_lock_lock(c,failed); \
562 } while (0)
563
564extern coap_lock_t global_lock;
565
566#else /* ! COAP_THREAD_SAFE */
567
568/*
569 * No locking - single thread
570 */
572
591#define coap_lock_lock(c,failed)
592
611#define coap_lock_specific_lock(lock,failed)
612
623#define coap_lock_unlock(c)
624
635#define coap_lock_specific_unlock(lock)
636
642#define coap_lock_init(lock)
643
649#define coap_lock_check_locked(c) {}
650
663#define coap_lock_callback(c,func) func
664
679#define coap_lock_callback_ret(r,c,func) (r) = func
680
694#define coap_lock_callback_release(c,func,failed) func
695
706#define coap_lock_specific_callback_release(lock,func,failed) func
707
724#define coap_lock_callback_ret_release(r,c,func,failed) (r) = func
725
741#define coap_lock_invert(c,alt_lock,failed) func
742
743#endif /* ! COAP_THREAD_SAFE */
744
746
747#endif /* COAP_THREADSAFE_INTERNAL_H_ */
int coap_mutex_t
#define coap_thread_pid_t
coap_mutex_t coap_lock_t