libcoap 4.3.5-develop-3f4d08f
Loading...
Searching...
No Matches
coap_threadsafe.c
Go to the documentation of this file.
1/* coap_threadsafe.c -- Thread safe function locking wrappers
2 *
3 * Copyright (C) 2023-2026 Jon Shallow <supjps-libcoap@jpshallow.com>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 *
7 * This file is part of the CoAP library libcoap. Please see
8 * README for terms of use.
9 */
10
17
18#if COAP_THREAD_SAFE
19#if COAP_THREAD_RECURSIVE_CHECK
20void
21coap_lock_unlock_func(coap_lock_t *lock, const char *file, int line) {
22 assert(coap_thread_pid == lock->pid);
23 if (lock->in_callback) {
24 assert(lock->lock_count > 0);
25 lock->lock_count--;
26 } else {
27 lock->pid = 0;
28 lock->unlock_file = file;
29 lock->unlock_line = line;
30 coap_mutex_unlock(&lock->mutex);
31 }
32}
33
34int
35coap_lock_lock_func(coap_lock_t *lock, const char *file, int line) {
36 if (!coap_started) {
37 /* libcoap not initialized with coap_startup() */
38 return 0;
39 }
40 if (coap_mutex_trylock(&lock->mutex)) {
41 if (coap_thread_pid == lock->pid) {
42 /* This thread locked the mutex */
43 if (lock->in_callback) {
44 /* This is called from within an app callback */
45 lock->lock_count++;
46 assert(lock->in_callback == lock->lock_count);
47 return 1;
48 } else {
49 coap_log_alert("Thread Deadlock: Last %s: %u, this %s: %u\n",
50 lock->lock_file, lock->lock_line, file, line);
51 assert(0);
52 }
53 }
54 /* Wait for the other thread to unlock */
55 coap_mutex_lock(&lock->mutex);
56 }
57 /* Just got the lock, so should not be in a locked callback */
58 assert(!lock->in_callback);
59 lock->pid = coap_thread_pid;
60 lock->lock_file = file;
61 lock->lock_line = line;
62 return 1;
63}
64
65#else /* ! COAP_THREAD_RECURSIVE_CHECK */
66
67void
68coap_lock_unlock_func(coap_lock_t *lock) {
69 assert(coap_thread_pid == lock->pid);
70 if (lock->in_callback) {
71 assert(lock->lock_count > 0);
72 lock->lock_count--;
73 } else {
74 lock->pid = 0;
75 coap_mutex_unlock(&lock->mutex);
76 }
77}
78
79int
80coap_lock_lock_func(coap_lock_t *lock) {
81 if (!coap_started) {
82 /* libcoap not initialized with coap_startup() */
83 return 0;
84 }
85 /*
86 * Some OS do not have support for coap_mutex_trylock() so
87 * cannot use that here and have to rely on lock-pid being stable
88 */
89 if (lock->in_callback && coap_thread_pid == lock->pid) {
90 lock->lock_count++;
91 assert(lock->in_callback == lock->lock_count);
92 return 1;
93 }
94 coap_mutex_lock(&lock->mutex);
95 /* Just got the lock, so should not be in a locked callback */
96 assert(!lock->in_callback);
97 lock->pid = coap_thread_pid;
98 return 1;
99}
100#endif /* ! COAP_THREAD_RECURSIVE_CHECK */
101
102#if !WITH_LWIP
103extern COAP_THREAD_LOCAL_VAR volatile int coap_thread_quit;
104
105/* Visible to only this thread */
106COAP_THREAD_LOCAL_VAR uint32_t thread_no = 0;
107/* Visible across all threads */
108uint32_t max_thread_no = 0;
109
110typedef struct {
111 coap_context_t *context;
112 uint32_t thread_no;
113} coap_thread_param_t;
114
115static void *
116coap_io_process_worker_thread(void *arg) {
117 coap_thread_param_t *thread_param = (coap_thread_param_t *)arg;
118 coap_context_t *context = thread_param->context;
119#if (COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG)
120 long unsigned int thread_pid = (long unsigned int)coap_thread_pid;
121#endif
122
123 thread_no = thread_param->thread_no;
124 coap_free_type(COAP_STRING, thread_param);
125
126 coap_log_debug("Thread %lx start\n", thread_pid);
127
128 while (!coap_thread_quit) {
129 int result;
130
131 coap_lock_lock(return 0);
132#ifndef __ZEPHYR__
133 result = coap_io_process_lkd(context, COAP_IO_WAIT);
134#else /* __ZEPHYR__ */
135 result = coap_io_process_lkd(context, 1000);
136#endif /* __ZEPHYR__ */
138 if (result < 0 || coap_thread_quit)
139 break;
140 }
142 coap_log_debug("Thread %lx exit\n", thread_pid);
143 return 0;
144}
145
146#if defined(HAVE_SIGNAL_H) || defined(__ZEPHYR__)
147#include <signal.h>
148#endif /* HAVE_SIGNAL_H || __ZEPHYR__ */
149
150/* sighandler_t is not defined is all OS */
151typedef void (*coap_sig_handler_t)(int);
152
153static coap_sig_handler_t old_sigint_handler = SIG_IGN;
154
155static void
156coap_thread_sigint_handler(int signum) {
158 if (old_sigint_handler != SIG_IGN && old_sigint_handler != SIG_DFL) {
159 old_sigint_handler(signum);
160 }
161}
162
163#ifndef _WIN32
164typedef void (*coap_sig_action_t)(int, siginfo_t *, void *);
165static coap_sig_action_t old_sigint_action = NULL;
166
167static void
168coap_thread_sigint_action(int signum, siginfo_t *siginfo, void *ptr) {
170 if (old_sigint_action != NULL) {
171 old_sigint_action(signum, siginfo, ptr);
172 }
173}
174#endif
176/* Must be run upder the protection of mutex m_io_threads */
177static void
178coap_io_process_kill_threads(coap_context_t *context) {
179 uint32_t i;
180
181#ifndef __ZEPHYR__
182 for (i = 0; i < context->thread_id_count ; i++) {
183 int s = pthread_kill(context->thread_id[i], COAP_THREAD_KILL_SIG);
184 if (s != 0) {
185 coap_log_err("thread kill failure\n");
186 }
187 }
188#else /* __ZEPHYR__ */
191#endif /* __ZEPHYR__ */
192
193 for (i = 0; i < context->thread_id_count ; i++) {
194 void *retval;
195 int s = pthread_join(context->thread_id[i], &retval);
196
197 if (s != 0) {
198 coap_log_err("thread join failure\n");
199 }
200 }
201 coap_free_type(COAP_STRING, context->thread_id);
202 context->thread_id = NULL;
203 context->thread_id_count = 0;
204}
205
206int
207coap_io_process_configure_threads(coap_context_t *context, uint32_t thread_count) {
208 uint32_t i;
209 uint32_t base_thread;
210
211 if (thread_count == 0)
212 return 0;
213
214 coap_mutex_lock(&m_io_threads);
215 /* Need to make sure it is non zero incase coap_log*() is called */
216 if (thread_no == 0) {
217 thread_no = ++max_thread_no;
218 }
219#ifdef _WIN32
220 old_sigint_handler = signal(COAP_THREAD_KILL_SIG, coap_thread_sigint_handler);
221#else /* ! _WIN32 */
222 struct sigaction oldact;
223 struct sigaction newact;
224
225 /* Get the old handler / action */
226 if (sigaction(COAP_THREAD_KILL_SIG, NULL, &oldact) != -1) {
227 if (oldact.sa_flags & SA_SIGINFO) {
228 if (oldact.sa_sigaction != coap_thread_sigint_action) {
229 memset(&newact, 0, sizeof(newact));
230 sigemptyset(&newact.sa_mask);
231 newact.sa_sigaction = coap_thread_sigint_action;
232 newact.sa_flags = SA_SIGINFO;
233 if (sigaction(COAP_THREAD_KILL_SIG, &newact, &oldact) != -1) {
234 old_sigint_action = oldact.sa_sigaction;
235 }
236 }
237 } else {
238 if (oldact.sa_handler != coap_thread_sigint_handler) {
239 memset(&newact, 0, sizeof(newact));
240 sigemptyset(&newact.sa_mask);
241 newact.sa_handler = coap_thread_sigint_handler;
242 newact.sa_flags = 0;
243 if (sigaction(COAP_THREAD_KILL_SIG, &newact, &oldact) != -1) {
244 old_sigint_handler = oldact.sa_handler;
245 }
246 }
247 }
248 }
249#endif /* ! _WIN32 */
250
251 if (context->thread_id_count) {
252 coap_io_process_kill_threads(context);
253 }
254
255 base_thread = max_thread_no + 1;
256 max_thread_no = base_thread + thread_count - 1;
257 context->thread_id = coap_malloc_type(COAP_STRING, thread_count * sizeof(pthread_t));
258 if (!context->thread_id) {
259 coap_log_err("thread start up memory allocate failure\n");
260 coap_mutex_unlock(&m_io_threads);
261 return 0;
262 }
263 for (i = 0; i < thread_count ; i++) {
264 coap_thread_param_t *thread_param = coap_malloc_type(COAP_STRING, sizeof(coap_thread_param_t));
265 int s;
266
267 thread_param->context = context;
268 thread_param->thread_no = i + base_thread;
269 s = pthread_create(&context->thread_id[i], NULL,
270 &coap_io_process_worker_thread, thread_param);
271 if (s != 0) {
272 coap_log_err("thread start up failure (%s)\n", coap_socket_strerror());
273 coap_mutex_unlock(&m_io_threads);
274 return 0;
275 }
276 context->thread_id_count++;
277 }
278 coap_mutex_unlock(&m_io_threads);
279 return 1;
280}
281
282COAP_API void
287}
288
289void
292 coap_mutex_lock(&m_io_threads);
293 /* Need to make sure it is non zero incase coap_log*() is called */
294 if (thread_no == 0) {
295 thread_no = ++max_thread_no;
296 }
297
298 if (context->thread_id_count) {
299 coap_io_process_kill_threads(context);
300 }
301
302 coap_mutex_unlock(&m_io_threads);
303 coap_lock_lock(return);
304}
305#endif /* !WITH_LWIP */
306
307#else /* ! COAP_THREAD_SAFE */
308
309int
310coap_io_process_configure_threads(coap_context_t *context, uint32_t thread_count) {
311 (void)context;
312 (void)thread_count;
313 return 0;
314}
315
316void
318 (void)context;
319}
320
321#endif /* ! COAP_THREAD_SAFE */
const char * coap_socket_strerror(void)
Definition coap_io.c:940
COAP_THREAD_LOCAL_VAR volatile int coap_thread_quit
Library specific build wrapper for coap_internal.h.
#define COAP_API
@ COAP_STRING
Definition coap_mem.h:33
void * coap_malloc_type(coap_memory_tag_t type, size_t size)
Allocates a chunk of size bytes and returns a pointer to the newly allocated memory.
void coap_free_type(coap_memory_tag_t type, void *p)
Releases the memory that was allocated by coap_malloc_type().
#define coap_thread_pid
#define coap_mutex_unlock(a)
#define coap_mutex_trylock(a)
#define coap_mutex_lock(a)
int coap_started
Definition coap_net.c:5237
#define NULL
Definition coap_option.h:30
void coap_io_process_remove_threads_lkd(coap_context_t *context)
Release the coap_io_process() worker threads.
int coap_io_process_lkd(coap_context_t *ctx, uint32_t timeout_ms)
The main I/O processing function.
int coap_io_process_configure_threads(coap_context_t *context, uint32_t thread_count)
Configure a defined number of threads to do the alternate coap_io_process() work with traffic load ba...
void coap_io_process_remove_threads(coap_context_t *context)
Release the coap_io_process() worker threads.
#define COAP_IO_WAIT
Definition coap_net.h:840
void coap_send_recv_terminate(void)
Terminate any active coap_send_recv() sessions.
Definition coap_net.c:2198
void coap_dtls_thread_shutdown(void)
Close down the underlying (D)TLS Library layer.
Definition coap_notls.c:171
coap_mutex_t coap_lock_t
#define coap_lock_unlock()
Dummy for no thread-safe code.
#define coap_lock_lock(failed)
Dummy for no thread-safe code.
#define coap_log_debug(...)
Definition coap_debug.h:126
#define coap_log_alert(...)
Definition coap_debug.h:90
#define coap_log_err(...)
Definition coap_debug.h:102
#define COAP_THREAD_LOCAL_VAR
Definition libcoap.h:84
The CoAP stack's global state is stored in a coap_context_t object.