Coverage Report

Created: 2026-05-12 06:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 */