libcoap 4.3.5-develop-1ba3158
Loading...
Searching...
No Matches
Work(3)
coap_io_loop

SYNOPSIS

#include <coap3/coap.h>

int coap_io_process_loop(coap_context_t *context, coap_io_process_thread_t main_loop_code, void *main_loop_code_arg, uint32_t timeout_ms, uint32_t thread_count);

void coap_io_process_terminate_loop(void);

int coap_io_process_configure_threads(coap_context_t *context, uint32_t thread_count);

void coap_io_process_remove_threads(coap_context_t *context);

For specific (D)TLS library support, link with -lcoap-3-notls, -lcoap-3-gnutls, -lcoap-3-openssl, -lcoap-3-mbedtls, -lcoap-3-wolfssl or -lcoap-3-tinydtls. Otherwise, link with -lcoap-3 to get the default (D)TLS library support.

DESCRIPTION

This man page focuses on setting up and supporting multiple threads, each one invoking coap_io_process(3) in a loop.

Each thread can receive a packet (a different packet for each thread), call the appropriate application call-back handler and potentially be spending time of consequence in that handler without blocking the reciept of other input traffic.

These functions should be called from the main thread. It is assumed that thread-safe code has been enabled.

FUNCTIONS

Function: coap_io_process_loop()

The coap_io_process_loop() function is used to set up additional threads that are in a loop just calling coap_io_process(3) with COAP_IO_WAIT. These threads are terminated when coap_io_process_terminate_loop() is called. The thread calling coap_io_process_loop() will also be in a separate loop that is calling coap_io_process(3), optionally calling main_loop_code (if not NULL) with argument main_loop_code_arg. For the thread calling coap_io_process_loop(), it will call main_loop_code at least every timeout_ms milli-secs, the call to main_loop_code start time aligned to the nearest second.

context defines the context to associate the threads with. thread_count is the number of threads to be running coap_io_process(3) which includes the coap_io_process_loop() calling thread in the count.

Function: coap_io_process_terminate_loop()

The coap_io_process_terminate_loop() function is used to terminate any added threads running under the control of coap_io_process_loop() and causing the thread that called coap_io_process_loop() to return.

Function: coap_io_process_configure_threads()

The coap_io_process_configure_threads() function is used to set up an additional thread_count threads for context. Usually coap_io_process_loop() would be called, but can be used to wrap with coap_io_process_remove_threads() a complex version of main_loop_code.

coap_io_process_loop() uses coap_io_process_configure_threads() and coap_io_process_remove_threads() to wrap the call to main_loop_code.

Function: coap_io_process_remove_threads()

The coap_io_process_remove_threads() function is used stop and remove threads created by coap_io_process_configure_threads() for context.

RETURN VALUES

coap_io_process_loop(), coap_io_process_configure_threads() return 1 on success else 0 on failure.

EXAMPLES

coap_io_process_loop()

#include <inttypes.h>
#include <stdio.h>
#include <signal.h>
#include <coap3/coap.h>
#include <coap3/coap_defines.h>

#if COAP_THREAD_SAFE
/* Define the number of coap_io_process() threads required */
#ifndef NUM_SERVER_THREADS
#define NUM_SERVER_THREADS 3
#endif /* NUM_SERVER_THREADS */
#endif /* COAP_THREAD_SAFE */

static volatile int quit = 0;
coap_resource_t *time_resource;
static time_t my_clock_base = 0;

/* SIGINT handler: set quit to 1 for graceful termination */
static void
handle_sigint(int signum COAP_UNUSED) {
  quit = 1;
#if NUM_SERVER_THREADS
  coap_io_process_terminate_loop();
#endif /* NUM_SERVER_THREADS */
}

static void
hnd_get_fetch_time(coap_resource_t *resource,
                   coap_session_t *session,
                   const coap_pdu_t *request,
                   const coap_string_t *query,
                   coap_pdu_t *response) {
  unsigned char buf[40];
  size_t len;
  time_t now;
  coap_tick_t t;
  (void)request;
  coap_pdu_code_t code = coap_pdu_get_code(request);
  size_t size;
  const uint8_t *data;
  coap_str_const_t *ticks = coap_make_str_const("ticks");

  if (my_clock_base) {

    /* calculate current time */
    coap_ticks(&t);
    now = my_clock_base + (t / COAP_TICKS_PER_SECOND);

    /* coap_get_data() sets size to 0 on error */
    (void)coap_get_data(request, &size, &data);

    if (code == COAP_REQUEST_CODE_GET && query != NULL &&
        coap_string_equal(query, ticks)) {
      /* parameter is in query, output ticks */
      len = snprintf((char *)buf, sizeof(buf), "%" PRIi64, (int64_t)now);
    } else if (code == COAP_REQUEST_CODE_FETCH && size == ticks->length &&
               memcmp(data, ticks->s, ticks->length) == 0) {
      /* parameter is in data, output ticks */
      len = snprintf((char *)buf, sizeof(buf), "%" PRIi64, (int64_t)now);
    } else {      /* output human-readable time */
      struct tm *tmp;
      tmp = gmtime(&now);
      if (!tmp) {
        /* If 'now' is not valid */
        coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
        return;
      } else {
        len = strftime((char *)buf, sizeof(buf), "%b %d %H:%M:%S", tmp);
      }
    }
    coap_pdu_set_code(response, COAP_RESPONSE_CODE_CONTENT);
    coap_add_data_large_response(resource, session, request, response,
                                 query, COAP_MEDIATYPE_TEXT_PLAIN, 1, 0,
                                 len,
                                 buf, NULL, NULL);
  } else {
    /* if my_clock_base was deleted, we pretend to have no such resource */
    coap_pdu_set_code(response, COAP_RESPONSE_CODE_NOT_FOUND);
  }
}

/*
 * This function sends off an Observe unsolicited response when the time
 * (based on seconds) changes.
 */
static void
do_time_observe_code(void *arg) {
  static coap_time_t t_last = 0;
  coap_time_t t_now;
  coap_tick_t now;

  (void)arg;
  coap_ticks(&now);
  t_now = coap_ticks_to_rt(now);
  if (t_now != t_last) {
    t_last = t_now;
    coap_resource_notify_observers(time_resource, NULL);
  }
}

static void
init_resources(coap_context_t *ctx) {
  coap_resource_t *r;

  my_clock_base = time(NULL);
  r = coap_resource_init(coap_make_str_const("time"), 0);
  coap_register_request_handler(r, COAP_REQUEST_GET, hnd_get_fetch_time);
  coap_register_request_handler(r, COAP_REQUEST_FETCH, hnd_get_fetch_time);
  coap_resource_set_get_observable(r, 1);

  coap_add_attr(r, coap_make_str_const("ct"), coap_make_str_const("0"), 0);
  coap_add_attr(r, coap_make_str_const("title"), coap_make_str_const("\"Internal Clock\""), 0);
  coap_add_attr(r, coap_make_str_const("rt"), coap_make_str_const("\"ticks\""), 0);
  coap_add_attr(r, coap_make_str_const("if"), coap_make_str_const("\"clock\""), 0);

  coap_add_resource(ctx, r);
  time_resource = r;
}

int
main(int argc, char **argv) {
  unsigned wait_ms;
  coap_context_t *ctx;

  (void)argc;
  (void)argv;

  ctx = coap_new_context(NULL);
  signal(SIGINT, handle_sigint);

  init_resources(ctx);

  /* Other general start up code */

  wait_ms = 1000;
#if NUM_SERVER_THREADS
  if (!coap_io_process_loop(ctx, do_time_observe_code, NULL, wait_ms,
                            NUM_SERVER_THREADS)) {
    coap_log_err("coap_io_process_loop: Startup failed\n");
  }
#else /* ! NUM_SERVER_THREADS */
  while (!quit) {
    unsigned int next_sec_ms;
    int result;
    coap_tick_t now;

    /*
     * result is time spent in coap_io_process()
     */
    result = coap_io_process(ctx, wait_ms);
    if (result < 0) {
      break;
    } else if (result && (unsigned)result < wait_ms) {
      /* decrement if there is a result wait time returned */
      wait_ms -= result;
    } else {
      /*
       * result == 0, or result >= wait_ms
       * (wait_ms could have decremented to a small value, below
       * the granularity of the timer in coap_io_process() and hence
       * result == 0)
       */
      wait_ms = 1000;
    }

    do_time_observe_code(NULL);

    /* need to wait until next second starts if wait_ms is too large */
    coap_ticks(&now);
    next_sec_ms = 1000 - (now % COAP_TICKS_PER_SECOND) *
                  1000 / COAP_TICKS_PER_SECOND;
    if (next_sec_ms && next_sec_ms < wait_ms)
      wait_ms = next_sec_ms;
  }
#endif /* ! NUM_SERVER_THREADS */

  /* General close down code */
}

SEE ALSO

coap_io_process(3)

FURTHER INFORMATION

See

"RFC7252: The Constrained Application Protocol (CoAP)"

for further information.

BUGS

Please raise an issue on GitHub at https://github.com/obgm/libcoap/issues to report any bugs.

Please raise a Pull Request at https://github.com/obgm/libcoap/pulls for any fixes.

AUTHORS

The libcoap project <libcoap-developers@lists.sourceforge.net>

Work(3)