/src/libcoap/src/coap_io_posix.c
Line | Count | Source |
1 | | /* coap_io_posix.c -- Network I/O functions for libcoap using Posix |
2 | | * |
3 | | * Copyright (C) 2012,2014,2016-2026 Olaf Bergmann <bergmann@tzi.org> and others |
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 | | |
11 | | /** |
12 | | * @file coap_io_posix.c |
13 | | * @brief Posix specific Network I/O functions |
14 | | */ |
15 | | |
16 | | #include "coap3/coap_libcoap_build.h" |
17 | | |
18 | | #if ! defined(WITH_LWIP) && ! defined(WITH_CONTIKI) && ! defined (RIOT_VERSION) |
19 | | |
20 | | #ifdef HAVE_STDIO_H |
21 | | # include <stdio.h> |
22 | | #endif |
23 | | #ifdef HAVE_UNISTD_H |
24 | | # include <unistd.h> |
25 | | #endif |
26 | | |
27 | | #ifndef __ZEPHYR__ |
28 | | #ifdef HAVE_SYS_SELECT_H |
29 | | # include <sys/select.h> |
30 | | #endif |
31 | | #ifdef HAVE_SYS_SOCKET_H |
32 | | # include <sys/socket.h> |
33 | | #endif |
34 | | #ifdef HAVE_SYS_IOCTL_H |
35 | | #include <sys/ioctl.h> |
36 | | #endif |
37 | | #ifdef HAVE_NETINET_IN_H |
38 | | # include <netinet/in.h> |
39 | | #endif |
40 | | #ifdef HAVE_SYS_UIO_H |
41 | | # include <sys/uio.h> |
42 | | #endif |
43 | | #ifdef _WIN32 |
44 | | #include <stdio.h> |
45 | | #endif /* _WIN32 */ |
46 | | #if COAP_EPOLL_SUPPORT |
47 | | #include <sys/epoll.h> |
48 | | #include <sys/timerfd.h> |
49 | | #ifdef HAVE_LIMITS_H |
50 | | #include <limits.h> |
51 | | #endif |
52 | | #endif /* COAP_EPOLL_SUPPORT */ |
53 | | #else /* __ZEPHYR__ */ |
54 | | #include <sys/ioctl.h> |
55 | | #include <sys/select.h> |
56 | | #endif /* __ZEPHYR__ */ |
57 | | |
58 | | #if COAP_EPOLL_SUPPORT |
59 | | void |
60 | | coap_epoll_ctl_add(coap_socket_t *sock, |
61 | | uint32_t events, |
62 | 4.94k | const char *func) { |
63 | 4.94k | int ret; |
64 | 4.94k | struct epoll_event event; |
65 | 4.94k | coap_context_t *context; |
66 | | |
67 | | #if COAP_MAX_LOGGING_LEVEL < _COAP_LOG_ERR |
68 | | (void)func; |
69 | | #endif |
70 | | |
71 | 4.94k | if (sock == NULL) |
72 | 0 | return; |
73 | | |
74 | 4.94k | #if COAP_SERVER_SUPPORT |
75 | 4.94k | context = sock->session ? sock->session->context : |
76 | 4.94k | sock->endpoint ? sock->endpoint->context : NULL; |
77 | | #else /* ! COAP_SERVER_SUPPORT */ |
78 | | context = sock->session ? sock->session->context : NULL; |
79 | | #endif /* ! COAP_SERVER_SUPPORT */ |
80 | 4.94k | if (context == NULL) |
81 | 0 | return; |
82 | | |
83 | | /* Needed if running 32bit as ptr is only 32bit */ |
84 | 4.94k | memset(&event, 0, sizeof(event)); |
85 | 4.94k | event.events = events; |
86 | 4.94k | event.data.ptr = sock; |
87 | | |
88 | 4.94k | ret = epoll_ctl(context->epfd, EPOLL_CTL_ADD, sock->fd, &event); |
89 | 4.94k | if (ret == -1) { |
90 | 0 | coap_log_err("%s: epoll_ctl ADD failed: %s (%d)\n", |
91 | 0 | func, |
92 | 0 | coap_socket_strerror(), errno); |
93 | 0 | } |
94 | 4.94k | } |
95 | | |
96 | | void |
97 | | coap_epoll_ctl_mod(coap_socket_t *sock, |
98 | | uint32_t events, |
99 | 3 | const char *func) { |
100 | 3 | int ret; |
101 | 3 | struct epoll_event event; |
102 | 3 | coap_context_t *context; |
103 | | |
104 | | #if COAP_MAX_LOGGING_LEVEL < _COAP_LOG_ERR |
105 | | (void)func; |
106 | | #endif |
107 | | |
108 | 3 | if (sock == NULL) |
109 | 0 | return; |
110 | | |
111 | 3 | #if COAP_SERVER_SUPPORT |
112 | 3 | context = sock->session ? sock->session->context : |
113 | 3 | sock->endpoint ? sock->endpoint->context : NULL; |
114 | | #else /* COAP_SERVER_SUPPORT */ |
115 | | context = sock->session ? sock->session->context : NULL; |
116 | | #endif /* COAP_SERVER_SUPPORT */ |
117 | 3 | if (context == NULL) |
118 | 0 | return; |
119 | | |
120 | 3 | event.events = events; |
121 | 3 | event.data.ptr = sock; |
122 | | |
123 | 3 | ret = epoll_ctl(context->epfd, EPOLL_CTL_MOD, sock->fd, &event); |
124 | 3 | if (ret == -1) { |
125 | | #if (COAP_MAX_LOGGING_LEVEL < COAP_LOG_ERR) |
126 | | (void)func; |
127 | | #endif |
128 | 0 | coap_log_err("%s: epoll_ctl MOD failed: %s (%d)\n", |
129 | 0 | func, |
130 | 0 | coap_socket_strerror(), errno); |
131 | 0 | } |
132 | 3 | } |
133 | | #endif /* COAP_EPOLL_SUPPORT */ |
134 | | |
135 | | COAP_API int |
136 | 27 | coap_io_process(coap_context_t *ctx, uint32_t timeout_ms) { |
137 | 27 | int ret; |
138 | | |
139 | 27 | coap_lock_lock(return 0); |
140 | 27 | ret = coap_io_process_lkd(ctx, timeout_ms); |
141 | 27 | coap_lock_unlock(); |
142 | 27 | return ret; |
143 | 27 | } |
144 | | |
145 | | int |
146 | 27 | coap_io_process_lkd(coap_context_t *ctx, uint32_t timeout_ms) { |
147 | 27 | return coap_io_process_with_fds_lkd(ctx, timeout_ms, 0, NULL, NULL, NULL); |
148 | 27 | } |
149 | | |
150 | | COAP_API int |
151 | | coap_io_process_with_fds(coap_context_t *ctx, uint32_t timeout_ms, |
152 | | int enfds, fd_set *ereadfds, fd_set *ewritefds, |
153 | 0 | fd_set *eexceptfds) { |
154 | 0 | int ret; |
155 | |
|
156 | 0 | coap_lock_lock(return 0); |
157 | 0 | ret = coap_io_process_with_fds_lkd(ctx, timeout_ms, enfds, ereadfds, ewritefds, |
158 | 0 | eexceptfds); |
159 | 0 | coap_lock_unlock(); |
160 | 0 | return ret; |
161 | 0 | } |
162 | | |
163 | | #if ! COAP_EPOLL_SUPPORT && COAP_THREAD_SAFE |
164 | | static unsigned int |
165 | | coap_io_prepare_fds(coap_context_t *ctx, |
166 | | int enfds, fd_set *ereadfds, fd_set *ewritefds, |
167 | | fd_set *eexceptfds, fd_set *readfds, fd_set *writefds, |
168 | | fd_set *exceptfds |
169 | | ) { |
170 | | coap_session_t *s, *stmp; |
171 | | unsigned int max_sockets = sizeof(ctx->sockets) / sizeof(ctx->sockets[0]); |
172 | | coap_fd_t nfds = 0; |
173 | | unsigned int i; |
174 | | |
175 | | ctx->num_sockets = 0; |
176 | | #if COAP_SERVER_SUPPORT |
177 | | coap_endpoint_t *ep; |
178 | | |
179 | | LL_FOREACH(ctx->endpoint, ep) { |
180 | | if (ep->sock.flags & (COAP_SOCKET_WANT_READ | COAP_SOCKET_WANT_WRITE | COAP_SOCKET_WANT_ACCEPT)) { |
181 | | if (ctx->num_sockets < max_sockets) |
182 | | ctx->sockets[ctx->num_sockets++] = &ep->sock; |
183 | | } |
184 | | SESSIONS_ITER(ep->sessions, s, stmp) { |
185 | | if (s->sock.flags & (COAP_SOCKET_WANT_READ|COAP_SOCKET_WANT_WRITE)) { |
186 | | if (ctx->num_sockets < max_sockets && !(s->sock.flags & COAP_SOCKET_SLAVE)) |
187 | | ctx->sockets[ctx->num_sockets++] = &s->sock; |
188 | | } |
189 | | } |
190 | | } |
191 | | #endif /* COAP_SERVER_SUPPORT */ |
192 | | #if COAP_CLIENT_SUPPORT |
193 | | SESSIONS_ITER(ctx->sessions, s, stmp) { |
194 | | if (s->sock.flags & (COAP_SOCKET_WANT_READ | |
195 | | COAP_SOCKET_WANT_WRITE | |
196 | | COAP_SOCKET_WANT_CONNECT)) { |
197 | | if (ctx->num_sockets < max_sockets && !(s->sock.flags & COAP_SOCKET_SLAVE)) |
198 | | ctx->sockets[ctx->num_sockets++] = &s->sock; |
199 | | } |
200 | | } |
201 | | #endif /* COAP_CLIENT_SUPPORT */ |
202 | | if (ereadfds) { |
203 | | *readfds = *ereadfds; |
204 | | nfds = enfds; |
205 | | } else { |
206 | | FD_ZERO(readfds); |
207 | | } |
208 | | if (ewritefds) { |
209 | | *writefds = *ewritefds; |
210 | | nfds = enfds; |
211 | | } else { |
212 | | FD_ZERO(writefds); |
213 | | } |
214 | | if (eexceptfds) { |
215 | | *exceptfds = *eexceptfds; |
216 | | nfds = enfds; |
217 | | } else { |
218 | | FD_ZERO(exceptfds); |
219 | | } |
220 | | for (i = 0; i < ctx->num_sockets; i++) { |
221 | | if (ctx->sockets[i]->fd + 1 > nfds) |
222 | | nfds = ctx->sockets[i]->fd + 1; |
223 | | if (ctx->sockets[i]->flags & COAP_SOCKET_WANT_READ) |
224 | | FD_SET(ctx->sockets[i]->fd, readfds); |
225 | | if (ctx->sockets[i]->flags & COAP_SOCKET_WANT_WRITE) |
226 | | FD_SET(ctx->sockets[i]->fd, writefds); |
227 | | #if !COAP_DISABLE_TCP |
228 | | if (ctx->sockets[i]->flags & COAP_SOCKET_WANT_ACCEPT) |
229 | | FD_SET(ctx->sockets[i]->fd, readfds); |
230 | | if (ctx->sockets[i]->flags & COAP_SOCKET_WANT_CONNECT) { |
231 | | FD_SET(ctx->sockets[i]->fd, writefds); |
232 | | FD_SET(ctx->sockets[i]->fd, exceptfds); |
233 | | } |
234 | | #endif /* !COAP_DISABLE_TCP */ |
235 | | } |
236 | | return nfds; |
237 | | } |
238 | | #endif /* ! COAP_EPOLL_SUPPORT && COAP_THREAD_SAFE */ |
239 | | |
240 | | int |
241 | | coap_io_process_with_fds_lkd(coap_context_t *ctx, uint32_t timeout_ms, |
242 | | int enfds, fd_set *ereadfds, fd_set *ewritefds, |
243 | 27 | fd_set *eexceptfds) { |
244 | 27 | coap_fd_t nfds = 0; |
245 | 27 | coap_tick_t before, now; |
246 | 27 | unsigned int timeout; |
247 | | #if ! COAP_EPOLL_SUPPORT |
248 | | struct timeval tv; |
249 | | int result; |
250 | | unsigned int i; |
251 | | #if ! COAP_CONSTRAINED_STACK |
252 | | fd_set readfds, writefds, exceptfds; |
253 | | #define COAP_READFDS readfds |
254 | | #define COAP_WRITEFDS writefds |
255 | | #define COAP_EXCEPTFDS exceptfds |
256 | | #else |
257 | | #define COAP_READFDS (ctx->readfds) |
258 | | #define COAP_WRITEFDS (ctx->writefds) |
259 | | #define COAP_EXCEPTFDS (ctx->exceptfds) |
260 | | #endif /* ! COAP_CONSTRAINED_STACK */ |
261 | | #endif /* ! COAP_EPOLL_SUPPORT */ |
262 | | |
263 | 27 | coap_lock_check_locked(); |
264 | 27 | coap_ticks(&before); |
265 | | |
266 | | #if ! COAP_EPOLL_SUPPORT |
267 | | |
268 | | timeout = coap_io_prepare_io_lkd(ctx, ctx->sockets, |
269 | | (sizeof(ctx->sockets) / sizeof(ctx->sockets[0])), |
270 | | &ctx->num_sockets, before); |
271 | | ctx->next_timeout = timeout ? timeout + before : 0; |
272 | | |
273 | | if (ereadfds) { |
274 | | COAP_READFDS = *ereadfds; |
275 | | nfds = enfds; |
276 | | } else { |
277 | | FD_ZERO(&COAP_READFDS); |
278 | | } |
279 | | if (ewritefds) { |
280 | | COAP_WRITEFDS = *ewritefds; |
281 | | nfds = enfds; |
282 | | } else { |
283 | | FD_ZERO(&COAP_WRITEFDS); |
284 | | } |
285 | | if (eexceptfds) { |
286 | | COAP_EXCEPTFDS = *eexceptfds; |
287 | | nfds = enfds; |
288 | | } else { |
289 | | FD_ZERO(&COAP_EXCEPTFDS); |
290 | | } |
291 | | for (i = 0; i < ctx->num_sockets; i++) { |
292 | | if (ctx->sockets[i]->fd + 1 > nfds) |
293 | | nfds = ctx->sockets[i]->fd + 1; |
294 | | if (ctx->sockets[i]->flags & COAP_SOCKET_WANT_READ) |
295 | | FD_SET(ctx->sockets[i]->fd, &COAP_READFDS); |
296 | | if (ctx->sockets[i]->flags & COAP_SOCKET_WANT_WRITE) |
297 | | FD_SET(ctx->sockets[i]->fd, &COAP_WRITEFDS); |
298 | | #if !COAP_DISABLE_TCP |
299 | | if (ctx->sockets[i]->flags & COAP_SOCKET_WANT_ACCEPT) |
300 | | FD_SET(ctx->sockets[i]->fd, &COAP_READFDS); |
301 | | if (ctx->sockets[i]->flags & COAP_SOCKET_WANT_CONNECT) { |
302 | | FD_SET(ctx->sockets[i]->fd, &COAP_WRITEFDS); |
303 | | FD_SET(ctx->sockets[i]->fd, &COAP_EXCEPTFDS); |
304 | | } |
305 | | #endif /* !COAP_DISABLE_TCP */ |
306 | | } |
307 | | |
308 | | if (timeout_ms == COAP_IO_NO_WAIT) { |
309 | | tv.tv_usec = 0; |
310 | | tv.tv_sec = 0; |
311 | | timeout = 1; |
312 | | } else if (timeout == 0 && timeout_ms == COAP_IO_WAIT) { |
313 | | ; |
314 | | } else { |
315 | | if (timeout == 0 || (timeout_ms != COAP_IO_WAIT && timeout_ms < timeout)) |
316 | | timeout = timeout_ms; |
317 | | tv.tv_usec = (timeout % 1000) * 1000; |
318 | | tv.tv_sec = (long)(timeout / 1000); |
319 | | } |
320 | | |
321 | | /* on Windows select will return an error if called without FDs */ |
322 | | #ifdef _WIN32 |
323 | | if (nfds > 0) { |
324 | | #endif /* _WIN32 */ |
325 | | /* Unlock so that other threads can lock/update ctx */ |
326 | | coap_lock_unlock(); |
327 | | |
328 | | result = select((int)nfds, &COAP_READFDS, &COAP_WRITEFDS, &COAP_EXCEPTFDS, |
329 | | timeout > 0 ? &tv : NULL); |
330 | | |
331 | | coap_lock_lock(return -1); |
332 | | #ifdef _WIN32 |
333 | | } else { |
334 | | if (timeout > 0) { |
335 | | DWORD milliseconds = tv.tv_sec * 1000 + tv.tv_usec / 1000; |
336 | | |
337 | | if (milliseconds > 2000) { |
338 | | /* Wait at most 2 seconds */ |
339 | | milliseconds = 2000; |
340 | | } |
341 | | if (milliseconds) { |
342 | | coap_lock_unlock(); |
343 | | Sleep(milliseconds); |
344 | | coap_lock_lock(return -1); |
345 | | } |
346 | | } |
347 | | result = 0; |
348 | | } |
349 | | #endif /* _WIN32 */ |
350 | | |
351 | | if (result < 0) { /* error */ |
352 | | #ifdef _WIN32 |
353 | | coap_win_error_to_errno(); |
354 | | #endif /* _WIN32 */ |
355 | | if (errno != EINTR) { |
356 | | #if COAP_THREAD_SAFE |
357 | | if (errno == EBADF) { |
358 | | coap_log_debug("select: %s\n", coap_socket_strerror()); |
359 | | goto all_over; |
360 | | } |
361 | | #endif /* COAP_THREAD_SAFE */ |
362 | | coap_log_err("select: %s\n", coap_socket_strerror()); |
363 | | return -1; |
364 | | } |
365 | | goto all_over; |
366 | | } |
367 | | #if COAP_THREAD_SAFE |
368 | | /* Need to refresh what is available to read / write etc. */ |
369 | | nfds = coap_io_prepare_fds(ctx, enfds, ereadfds, ewritefds, eexceptfds, |
370 | | &COAP_READFDS, &COAP_WRITEFDS, &COAP_EXCEPTFDS); |
371 | | #ifdef _WIN32 |
372 | | if (nfds > 0) { |
373 | | #endif /* _WIN32 */ |
374 | | tv.tv_usec = 0; |
375 | | tv.tv_sec = 0; |
376 | | result = select((int)nfds, &COAP_READFDS, &COAP_WRITEFDS, &COAP_EXCEPTFDS, &tv); |
377 | | if (result < 0) { /* error */ |
378 | | #ifdef _WIN32 |
379 | | coap_win_error_to_errno(); |
380 | | #endif /* _WIN32 */ |
381 | | if (errno != EINTR) { |
382 | | if (errno == EBADF) { |
383 | | coap_log_debug("select: %s\n", coap_socket_strerror()); |
384 | | goto all_over; |
385 | | } |
386 | | coap_log_err("select: %s\n", coap_socket_strerror()); |
387 | | return -1; |
388 | | } |
389 | | goto all_over; |
390 | | } |
391 | | #ifdef _WIN32 |
392 | | } |
393 | | #endif /* _WIN32 */ |
394 | | #endif /* COAP_THREAD_SAFE */ |
395 | | if (ereadfds) { |
396 | | *ereadfds = COAP_READFDS; |
397 | | } |
398 | | if (ewritefds) { |
399 | | *ewritefds = COAP_WRITEFDS; |
400 | | } |
401 | | if (eexceptfds) { |
402 | | *eexceptfds = COAP_EXCEPTFDS; |
403 | | } |
404 | | |
405 | | if (result > 0) { |
406 | | for (i = 0; i < ctx->num_sockets; i++) { |
407 | | if ((ctx->sockets[i]->flags & COAP_SOCKET_WANT_READ) && |
408 | | FD_ISSET(ctx->sockets[i]->fd, &COAP_READFDS)) |
409 | | ctx->sockets[i]->flags |= COAP_SOCKET_CAN_READ; |
410 | | #if !COAP_DISABLE_TCP |
411 | | if ((ctx->sockets[i]->flags & COAP_SOCKET_WANT_ACCEPT) && |
412 | | FD_ISSET(ctx->sockets[i]->fd, &COAP_READFDS)) |
413 | | ctx->sockets[i]->flags |= COAP_SOCKET_CAN_ACCEPT; |
414 | | if ((ctx->sockets[i]->flags & COAP_SOCKET_WANT_WRITE) && |
415 | | FD_ISSET(ctx->sockets[i]->fd, &COAP_WRITEFDS)) |
416 | | ctx->sockets[i]->flags |= COAP_SOCKET_CAN_WRITE; |
417 | | if ((ctx->sockets[i]->flags & COAP_SOCKET_WANT_CONNECT) && |
418 | | (FD_ISSET(ctx->sockets[i]->fd, &COAP_WRITEFDS) || |
419 | | FD_ISSET(ctx->sockets[i]->fd, &COAP_EXCEPTFDS))) |
420 | | ctx->sockets[i]->flags |= COAP_SOCKET_CAN_CONNECT; |
421 | | #endif /* !COAP_DISABLE_TCP */ |
422 | | } |
423 | | } |
424 | | |
425 | | coap_ticks(&now); |
426 | | coap_io_do_io_lkd(ctx, now); |
427 | | coap_ticks(&now); |
428 | | timeout = coap_io_prepare_io_lkd(ctx, ctx->sockets, |
429 | | (sizeof(ctx->sockets) / sizeof(ctx->sockets[0])), |
430 | | &ctx->num_sockets, now); |
431 | | ctx->next_timeout = timeout ? timeout + now : 0; |
432 | | |
433 | | #else /* COAP_EPOLL_SUPPORT */ |
434 | 27 | (void)ereadfds; |
435 | 27 | (void)ewritefds; |
436 | 27 | (void)eexceptfds; |
437 | 27 | (void)enfds; |
438 | | |
439 | 27 | timeout = coap_io_prepare_epoll_lkd(ctx, before); |
440 | | |
441 | 27 | do { |
442 | 27 | struct epoll_event events[COAP_MAX_EPOLL_EVENTS]; |
443 | 27 | int etimeout; |
444 | | |
445 | | /* Potentially adjust based on what the caller wants */ |
446 | 27 | if (timeout_ms == COAP_IO_NO_WAIT) { |
447 | | /* Need to return immediately from epoll_wait() */ |
448 | 0 | etimeout = 0; |
449 | 27 | } else if (timeout == 0 && timeout_ms == COAP_IO_WAIT) { |
450 | | /* |
451 | | * Nothing found in coap_io_prepare_epoll_lkd() and COAP_IO_WAIT set, |
452 | | * so wait forever in epoll_wait(). |
453 | | */ |
454 | 0 | etimeout = -1; |
455 | 27 | } else { |
456 | 27 | etimeout = timeout; |
457 | 27 | if (timeout == 0 || (timeout_ms != COAP_IO_WAIT && timeout_ms < timeout)) |
458 | 27 | etimeout = timeout_ms; |
459 | 27 | if (etimeout < 0) { |
460 | | /* |
461 | | * If timeout > INT_MAX, epoll_wait() cannot wait longer than this as |
462 | | * it has int timeout parameter |
463 | | */ |
464 | 0 | etimeout = INT_MAX; |
465 | 0 | } |
466 | 27 | } |
467 | | |
468 | | /* Unlock so that other threads can lock/update ctx */ |
469 | 27 | coap_lock_unlock(); |
470 | | |
471 | 27 | nfds = epoll_wait(ctx->epfd, events, COAP_MAX_EPOLL_EVENTS, etimeout); |
472 | 27 | if (nfds < 0) { |
473 | 0 | if (errno != EINTR) { |
474 | 0 | coap_log_err("epoll_wait: unexpected error: %s (%d)\n", |
475 | 0 | coap_socket_strerror(), nfds); |
476 | 0 | } |
477 | 0 | coap_lock_lock(return -1); |
478 | 0 | break; |
479 | 0 | } |
480 | | |
481 | 27 | coap_lock_lock(return -1); |
482 | | #if COAP_THREAD_SAFE |
483 | | /* Need to refresh what is available to read / write etc. */ |
484 | | nfds = epoll_wait(ctx->epfd, events, COAP_MAX_EPOLL_EVENTS, 0); |
485 | | if (nfds < 0) { |
486 | | if (errno != EINTR) { |
487 | | coap_log_err("epoll_wait: unexpected error: %s (%d)\n", |
488 | | coap_socket_strerror(), nfds); |
489 | | } |
490 | | break; |
491 | | } |
492 | | #endif /* COAP_THREAD_SAFE */ |
493 | | |
494 | 27 | coap_io_do_epoll_lkd(ctx, events, nfds); |
495 | | |
496 | | /* |
497 | | * reset to COAP_IO_NO_WAIT (which causes etimeout to become 0) |
498 | | * incase we have to do another iteration |
499 | | * (COAP_MAX_EPOLL_EVENTS insufficient) |
500 | | */ |
501 | 27 | timeout_ms = COAP_IO_NO_WAIT; |
502 | | |
503 | | /* Keep retrying until less than COAP_MAX_EPOLL_EVENTS are returned */ |
504 | 27 | } while (nfds == COAP_MAX_EPOLL_EVENTS); |
505 | | |
506 | 27 | #endif /* COAP_EPOLL_SUPPORT */ |
507 | 27 | #if COAP_SERVER_SUPPORT |
508 | 27 | coap_expire_cache_entries(ctx); |
509 | 27 | #endif /* COAP_SERVER_SUPPORT */ |
510 | 27 | #if COAP_ASYNC_SUPPORT |
511 | | /* Check to see if we need to send off any Async requests as delay might |
512 | | have been updated */ |
513 | 27 | coap_ticks(&now); |
514 | 27 | coap_check_async(ctx, now, NULL); |
515 | 27 | #endif /* COAP_ASYNC_SUPPORT */ |
516 | | |
517 | | #if ! COAP_EPOLL_SUPPORT |
518 | | all_over: |
519 | | #endif /* COAP_EPOLL_SUPPORT */ |
520 | 27 | coap_ticks(&now); |
521 | 27 | return (int)(((now - before) * 1000) / COAP_TICKS_PER_SECOND); |
522 | 27 | } |
523 | | |
524 | | COAP_THREAD_LOCAL_VAR volatile int coap_thread_quit = 0; |
525 | | |
526 | | void |
527 | 0 | coap_io_process_terminate_loop(void) { |
528 | 0 | coap_send_recv_terminate(); |
529 | 0 | coap_thread_quit = 1; |
530 | 0 | } |
531 | | |
532 | | COAP_API int |
533 | | coap_io_process_loop(coap_context_t *context, |
534 | | coap_io_process_thread_t main_loop_code, |
535 | | void *main_loop_code_arg, uint32_t timeout_ms, |
536 | 0 | uint32_t thread_count) { |
537 | 0 | int ret; |
538 | |
|
539 | 0 | if (!context) |
540 | 0 | return 0; |
541 | 0 | coap_lock_lock(return 0); |
542 | 0 | ret = coap_io_process_loop_lkd(context, main_loop_code, |
543 | 0 | main_loop_code_arg, timeout_ms, |
544 | 0 | thread_count); |
545 | 0 | coap_lock_unlock(); |
546 | 0 | return ret; |
547 | 0 | } |
548 | | |
549 | | int |
550 | | coap_io_process_loop_lkd(coap_context_t *context, |
551 | | coap_io_process_thread_t main_loop_code, |
552 | | void *main_loop_code_arg, uint32_t timeout_ms, |
553 | 0 | uint32_t thread_count) { |
554 | 0 | int ret = 0;; |
555 | |
|
556 | | #if COAP_THREAD_SAFE |
557 | | if (thread_count > 1) { |
558 | | if (!coap_io_process_configure_threads(context, thread_count - 1)) |
559 | | return 0; |
560 | | } |
561 | | #else /* COAP_THREAD_SAFE */ |
562 | 0 | thread_count = 1; |
563 | 0 | #endif /* COAP_THREAD_SAFE */ |
564 | 0 | while (!coap_thread_quit) { |
565 | 0 | if (main_loop_code) { |
566 | 0 | coap_tick_t begin, end; |
567 | 0 | uint32_t used_ms; |
568 | |
|
569 | 0 | coap_ticks(&begin); |
570 | | /* |
571 | | * main_loop_codecode should not be blocking for any time, and not calling |
572 | | * coap_io_process(). |
573 | | */ |
574 | 0 | coap_lock_callback_release(main_loop_code(main_loop_code_arg), |
575 | | /* On re-lock failure */ |
576 | 0 | ret = 0; break); |
577 | | /* |
578 | | * Need to delay for the remainder of timeout_ms. In case main_loop_code() |
579 | | * is time sensitive (e.g Observe subscription to /time), delay to the |
580 | | * start of the a second boundary |
581 | | */ |
582 | 0 | coap_ticks(&end); |
583 | 0 | used_ms = (uint32_t)(end - begin) * 1000 / COAP_TICKS_PER_SECOND; |
584 | 0 | if (timeout_ms == COAP_IO_NO_WAIT || timeout_ms == COAP_IO_WAIT) { |
585 | 0 | ret = coap_io_process_lkd(context, timeout_ms); |
586 | 0 | } else if (timeout_ms > used_ms) { |
587 | | /* Wait for remaining time rounded up to next second start */ |
588 | 0 | coap_tick_t next_time = end + (timeout_ms - used_ms) * COAP_TICKS_PER_SECOND / 1000; |
589 | 0 | unsigned int next_sec_us; |
590 | 0 | unsigned int next_sec_ms; |
591 | |
|
592 | 0 | next_sec_us = (timeout_ms - used_ms) * 1000000 / COAP_TICKS_PER_SECOND + 1000000 - |
593 | 0 | (coap_ticks_to_rt_us(next_time) % 1000000); |
594 | 0 | next_sec_ms = (next_sec_us + 999) / 1000; |
595 | 0 | if (next_sec_ms > timeout_ms && next_sec_ms > 1000) |
596 | 0 | next_sec_ms -= 1000; |
597 | 0 | ret = coap_io_process_lkd(context, next_sec_ms ? next_sec_ms : 1); |
598 | 0 | } else { |
599 | | /* timeout_ms has expired */ |
600 | 0 | ret = coap_io_process_lkd(context, COAP_IO_NO_WAIT); |
601 | 0 | } |
602 | |
|
603 | 0 | if (thread_count == 1) { |
604 | | /* |
605 | | * Need to delay if only one thread until the remainder of |
606 | | * timeout_ms is used up. Otherwise, another thread will be |
607 | | * waiting on coap_io_process() to do any input / timeout work. |
608 | | */ |
609 | 0 | coap_ticks(&end); |
610 | 0 | used_ms = (uint32_t)(end - begin) * 1000 / COAP_TICKS_PER_SECOND; |
611 | 0 | if (timeout_ms > 0 && timeout_ms < used_ms) { |
612 | 0 | ret = coap_io_process_lkd(context, used_ms - timeout_ms); |
613 | 0 | } else { |
614 | 0 | ret = coap_io_process_lkd(context, COAP_IO_NO_WAIT); |
615 | 0 | } |
616 | 0 | } |
617 | 0 | } else { |
618 | 0 | ret = coap_io_process_lkd(context, timeout_ms); |
619 | 0 | } |
620 | | /* coap_io_process_lkd() can return 0 */ |
621 | 0 | if (ret >= 0) |
622 | 0 | ret = 1; |
623 | |
|
624 | 0 | if (ret < 0) { |
625 | 0 | ret = 0; |
626 | 0 | break; |
627 | 0 | } |
628 | 0 | } |
629 | | #if COAP_THREAD_SAFE |
630 | | coap_io_process_remove_threads_lkd(context); |
631 | | #endif /* COAP_THREAD_SAFE */ |
632 | 0 | coap_thread_quit = 0; |
633 | 0 | return ret; |
634 | 0 | } |
635 | | |
636 | | #else /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */ |
637 | | |
638 | | #ifdef __clang__ |
639 | | /* Make compilers happy that do not like empty modules. As this function is |
640 | | * never used, we ignore -Wunused-function at the end of compiling this file |
641 | | */ |
642 | | #pragma GCC diagnostic ignored "-Wunused-function" |
643 | | #endif |
644 | | static inline void |
645 | | dummy(void) { |
646 | | } |
647 | | |
648 | | #endif /* WITH_LWIP || WITH_CONTIKI || RIOT_VERSION */ |