libcoap 4.3.4-develop-9f1418e
coap_mutex_internal.h
Go to the documentation of this file.
1/*
2 * coap_mutex.h -- mutex utilities
3 *
4 * Copyright (C) 2019-2024 Jon Shallow <supjps-libcoap@jpshallow.com>
5 * 2019 Olaf Bergmann <bergmann@tzi.org>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * This file is part of the CoAP library libcoap. Please see README for terms
10 * of use.
11 */
12
18#ifndef COAP_MUTEX_INTERNAL_H_
19#define COAP_MUTEX_INTERNAL_H_
20
21/*
22 * Mutexes are used for
23 * 1) If there is a constrained stack, and large static variables (instead
24 * of the large variable being on the stack) need to be protected.
25 * 2) libcoap if built with thread safe support.
26 */
27#if defined(HAVE_PTHREAD_H) && defined(HAVE_PTHREAD_MUTEX_LOCK)
28#include <pthread.h>
29
30typedef pthread_mutex_t coap_mutex_t;
31
32#define coap_mutex_init(a) pthread_mutex_init(a, NULL)
33#define coap_mutex_destroy(a) pthread_mutex_destroy(a)
34#define coap_mutex_lock(a) pthread_mutex_lock(a)
35#define coap_mutex_trylock(a) pthread_mutex_trylock(a)
36#define coap_mutex_unlock(a) pthread_mutex_unlock(a)
37#define coap_thread_pid_t pthread_t
38#define coap_thread_pid pthread_self()
39
40#elif defined(RIOT_VERSION)
41/* use RIOT's mutex API */
42#include <mutex.h>
43
44typedef mutex_t coap_mutex_t;
45
46#define coap_mutex_init(a) mutex_init(a)
47#define coap_mutex_destroy(a)
48#define coap_mutex_lock(a) mutex_lock(a)
49#define coap_mutex_trylock(a) mutex_trylock(a)
50#define coap_mutex_unlock(a) mutex_unlock(a)
51#define coap_thread_pid_t kernel_pid_t
52#define coap_thread_pid thread_getpid()
53
54#elif defined(WITH_LWIP)
55/* Use LwIP's mutex API */
56
57#if NO_SYS
58#if COAP_THREAD_SAFE
59#error Multi-threading not supported (no mutex support)
60#endif /* ! COAP_THREAD_SAFE */
61/* Single threaded, no-op'd in lwip/sys.h */
62typedef int coap_mutex_t;
63
64#define coap_mutex_init(a) *(a) = 0
65#define coap_mutex_destroy(a) *(a) = 0
66#define coap_mutex_lock(a) *(a) = 1
67#define coap_mutex_trylock(a) *(a) = 1
68#define coap_mutex_unlock(a) *(a) = 0
69#define coap_thread_pid_t int
70#define coap_thread_pid 1
71
72#else /* !NO_SYS */
73#include <lwip/sys.h>
74#ifdef LWIP_UNIX_LINUX
75#include <pthread.h>
76typedef pthread_mutex_t coap_mutex_t;
77
78#define coap_mutex_init(a) pthread_mutex_init(a, NULL)
79#define coap_mutex_destroy(a) pthread_mutex_destroy(a)
80#define coap_mutex_lock(a) pthread_mutex_lock(a)
81#define coap_mutex_trylock(a) pthread_mutex_trylock(a)
82#define coap_mutex_unlock(a) pthread_mutex_unlock(a)
83#define coap_thread_pid_t pthread_t
84#define coap_thread_pid pthread_self()
85#else /* ! LWIP_UNIX_LINUX */
86typedef sys_mutex_t coap_mutex_t;
87
88#define coap_mutex_init(a) sys_mutex_new(a)
89#define coap_mutex_destroy(a) sys_mutex_set_invalid(a)
90#define coap_mutex_lock(a) sys_mutex_lock(a)
91#define coap_mutex_unlock(a) sys_mutex_unlock(a)
92#define coap_thread_pid_t sys_thread_t
93#define coap_thread_pid (coap_thread_pid_t)1
94
95#if COAP_THREAD_RECURSIVE_CHECK
96#error COAP_THREAD_RECURSIVE_CHECK not supported (no coap_mutex_trylock())
97#endif /* COAP_THREAD_RECURSIVE_CHECK */
98#endif /* !LWIP_UNIX_LINUX */
99#endif /* !NO_SYS */
100
101#elif defined(WITH_CONTIKI)
102#if COAP_THREAD_SAFE
103#error Multi-threading not supported (no mutex support)
104#endif /* ! COAP_THREAD_SAFE */
105/* Contiki does not have a mutex API, used as single thread */
106typedef int coap_mutex_t;
107
108#define coap_mutex_init(a) *(a) = 0
109#define coap_mutex_destroy(a) *(a) = 0
110#define coap_mutex_lock(a) *(a) = 1
111#define coap_mutex_trylock(a) *(a) = 1
112#define coap_mutex_unlock(a) *(a) = 0
113#define coap_thread_pid_t int
114#define coap_thread_pid 1
115
116#elif defined(__ZEPHYR__)
117#include <zephyr/sys/mutex.h>
118
119typedef struct sys_mutex coap_mutex_t;
120
121#define coap_mutex_init(a) sys_mutex_init(a)
122#define coap_mutex_destroy(a)
123#define coap_mutex_lock(a) sys_mutex_lock(a, K_FOREVER)
124#define coap_mutex_trylock(a) sys_mutex_lock(a, K_NO_WAIT)
125#define coap_mutex_unlock(a) sys_mutex_unlock(a)
126
127#else /* !__ZEPYR__ && !WITH_CONTIKI && !WITH_LWIP && !RIOT_VERSION && !HAVE_PTHREAD_H && !HAVE_PTHREAD_MUTEX_LOCK */
128/* define stub mutex functions */
129#if COAP_THREAD_SAFE
130#error Multi-threading not supported (no mutex support)
131#else /* ! COAP_THREAD_SAFE */
132#if COAP_CONSTRAINED_STACK
133#warning "stub mutex functions"
134#endif /* COAP_CONSTRAINED_STACK */
135#endif /* ! COAP_THREAD_SAFE */
136typedef int coap_mutex_t;
137
138#define coap_mutex_init(a) *(a) = 0
139#define coap_mutex_destroy(a) *(a) = 0
140#define coap_mutex_lock(a) *(a) = 1
141#define coap_mutex_trylock(a) *(a) = 1
142#define coap_mutex_unlock(a) *(a) = 0
143#define coap_thread_pid_t int
144#define coap_thread_pid 1
145
146#endif /* !WITH_CONTIKI && !WITH_LWIP && !RIOT_VERSION && !HAVE_PTHREAD_H && !HAVE_PTHREAD_MUTEX_LOCK */
147
148#if COAP_CONSTRAINED_STACK
149
150extern coap_mutex_t m_show_pdu;
151extern coap_mutex_t m_log_impl;
152extern coap_mutex_t m_dtls_recv;
153extern coap_mutex_t m_read_session;
154extern coap_mutex_t m_read_endpoint;
155extern coap_mutex_t m_persist_add;
156
157#endif /* COAP_CONSTRAINED_STACK */
158
159/*
160 * Support thread safe access into libcoap
161 *
162 * Locking at different component levels (i.e context and session) is
163 * problematic in that coap_process_io() needs to lock the context as
164 * it scans for all the sessions and then could lock the session being
165 * processed as well - but context needs to remain locked as a list is
166 * being scanned.
167 *
168 * Then if the session process needs to update context ( e.g. delayqueue),
169 * context needs to be locked. So, if coap_send() is done on a session,
170 * it has to be locked, but a retransmission of a PDU by coap_process_io()
171 * has the context already locked.
172 *
173 * So the initial support for thread safe is done at the context level.
174 *
175 * Any public API call needs to potentially lock context, as there may be
176 * multiple contexts. If a public API needs thread safe protection, a
177 * locking wrapper for coap_X() is added to src/coap_threadsafe.c which then
178 * calls the coap_X_locked() function of coap_X() having locked context.
179 *
180 * Then an entry is added to include/coap3/coap_threadsafe_internal.h to map
181 * all the coap_X() definitions and calls within the libcoap code to
182 * coap_X_locked() (with the exception of src/coap_threadsafe.c).
183 *
184 * A second entry is added to include/coap3/coap_threadsafe_internal.h which
185 * defines the coap_X_locked() function header.
186 *
187 * Any call-back into app space must be done by using the coap_lock_callback()
188 * (or coap_lock_callback_ret()) wrapper.
189 *
190 * Note:
191 * libcoap may call a handler, which may in turn call into libcoap, which may
192 * then call a handler. context will remain locked thoughout this process.
193 *
194 * Any wait on select() or equivalent when a thread is waiting on an event
195 * must be preceded by unlock context, and then context re-locked after
196 * return;
197 *
198 * To check for recursive deadlocks, COAP_THREAD_RECURSIVE_CHECK needs to be
199 * defined.
200 *
201 * If thread safe is not enabled, then coap_threadsafe.c and
202 * coap_threadsafe_internal.h do nothing.
203 */
204#if COAP_THREAD_SAFE
205# if COAP_THREAD_RECURSIVE_CHECK
206
207/*
208 * Locking, with deadlock detection
209 */
210typedef struct coap_lock_t {
211 coap_mutex_t mutex;
213 coap_thread_pid_t freeing_pid;
214 const char *lock_file;
215 unsigned int lock_line;
216 unsigned int unlock_line;
217 const char *unlock_file;
218 const char *callback_file;
219 unsigned int callback_line;
220 unsigned int being_freed;
221 unsigned int in_callback;
222 unsigned int lock_count;
224
225void coap_lock_unlock_func(coap_lock_t *lock, const char *file, int line);
226int coap_lock_lock_func(coap_lock_t *lock, const char *file, int line);
227
228#define coap_lock_lock(s,failed) do { \
229 assert(s); \
230 if (!coap_lock_lock_func(&(s)->lock, __FILE__, __LINE__)) { \
231 failed; \
232 } \
233 } while (0)
234
235#define coap_lock_unlock(s) do { \
236 assert(s); \
237 coap_lock_unlock_func(&(s)->lock, __FILE__, __LINE__); \
238 } while (0)
239
240#define coap_lock_callback(s,func) do { \
241 coap_lock_check_locked(s); \
242 (s)->lock.in_callback++; \
243 (s)->lock.callback_file = __FILE__; \
244 (s)->lock.callback_line = __LINE__; \
245 func; \
246 (s)->lock.in_callback--; \
247 } while (0)
248
249#define coap_lock_callback_ret(r,s,func) do { \
250 coap_lock_check_locked(s); \
251 (s)->lock.in_callback++; \
252 (s)->lock.callback_file = __FILE__; \
253 (s)->lock.callback_line = __LINE__; \
254 r = func; \
255 (s)->lock.in_callback--; \
256 } while (0)
257
258# else /* ! COAP_THREAD_RECURSIVE_CHECK */
259
260/*
261 * Locking, but no deadlock detection
262 */
263typedef struct coap_lock_t {
264 coap_mutex_t mutex;
266 coap_thread_pid_t freeing_pid;
267 uint32_t being_freed;
268 uint32_t in_callback;
269 volatile uint32_t lock_count;
271
272void coap_lock_unlock_func(coap_lock_t *lock);
273int coap_lock_lock_func(coap_lock_t *lock);
274
275#define coap_lock_lock(s,failed) do { \
276 assert(s); \
277 if (!coap_lock_lock_func(&(s)->lock)) { \
278 failed; \
279 } \
280 } while (0)
281
282#define coap_lock_unlock(s) do { \
283 assert(s); \
284 coap_lock_unlock_func(&(s)->lock); \
285 } while (0)
286
287#define coap_lock_callback(s,func) do { \
288 coap_lock_check_locked(s); \
289 (s)->lock.in_callback++; \
290 func; \
291 (s)->lock.in_callback--; \
292 } while (0)
293
294#define coap_lock_callback_ret(r,s,func) do { \
295 coap_lock_check_locked(s); \
296 (s)->lock.in_callback++; \
297 r = func; \
298 (s)->lock.in_callback--; \
299 } while (0)
300
301# endif /* ! COAP_THREAD_RECURSIVE_CHECK */
302
303#define coap_lock_init(s) do { \
304 assert(s); \
305 memset(&((s)->lock), 0, sizeof((s)->lock)); \
306 coap_mutex_init(&(s)->lock.mutex); \
307 } while (0)
308
309#define coap_lock_being_freed(s,failed) do { \
310 coap_lock_lock(s,failed); \
311 (s)->lock.being_freed = 1; \
312 (s)->lock.freeing_pid = coap_thread_pid; \
313 coap_lock_unlock(s); \
314 } while (0)
315
316#define coap_lock_check_locked(s) do { \
317 assert ((s) && (s)->lock.being_freed ? coap_thread_pid == (s)->lock.freeing_pid: coap_thread_pid == (s)->lock.pid); \
318 } while (0)
319
320#define coap_lock_invert(s,func,f) do { \
321 coap_lock_check_locked(s); \
322 if (!(s)->lock.being_freed) { \
323 coap_lock_unlock(s); \
324 func; \
325 coap_lock_lock(s,f); \
326 } else { \
327 func; \
328 } \
329 } while (0)
330
331#else /* ! COAP_THREAD_SAFE */
332
333/*
334 * No locking - single thread
335 */
337
338#define coap_lock_lock(s,failed)
339#define coap_lock_unlock(s)
340#define coap_lock_init(s)
341#define coap_lock_being_freed(s,failed)
342#define coap_lock_check_locked(s) {}
343#define coap_lock_callback(s,func) func
344#define coap_lock_callback_ret(r,s,func) ret = func
345#define coap_lock_invert(s,func,f) func
346
347#endif /* ! COAP_THREAD_SAFE */
348
349#endif /* COAP_MUTEX_INTERNAL_H_ */
coap_mutex_t coap_lock_t
int coap_mutex_t
#define coap_thread_pid_t