libcoap 4.3.5-develop-19cef11
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-2024 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 assert(c); \
323 coap_lock_unlock_func(); \
324 } while (0)
325
336#define coap_lock_callback(c,func) do { \
337 coap_lock_check_locked(c); \
338 global_lock.in_callback++; \
339 func; \
340 global_lock.in_callback--; \
341 } while (0)
342
355#define coap_lock_callback_ret(r,c,func) do { \
356 coap_lock_check_locked(c); \
357 global_lock.in_callback++; \
358 global_lock.in_callback++; \
359 (r) = func; \
360 global_lock.in_callback--; \
361 } while (0)
362
374#define coap_lock_callback_release(c,func,failed) do { \
375 coap_lock_check_locked(c); \
376 coap_lock_unlock(c); \
377 func; \
378 coap_lock_lock(c,failed); \
379 } while (0)
380
394#define coap_lock_callback_ret_release(r,c,func,failed) do { \
395 coap_lock_check_locked(c); \
396 coap_lock_unlock(c); \
397 (r) = func; \
398 coap_lock_lock(c,failed); \
399 } while (0)
400
401# endif /* ! COAP_THREAD_RECURSIVE_CHECK */
402
406#define coap_lock_init() do { \
407 memset(&global_lock.mutex, 0, sizeof(global_lock.mutex)); \
408 coap_mutex_init(&global_lock.mutex); \
409 } while (0)
410
414#define coap_lock_check_locked(c) do { \
415 assert(coap_thread_pid == global_lock.pid); \
416 } while (0)
417
431#define coap_lock_invert(c,alt_lock,failed) do { \
432 coap_lock_check_locked(c); \
433 coap_lock_unlock(c); \
434 alt_lock; \
435 coap_lock_lock(c,failed); \
436 } while (0)
437
438extern coap_lock_t global_lock;
439
440#else /* ! COAP_THREAD_SAFE */
441
442/*
443 * No locking - single thread
444 */
446
465#define coap_lock_lock(c,failed)
466
477#define coap_lock_unlock(c)
478
484#define coap_lock_init()
485
491#define coap_lock_check_locked(c) {}
492
505#define coap_lock_callback(c,func) func
506
521#define coap_lock_callback_ret(r,c,func) (r) = func
522
536#define coap_lock_callback_release(c,func,failed) func
537
554#define coap_lock_callback_ret_release(r,c,func,failed) (r) = func
555
571#define coap_lock_invert(c,alt_lock,failed) func
572
573#endif /* ! COAP_THREAD_SAFE */
574
577#endif /* COAP_THREADSAFE_INTERNAL_H_ */
int coap_mutex_t
#define coap_thread_pid_t
coap_mutex_t coap_lock_t