From e46ad92cd3aece21e80b1902f9fba879c5799512 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Fri, 6 Dec 2019 15:45:35 +0100 Subject: [PATCH] sock_async_event: initial import of event-based implementation --- Makefile.dep | 5 + sys/Makefile | 3 + sys/Makefile.include | 4 + sys/include/net/sock/async/event.h | 260 ++++++++++++++++++++ sys/net/sock/async/event/Makefile | 3 + sys/net/sock/async/event/Makefile.include | 2 + sys/net/sock/async/event/sock_async_ctx.h | 71 ++++++ sys/net/sock/async/event/sock_async_event.c | 126 ++++++++++ 8 files changed, 474 insertions(+) create mode 100644 sys/include/net/sock/async/event.h create mode 100644 sys/net/sock/async/event/Makefile create mode 100644 sys/net/sock/async/event/Makefile.include create mode 100644 sys/net/sock/async/event/sock_async_ctx.h create mode 100644 sys/net/sock/async/event/sock_async_event.c diff --git a/Makefile.dep b/Makefile.dep index dde5823a2e..87481faa5c 100644 --- a/Makefile.dep +++ b/Makefile.dep @@ -801,6 +801,11 @@ ifneq (,$(filter vfs,$(USEMODULE))) endif endif +ifneq (,$(filter sock_async_event,$(USEMODULE))) + USEMODULE += sock_async + USEMODULE += event +endif + ifneq (,$(filter sock_dns,$(USEMODULE))) USEMODULE += sock_util USEMODULE += posix_headers diff --git a/sys/Makefile b/sys/Makefile index 91a09e7b1f..1a9b2301de 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -112,6 +112,9 @@ endif ifneq (,$(filter emcute,$(USEMODULE))) DIRS += net/application_layer/emcute endif +ifneq (,$(filter sock_async_event,$(USEMODULE))) + DIRS += net/sock/async/event +endif ifneq (,$(filter sock_util,$(USEMODULE))) DIRS += net/sock endif diff --git a/sys/Makefile.include b/sys/Makefile.include index 6d59155f99..b4b3a2dd2a 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -96,6 +96,10 @@ ifneq (,$(filter riotboot,$(FEATURES_USED))) include $(RIOTBASE)/sys/riotboot/Makefile.include endif +ifneq (,$(filter sock_async_event,$(USEMODULE))) + include $(RIOTBASE)/sys/net/sock/async/event/Makefile.include +endif + ifneq (,$(filter ssp,$(USEMODULE))) include $(RIOTBASE)/sys/ssp/Makefile.include endif diff --git a/sys/include/net/sock/async/event.h b/sys/include/net/sock/async/event.h new file mode 100644 index 0000000000..5047686df8 --- /dev/null +++ b/sys/include/net/sock/async/event.h @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup net_sock_async_event Asynchronous sock with event API + * @ingroup net_sock + * @brief Provides an implementation of asynchronous sock for + * @ref sys_event + * @experimental This API is still under development and should not be used + * in production yet. + * + * How To Use + * ---------- + * + * You need to [include](@ref including-modules) at least one module that + * implements a [`sock` API](@ref net_sock) (e.g. `gnrc_sock_udp` and + * `gnrc_sock_async` for the [GNRC](@ref net_gnrc) implementation using UDP) and + * the module `sock_async_event` in your application's Makefile. + * + * For the following example [`sock_udp`](@ref net_sock_udp) is used. It is + * however easily adaptable for other `sock` types: + * + * ### An asynchronous UDP Echo server using the event API + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include + * + * #include "net/sock/udp.h" + * #include "net/sock/async/event.h" + * + * event_queue_t queue; + * uint8_t buf[128]; + * + * void handler(sock_udp_t *sock, sock_async_flags_t type) + * { + * if (type & SOCK_ASYNC_MSG_RECV) { + * sock_udp_ep_t remote; + * ssize_t res; + * + * if ((res = sock_udp_recv(sock, buf, sizeof(buf), 0, + * &remote)) >= 0) { + * puts("Received a message"); + * if (sock_udp_send(sock, buf, res, &remote) < 0) { + * puts("Error sending reply"); + * } + * } + * } + * } + * + * int main(void) + * { + * sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + * sock_udp_t sock; + * + * local.port = 12345; + * + * if (sock_udp_create(&sock, &local, NULL, 0) < 0) { + * puts("Error creating UDP sock"); + * return 1; + * } + * + * event_queue_init(&queue); + * sock_udp_event_init(&sock, &queue, handler); + * event_loop(&queue); + * return 0; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Above you see a simple UDP echo server using @ref sys_event. Don't forget to + * also @ref including-modules "include" the IPv6 module of your networking + * implementation (e.g. `gnrc_ipv6_default` for @ref net_gnrc GNRC) and at least + * one network device. + * + * After including the header file for @ref net_sock_udp "UDP sock" and + * @ref net_sock_async_event "asynchronous handling", we create the event queue + * `queue` and allocate some buffer space `buf` to store the data received by + * the server: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include "net/sock/udp.h" + * #include "net/sock/async/event.h" + * + * event_queue_t queue; + * uint8_t buf[128]; + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * We then define an event handler in form of a function. The event handler + * checks if the triggering event was a receive event by checking the flags + * provided with sock_event_t::type. If it is a receive event it copies the + * incoming message to `buf` and its sender into `remote` using @ref + * sock_udp_recv(). Note, that we use @ref sock_udp_recv() non-blocking by + * setting `timeout` to 0. If an error occurs on receive, we just ignore it and + * return from the event handler. + * + * If we receive a message we use its `remote` to reply. In case of an error on + * send, we print an according message: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * void handler(sock_udp_t *sock, sock_async_flags_t type) + * { + * if (type & SOCK_ASYNC_MSG_RECV) { + * sock_udp_ep_t remote; + * ssize_t res; + * + * if ((res = sock_udp_recv(sock, buf, sizeof(buf), 0, + * &remote)) >= 0) { + * puts("Received a message"); + * if (sock_udp_send(sock, buf, res, &remote) < 0) { + * puts("Error sending reply"); + * } + * } + * } + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * To be able to listen for incoming packets we bind the `sock` by setting a + * local end point with a port (`12345` in this case). + * + * We then proceed to create the `sock`. It is bound to `local` and thus listens + * for UDP packets with @ref udp_hdr_t::dst_port "destination port" `12345`. + * Since we don't need any further configuration we set the flags to 0. + * In case of an error we stop the program: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + * sock_udp_t sock; + * + * local.port = 12345; + * + * if (sock_udp_create(&sock, &local, NULL, 0) < 0) { + * puts("Error creating UDP sock"); + * return 1; + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Finally, we initialize the event queue `queue`, initialize asynchronous event + * handling for `sock` using it and the previously defined event handler, and go + * into an endless loop to handle all occurring events on `queue`: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * event_queue_init(&queue); + * sock_udp_event_init(&sock, &queue, handler); + * event_loop(&queue); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @{ + * + * @file + * @brief Asynchronous sock using @ref sys_event definitions. + * + * @author Martine Lenders + * @author Kaspar Schleiser + */ +#ifndef NET_SOCK_ASYNC_EVENT_H +#define NET_SOCK_ASYNC_EVENT_H + +#include "event.h" +/* guard required since `sock_dtls_types.h` might not be provided */ +#ifdef MODULE_SOCK_DTLS +#include "net/sock/dtls.h" +#endif /* MODULE_SOCK_DTLS */ +#include "net/sock/ip.h" +#include "net/sock/tcp.h" +#include "net/sock/udp.h" +#include "net/sock/async.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* guard required since `sock_dtls_types.h` might not be provided */ +#if defined(MODULE_SOCK_DTLS) || defined(DOXYGEN) +/** + * @brief Makes a DTLS sock able to handle asynchronous events using + * @ref sys_event. + * + * @param[in] sock A DTLS sock object. + * @param[in] ev_queue The queue the events on @p sock will be added to. + * @param[in] handler The event handler function to call on an event on + * @p sock. + * + * @note Only available with module `sock_dtls`. + */ +void sock_dtls_event_init(sock_dtls_t *sock, event_queue_t *ev_queue, + sock_dtls_cb_t handler); +#endif /* defined(MODULE_SOCK_DTLS) || defined(DOXYGEN) */ + +#if defined(MODULE_SOCK_IP) || defined(DOXYGEN) +/** + * @brief Makes a raw IPv4/IPv6 sock able to handle asynchronous events using + * @ref sys_event. + * + * @param[in] sock A raw IPv4/IPv6 sock object. + * @param[in] ev_queue The queue the events on @p sock will be added to. + * @param[in] handler The event handler function to call on an event on + * @p sock. + * + * @note Only available with module `sock_ip`. + */ +void sock_ip_event_init(sock_ip_t *sock, event_queue_t *ev_queue, + sock_ip_cb_t handler); +#endif /* defined(MODULE_SOCK_IP) || defined(DOXYGEN) */ + +#if defined(MODULE_SOCK_TCP) || defined(DOXYGEN) +/** + * @brief Makes a TCP sock able to handle asynchronous events using + * @ref sys_event. + * + * @param[in] sock A TCP sock object. + * @param[in] ev_queue The queue the events on @p sock will be added to. + * @param[in] handler The event handler function to call on an event on + * @p sock. + * + * @note Only available with module `sock_tcp`. + */ +void sock_tcp_event_init(sock_tcp_t *sock, event_queue_t *ev_queue, + sock_tcp_cb_t handler); + +/** + * @brief Makes a TCP listening queue able to handle asynchronous events using + * @ref sys_event. + * + * @param[in] queue A TCP listening queue. + * @param[in] ev_queue The queue the events on @p sock will be added to. + * @param[in] handler The event handler function to call on an event on + * @p sock. + * + * @note Only available with module `sock_tcp`. + */ +void sock_tcp_queue_event_init(sock_tcp_queue_t *queue, event_queue_t *ev_queue, + sock_tcp_queue_cb_t handler); +#endif /* defined(MODULE_SOCK_TCP) || defined(DOXYGEN) */ + +#if defined(MODULE_SOCK_UDP) || defined(DOXYGEN) +/** + * @brief Makes a UDP sock able to handle asynchronous events using + * @ref sys_event. + * + * @param[in] sock A UDP sock object. + * @param[in] ev_queue The queue the events on @p sock will be added to. + * @param[in] handler The event handler function to call on an event on + * @p sock. + * + * @note Only available with module `sock_udp`. + */ +void sock_udp_event_init(sock_udp_t *sock, event_queue_t *ev_queue, + sock_udp_cb_t handler); +#endif /* defined(MODULE_SOCK_UDP) || defined(DOXYGEN) */ + +#ifdef __cplusplus +} +#endif + +#endif /* NET_SOCK_ASYNC_EVENT_H */ +/** @} */ diff --git a/sys/net/sock/async/event/Makefile b/sys/net/sock/async/event/Makefile new file mode 100644 index 0000000000..944aa5017e --- /dev/null +++ b/sys/net/sock/async/event/Makefile @@ -0,0 +1,3 @@ +MODULE := sock_async_event + +include $(RIOTBASE)/Makefile.base diff --git a/sys/net/sock/async/event/Makefile.include b/sys/net/sock/async/event/Makefile.include new file mode 100644 index 0000000000..b41051c246 --- /dev/null +++ b/sys/net/sock/async/event/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE_INCLUDES += $(RIOTBASE)/sys/net/sock/async/event +CFLAGS += -DSOCK_HAS_ASYNC -DSOCK_HAS_ASYNC_CTX diff --git a/sys/net/sock/async/event/sock_async_ctx.h b/sys/net/sock/async/event/sock_async_ctx.h new file mode 100644 index 0000000000..1ecc7d39ce --- /dev/null +++ b/sys/net/sock/async/event/sock_async_ctx.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup net_sock_async_event + * @{ + * + * @file + * @brief Type definitions for asynchronous socks with @ref sys_event + * + * @author Martine Lenders + */ +#ifndef SOCK_ASYNC_CTX_H +#define SOCK_ASYNC_CTX_H + +#include "event.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Generalized callback type + */ +typedef union { + void (*generic)(void *, sock_async_flags_t); /**< anything goes */ +#ifdef MODULE_SOCK_DTLS + sock_dtls_cb_t dtls; /**< DTLS callback */ +#endif +#ifdef MODULE_SOCK_IP + sock_ip_cb_t ip; /**< IP callback */ +#endif +#ifdef MODULE_SOCK_TCP + sock_tcp_cb_t tcp; /**< TCP callback */ + sock_tcp_queue_cb_t tcp_queue; /**< TCP queue callback */ +#endif +#ifdef MODULE_SOCK_UDP + sock_udp_cb_t udp; /**< UDP callback */ +#endif +} sock_event_cb_t; + +/** + * @brief Event definition for context scope + */ +typedef struct { + event_t super; /**< event structure that gets extended */ + sock_event_cb_t cb; /**< callback */ + void *sock; /**< generic pointer to a @ref net_sock object */ + sock_async_flags_t type; /**< types of the event */ +} sock_event_t; + +/** + * @brief Asynchronous context for @ref net_sock_async_event + */ +typedef struct { + sock_event_t event; /**< event storage */ + event_queue_t *queue; /**< event queue to post socket events to */ +} sock_async_ctx_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* SOCK_ASYNC_CTX_H */ +/** @} */ diff --git a/sys/net/sock/async/event/sock_async_event.c b/sys/net/sock/async/event/sock_async_event.c new file mode 100644 index 0000000000..e7b3666b0f --- /dev/null +++ b/sys/net/sock/async/event/sock_async_event.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2019 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @author Martine Lenders + */ + +#include "net/sock/async/event.h" + +static void _event_handler(event_t *ev) +{ + sock_event_t *event = (sock_event_t *)ev; + + event->cb.generic(event->sock, event->type); + event->type = 0; +} + +static inline void _cb(void *sock, sock_async_flags_t type, + sock_async_ctx_t *ctx) +{ + ctx->event.sock = sock; + ctx->event.type |= type; + event_post(ctx->queue, &ctx->event.super); +} + +static void _set_ctx(sock_async_ctx_t *ctx, event_queue_t *ev_queue) +{ + ctx->event.type = 0; + ctx->event.super.list_node.next = NULL; + ctx->event.super.handler = _event_handler; + ctx->queue = ev_queue; +} + +#ifdef MODULE_SOCK_DTLS +static void _dtls_cb(sock_dtls_t *sock, sock_async_flags_t type) +{ + _cb(sock, type, sock_dtls_get_async_ctx(sock)); +} + +void sock_dtls_event_init(sock_dtls_t *sock, event_queue_t *ev_queue, + sock_dtls_cb_t handler) +{ + sock_async_ctx_t *ctx = sock_dtls_get_async_ctx(sock); + + _set_ctx(ctx, ev_queue); + ctx->event.cb.dtls = handler; + sock_dtls_set_cb(sock, _dtls_cb); +} +#endif /* MODULE_SOCK_DTLS */ + +#ifdef MODULE_SOCK_IP +static void _ip_cb(sock_ip_t *sock, sock_async_flags_t type) +{ + _cb(sock, type, sock_ip_get_async_ctx(sock)); +} + +void sock_ip_event_init(sock_ip_t *sock, event_queue_t *ev_queue, + sock_ip_cb_t handler) +{ + sock_async_ctx_t *ctx = sock_ip_get_async_ctx(sock); + + _set_ctx(ctx, ev_queue); + ctx->event.cb.ip = handler; + sock_ip_set_cb(sock, _ip_cb); +} +#endif /* MODULE_SOCK_IP */ + +#ifdef MODULE_SOCK_TCP +static void _tcp_cb(sock_tcp_t *sock, sock_async_flags_t type) +{ + _cb(sock, type, sock_tcp_get_async_ctx(sock)); +} + +void sock_tcp_event_init(sock_tcp_t *sock, event_queue_t *ev_queue, + sock_tcp_cb_t handler) +{ + sock_async_ctx_t *ctx = sock_tcp_get_async_ctx(sock); + + _set_ctx(ctx, ev_queue); + ctx->event.cb.tcp = handler; + sock_tcp_set_cb(sock, _tcp_cb); +} + +static void _tcp_queue_cb(sock_tcp_queue_t *queue, sock_async_flags_t type) +{ + _cb(queue, type, sock_tcp_queue_get_async_ctx(queue)); +} + +void sock_tcp_queue_event_init(sock_tcp_queue_t *queue, + event_queue_t *ev_queue, + sock_tcp_queue_cb_t handler) +{ + sock_async_ctx_t *ctx = sock_tcp_queue_get_async_ctx(sock); + + _set_ctx(ctx, ev_queue); + ctx->event.cb.tcp_queue = handler; + sock_tcp_queue_set_cb(queue, _tcp_queue_cb); +} +#endif /* MODULE_SOCK_TCP */ + +#ifdef MODULE_SOCK_UDP +static void _udp_cb(sock_udp_t *sock, sock_async_flags_t type) +{ + _cb(sock, type, sock_udp_get_async_ctx(sock)); +} + +void sock_udp_event_init(sock_udp_t *sock, event_queue_t *ev_queue, + sock_udp_cb_t handler) +{ + sock_async_ctx_t *ctx = sock_udp_get_async_ctx(sock); + + _set_ctx(ctx, ev_queue); + ctx->event.cb.udp = handler; + sock_udp_set_cb(sock, _udp_cb); +} +#endif /* MODULE_SOCK_UDP */ + +/** @} */