libcoap 4.3.5-develop-5a5330d
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-2025 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(const char *file, int line) {
22 assert(coap_thread_pid == global_lock.pid);
23 if (global_lock.in_callback) {
24 assert(global_lock.lock_count > 0);
25 global_lock.lock_count--;
26 } else {
27 global_lock.pid = 0;
28 global_lock.unlock_file = file;
29 global_lock.unlock_line = line;
30 coap_mutex_unlock(&global_lock.mutex);
31 }
32}
33
34int
35coap_lock_lock_func(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(&global_lock.mutex)) {
41 if (coap_thread_pid == global_lock.pid) {
42 /* This thread locked the mutex */
43 if (global_lock.in_callback) {
44 /* This is called from within an app callback */
45 global_lock.lock_count++;
46 assert(global_lock.in_callback == global_lock.lock_count);
47 return 1;
48 } else {
49 coap_log_alert("Thread Deadlock: Last %s: %u, this %s: %u\n",
50 global_lock.lock_file, global_lock.lock_line, file, line);
51 assert(0);
52 }
53 }
54 /* Wait for the other thread to unlock */
55 coap_mutex_lock(&global_lock.mutex);
56 }
57 /* Just got the lock, so should not be in a locked callback */
58 assert(!global_lock.in_callback);
59 global_lock.pid = coap_thread_pid;
60 global_lock.lock_file = file;
61 global_lock.lock_line = line;
62 return 1;
63}
64
65#else /* ! COAP_THREAD_RECURSIVE_CHECK */
66
67void
68coap_lock_unlock_func(void) {
69 assert(coap_thread_pid == global_lock.pid);
70 if (global_lock.in_callback) {
71 assert(global_lock.lock_count > 0);
72 global_lock.lock_count--;
73 } else {
74 global_lock.pid = 0;
75 coap_mutex_unlock(&global_lock.mutex);
76 }
77}
78
79int
80coap_lock_lock_func(void) {
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 (global_lock.in_callback && coap_thread_pid == global_lock.pid) {
90 global_lock.lock_count++;
91 assert(global_lock.in_callback == global_lock.lock_count);
92 return 1;
93 }
94 coap_mutex_lock(&global_lock.mutex);
95 /* Just got the lock, so should not be in a locked callback */
96 assert(!global_lock.in_callback);
97 global_lock.pid = coap_thread_pid;
98 return 1;
99}
100#endif /* ! COAP_THREAD_RECURSIVE_CHECK */
101
102#if !WITH_LWIP
103extern volatile int coap_thread_quit;
104static pthread_t *thread_id = NULL;
105static uint32_t thread_id_count = 0;
106
107/* Visible to only this thread */
108COAP_THREAD_LOCAL_VAR uint32_t thread_no = 0;
109/* Visible across all threads */
110uint32_t max_thread_no = 0;
111
112typedef struct {
113 coap_context_t *context;
114 uint32_t thread_no;
115} coap_thread_param_t;
116
117static void *
118coap_io_process_worker_thread(void *arg) {
119 coap_thread_param_t *thread_param = (coap_thread_param_t *)arg;
120 coap_context_t *context = thread_param->context;
121#if (COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG)
122 long unsigned int thread_pid = (long unsigned int)coap_thread_pid;
123#endif
124
125 thread_no = thread_param->thread_no;
126 coap_free_type(COAP_STRING, thread_param);
127
128 coap_log_debug("Thread %lx start\n", thread_pid);
129
130 while (!coap_thread_quit) {
131 int result;
132
133 coap_lock_lock(return 0);
134#ifndef __ZEPHYR__
135 result = coap_io_process_lkd(context, COAP_IO_WAIT);
136#else /* __ZEPHYR__ */
137 result = coap_io_process_lkd(context, 1000);
138#endif /* __ZEPHYR__ */
140 if (result < 0 || coap_thread_quit)
141 break;
142 }
143 coap_log_debug("Thread %lx exit\n", thread_pid);
144 return 0;
145}
146
147int
148coap_io_process_configure_threads(coap_context_t *context, uint32_t thread_count) {
149 uint32_t i;
150
151 coap_mutex_lock(&m_io_threads);
152
153 thread_no = 1;
154 max_thread_no = 1 + thread_count;
155 coap_free_type(COAP_STRING, thread_id);
156 thread_id = coap_malloc_type(COAP_STRING, thread_count * sizeof(pthread_t));
157 if (!thread_id) {
158 coap_log_err("thread start up memory allocate failure\n");
159 coap_mutex_unlock(&m_io_threads);
160 return 0;
161 }
162 for (i = 0; i < thread_count ; i++) {
163 coap_thread_param_t *thread_param = coap_malloc_type(COAP_STRING, sizeof(coap_thread_param_t));
164 int s;
165
166 thread_param->context = context;
167 thread_param->thread_no = i + 2;
168 s = pthread_create(&thread_id[i], NULL,
169 &coap_io_process_worker_thread, thread_param);
170 if (s != 0) {
171 coap_log_err("thread start up failure (%s)\n", coap_socket_strerror());
172 coap_mutex_unlock(&m_io_threads);
173 return 0;
174 }
175 thread_id_count++;
176 }
177 coap_mutex_unlock(&m_io_threads);
178 return 1;
179}
180
181#ifdef HAVE_SIGNAL_H
182#include <signal.h>
183#endif /* HAVE_SIGNAL_H */
184void
186 uint32_t i;
187
188 (void)context;
189
191 coap_mutex_lock(&m_io_threads);
192
193#ifndef __ZEPHYR__
194 for (i = 0; i < thread_id_count ; i++) {
195 int s = pthread_kill(thread_id[i], SIGINT);
196 if (s != 0) {
197 coap_log_err("thread kill failure\n");
198 }
199 }
200#else /* __ZEPHYR__ */
203#endif /* __ZEPHYR__ */
204
205 for (i = 0; i < thread_id_count ; i++) {
206 void *retval;
207 int s = pthread_join(thread_id[i], &retval);
208 if (s != 0) {
209 coap_log_err("thread join failure\n");
210 }
211 }
212 coap_free_type(COAP_STRING, thread_id);
213 thread_id = NULL;
214 thread_id_count = 0;
215
216 coap_mutex_unlock(&m_io_threads);
217 coap_lock_lock(return);
218}
219#endif /* !WITH_LWIP */
220
221#else /* ! COAP_THREAD_SAFE */
222
223int
224coap_io_process_configure_threads(coap_context_t *context, uint32_t thread_count) {
225 (void)context;
226 (void)thread_count;
227 return 0;
228}
229
230void
232 (void)context;
233}
234
235#endif /* ! COAP_THREAD_SAFE */
const char * coap_socket_strerror(void)
Definition coap_io.c:847
volatile int coap_thread_quit
Library specific build wrapper for coap_internal.h.
@ COAP_STRING
Definition coap_mem.h:34
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:5119
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:825
void coap_send_recv_terminate(void)
Terminate any active coap_send_recv() sessions.
Definition coap_net.c:2115
#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.