diff --git a/.sai.json b/.sai.json index 76da2b229..ac00c3aca 100644 --- a/.sai.json +++ b/.sai.json @@ -195,6 +195,10 @@ "libglib": { "cmake": "-DLWS_WITH_GLIB=ON" }, + "sdevent": { + "cmake": "-DLWS_WITH_SDEVENT=ON", + "platforms": "none, linux-fedora-32/x86_64-amd/gcc" + }, "ipv6": { "cmake": "-DLWS_IPV6=ON", "platforms": "windows-10/x86_64-amd/mingw64, windows-10/x86_64-amd/msvc" diff --git a/CMakeLists-implied-options.txt b/CMakeLists-implied-options.txt index eb4f306b5..3c52fb6d6 100644 --- a/CMakeLists-implied-options.txt +++ b/CMakeLists-implied-options.txt @@ -118,7 +118,8 @@ endif() if (LWS_WITH_LIBEV OR LWS_WITH_LIBUV OR LWS_WITH_LIBEVENT OR - LWS_WITH_GLIB) + LWS_WITH_GLIB OR + LWS_WITH_SDEVENT) set(LWS_WITH_EVENT_LIBS 1) else() unset(LWS_WITH_EVENT_LIBS) diff --git a/CMakeLists.txt b/CMakeLists.txt index cfcc1f049..cf418e907 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ option(LWS_WITH_LIBEV "Compile with support for libev" OFF) option(LWS_WITH_LIBUV "Compile with support for libuv" OFF) option(LWS_WITH_LIBEVENT "Compile with support for libevent" OFF) option(LWS_WITH_GLIB "Compile with support for glib event loop" OFF) +option(LWS_WITH_SDEVENT "Compile with support for sd-event loop" OFF) if (UNIX) # since v4.1, on unix platforms default is build any event libs as runtime plugins diff --git a/cmake/lws_config.h.in b/cmake/lws_config.h.in index e34e80139..329218cae 100644 --- a/cmake/lws_config.h.in +++ b/cmake/lws_config.h.in @@ -152,6 +152,7 @@ #cmakedefine LWS_WITH_LIBEV #cmakedefine LWS_WITH_LIBEVENT #cmakedefine LWS_WITH_LIBUV +#cmakedefine LWS_WITH_SDEVENT #cmakedefine LWS_WITH_LWSAC #cmakedefine LWS_LOGS_TIMESTAMP #cmakedefine LWS_WITH_MBEDTLS diff --git a/include/libwebsockets/lws-context-vhost.h b/include/libwebsockets/lws-context-vhost.h index 796cc476f..2a7bdbe59 100644 --- a/include/libwebsockets/lws-context-vhost.h +++ b/include/libwebsockets/lws-context-vhost.h @@ -233,6 +233,10 @@ #define LWS_SERVER_OPTION_SS_PROXY (1ll << 36) /**< (VH) We are being a SS Proxy listen socket for the vhost */ +#define LWS_SERVER_OPTION_SDEVENT (1ll << 37) + /**< (CTX) Use sd-event loop */ + + /****** add new things just above ---^ ******/ diff --git a/lib/core/context.c b/lib/core/context.c index fe66b9879..d11246ef1 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -369,6 +369,7 @@ static const struct lws_evlib_map { { LWS_SERVER_OPTION_LIBEVENT, "evlib_event" }, { LWS_SERVER_OPTION_GLIB, "evlib_glib" }, { LWS_SERVER_OPTION_LIBEV, "evlib_ev" }, + { LWS_SERVER_OPTION_SDEVENT, "evlib_sd" }, }; static const char * const dlist[] = { ".", /* Priority 1: plugins in cwd */ @@ -598,6 +599,13 @@ lws_create_context(const struct lws_context_creation_info *info) } #endif +#if defined(LWS_WITH_SDEVENT) + if (lws_check_opt(info->options, LWS_SERVER_OPTION_SDEVENT)) { + extern const lws_plugin_evlib_t evlib_sd; + plev = &evlib_sd; + } +#endif + #endif /* with event libs */ #endif /* not with ev plugins */ diff --git a/lib/event-libs/CMakeLists.txt b/lib/event-libs/CMakeLists.txt index 3bb647652..b0226c281 100644 --- a/lib/event-libs/CMakeLists.txt +++ b/lib/event-libs/CMakeLists.txt @@ -92,6 +92,10 @@ if (LWS_WITH_LIBEV) set(LWS_HAVE_EVBACKEND_IOURING ${LWS_HAVE_EVBACKEND_IOURING} PARENT_SCOPE) endif() +if (LWS_WITH_SDEVENT) + add_subdir_include_directories(sdevent) +endif() + # diff --git a/lib/event-libs/README.md b/lib/event-libs/README.md index 69ea7633f..5d49bec1f 100644 --- a/lib/event-libs/README.md +++ b/lib/event-libs/README.md @@ -8,7 +8,7 @@ and native WSA on windows. To get access to epoll() or other platform specific better poll waits, or to integrate with existing applications already using a specific event loop, it can be desirable for lws to use another external event library, like libuv, glib, -libevent or libev. +libevent, libev, or sdevent. Lws supports wholesale replacement of its wait selectable at runtime, either by building support for one or more event lib into the libwebsockets library, or by diff --git a/lib/event-libs/sdevent/CMakeLists.txt b/lib/event-libs/sdevent/CMakeLists.txt new file mode 100644 index 000000000..c4222d388 --- /dev/null +++ b/lib/event-libs/sdevent/CMakeLists.txt @@ -0,0 +1,44 @@ +# The strategy is to only export to PARENT_SCOPE +# +# - changes to LIB_LIST +# - includes via include_directories +# +# and keep everything else private + +include_directories(.) + +# configure or find systemd library +set(LIB_SYSTEMD_LIBRARIES CACHE PATH "Path to the libsystemd library") +if ("${LWS_SYSTEMD_LIBRARIES}" STREQUAL "") + if (NOT LIB_SYSTEMD_FOUND) + find_path(LIBSYSTEMD_INCLUDE_DIRS NAMES systemd/sd-event.h) + find_library(LIBSYSTEMD_LIBRARIES NAMES systemd) + endif() +else() + set(LIBSYSTEMD_LIBRARIES ${LWS_SYSTEMD_LIBRARIES}) + set(LIBSYSTEMD_INCLUDE_DIRS ${LWS_LIBSYSTEMD_INCLUDE_DIRS}) +endif() +message("libsystemd include dir: ${LIBSYSTEMD_INCLUDE_DIRS}") +message("libsystemd libraries: ${LIBSYSTEMD_LIBRARIES}") + +if (LWS_WITH_EVLIB_PLUGINS) + + create_evlib_plugin( + evlib_sd + sdevent.c + private-lib-event-libs-sdevent.h + ${LIBSYSTEMD_LIBRARIES} + ) + +else() + + list(APPEND LIB_LIST ${LIBSYSTEMD_LIBRARIES}) + list(APPEND SOURCES event-libs/sdevent/sdevent.c) + +endif() + +# +# Keep explicit parent scope exports at end +# + +exports_to_parent_scope() diff --git a/lib/event-libs/sdevent/private-lib-event-libs-sdevent.h b/lib/event-libs/sdevent/private-lib-event-libs-sdevent.h new file mode 100644 index 000000000..b530d7052 --- /dev/null +++ b/lib/event-libs/sdevent/private-lib-event-libs-sdevent.h @@ -0,0 +1,3 @@ +#include + +extern const struct lws_event_loop_ops event_loop_ops_sdevent; diff --git a/lib/event-libs/sdevent/sdevent.c b/lib/event-libs/sdevent/sdevent.c new file mode 100644 index 000000000..3f49fe01a --- /dev/null +++ b/lib/event-libs/sdevent/sdevent.c @@ -0,0 +1,431 @@ +#include + +#include +#include "private-lib-event-libs-sdevent.h" + +#define pt_to_priv_sd(_pt) ((struct lws_pt_eventlibs_sdevent *)(_pt)->evlib_pt) +#define wsi_to_priv_sd(_w) ((struct lws_wsi_watcher_sdevent *)(_w)->evlib_wsi) + +struct lws_pt_eventlibs_sdevent { + struct lws_context_per_thread *pt; + struct sd_event *io_loop; + struct sd_event_source *sultimer; + struct sd_event_source *idletimer; +}; + +struct lws_wsi_watcher_sdevent { + struct sd_event_source *source; + uint32_t events; +}; + +static int +sultimer_handler(sd_event_source *s, uint64_t usec, void *userdata) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *)userdata; + + lws_usec_t us; + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + if (us) { + uint64_t at; + + sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &at); + at += (uint64_t)us; + sd_event_source_set_time(pt_to_priv_sd(pt)->sultimer, at); + sd_event_source_set_enabled(pt_to_priv_sd(pt)->sultimer, + SD_EVENT_ONESHOT); + } + + lws_pt_unlock(pt); + lws_context_unlock(pt->context); + + return 0; +} + +static int +idle_handler(sd_event_source *s, uint64_t usec, void *userdata) +{ + struct lws_context_per_thread *pt = (struct lws_context_per_thread *)userdata; + + lws_usec_t us; + + lws_service_do_ripe_rxflow(pt); + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + + /* + * is there anybody with pending stuff that needs service forcing? + */ + if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) + /* -1 timeout means just do forced service */ + _lws_plat_service_forced_tsi(pt->context, pt->tid); + + /* account for sultimer */ + + us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, + lws_now_usecs()); + + if (us) { + uint64_t at; + + sd_event_now(sd_event_source_get_event(s), CLOCK_MONOTONIC, &at); + at += (uint64_t)us; + sd_event_source_set_time(pt_to_priv_sd(pt)->sultimer, at); + sd_event_source_set_enabled(pt_to_priv_sd(pt)->sultimer, + SD_EVENT_ONESHOT); + } + + sd_event_source_set_enabled(pt_to_priv_sd(pt)->idletimer, SD_EVENT_OFF); + + lws_pt_unlock(pt); + lws_context_unlock(pt->context); + + return 0; +} + +static int +sock_accept_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) +{ + struct lws *wsi = (struct lws *)userdata; + struct lws_context *context = wsi->a.context; + struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; + struct sd_event_source *idletimer, *watcher; + struct lws_pollfd eventfd; + + lws_context_lock(pt->context, __func__); + lws_pt_lock(pt, __func__); + + if (pt->is_destroyed) + goto bail; + + eventfd.fd = fd; + eventfd.events = 0; + eventfd.revents = 0; + + if (revents & EPOLLIN) { + eventfd.events |= LWS_POLLIN; + eventfd.revents |= LWS_POLLIN; + } + + if (revents & EPOLLOUT) { + eventfd.events |= LWS_POLLOUT; + eventfd.revents |= LWS_POLLOUT; + } + + lws_pt_unlock(pt); + lws_context_unlock(pt->context); + + lws_service_fd_tsi(context, &eventfd, wsi->tsi); + + if (pt->destroy_self) { + lws_context_destroy(pt->context); + return -1; + } + + /* fire idle handler */ + idletimer = pt_to_priv_sd(pt)->idletimer; + if (idletimer) { + sd_event_source_set_time(idletimer, (uint64_t) 0); + sd_event_source_set_enabled(idletimer, SD_EVENT_ON); + } + + /* + * allow further events + * + * Note: + * do not move the assignment up, lws_service_fd_tsi may invalidate it! + */ + watcher = wsi_to_priv_sd(wsi)->source; + if (watcher) + sd_event_source_set_enabled(watcher, SD_EVENT_ONESHOT); + + return 0; + +bail: + lws_pt_unlock(pt); + lws_context_unlock(pt->context); + + return -1; +} + +static void +io_sd(struct lws *wsi, unsigned int flags) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + + /* + * Only manipulate if there is an event source, and if + * the pt is still alive + */ + if (!pt_to_priv_sd(pt)->io_loop || + !wsi_to_priv_sd(wsi)->source || + pt->is_destroyed) + return; + + // assert that the requested flags do not contain anything unexpected + if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { + lwsl_err("%s: assert: flags %d", __func__, flags); + assert(0); + } + + // we are overdoing a bit here, so it resembles the structure in libuv.c + if (flags & LWS_EV_START) { + if (flags & LWS_EV_WRITE) + wsi_to_priv_sd(wsi)->events |= EPOLLOUT; + + if (flags & LWS_EV_READ) + wsi_to_priv_sd(wsi)->events |= EPOLLIN; + + sd_event_source_set_io_events(wsi_to_priv_sd(wsi)->source, + wsi_to_priv_sd(wsi)->events); + sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source, + SD_EVENT_ONESHOT); + } else { + if (flags & LWS_EV_WRITE) + wsi_to_priv_sd(wsi)->events = + wsi_to_priv_sd(wsi)->events & + (uint32_t)(~EPOLLOUT); + + if (flags & LWS_EV_READ) + wsi_to_priv_sd(wsi)->events = + wsi_to_priv_sd(wsi)->events & + (uint32_t)(~EPOLLIN); + + sd_event_source_set_io_events(wsi_to_priv_sd(wsi)->source, + wsi_to_priv_sd(wsi)->events); + + if (!(wsi_to_priv_sd(wsi)->events & (EPOLLIN | EPOLLOUT))) + sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source, + SD_EVENT_ONESHOT); + else + sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source, + SD_EVENT_OFF); + } +} + +static int +init_vhost_listen_wsi_sd(struct lws *wsi) +{ + struct lws_context_per_thread *pt; + + if (!wsi) + return 0; + + pt = &wsi->a.context->pt[(int)wsi->tsi]; + + sd_event_add_io(pt_to_priv_sd(pt)->io_loop, + &wsi_to_priv_sd(wsi)->source, + wsi->desc.sockfd, + wsi_to_priv_sd(wsi)->events, + sock_accept_handler, + wsi); + + io_sd(wsi, LWS_EV_START | LWS_EV_READ); + + return 0; +} + +static int +init_pt_sd(struct lws_context *context, void *_loop, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt); + struct sd_event *loop = (struct sd_event *)_loop; + struct lws_vhost *vh; + int first = 1; // we are the first that create and initialize the loop + + ptpriv->pt = pt; + + // make sure we have an event loop + if (!ptpriv->io_loop) { + if (!loop) { + if (sd_event_default(&loop) < 0) { + lwsl_err("%s: sd_event_default failed\n", __func__); + + return -1; + } + pt->event_loop_foreign = 0; + } else { + sd_event_ref(loop); + pt->event_loop_foreign = 1; + } + + ptpriv->io_loop = loop; + + } else + /* + * If the loop was initialized before, we do not need to + * do full initialization + */ + first = 0; + + // initialize accept/read for vhosts + // Note: default vhost usually not included here + for (vh = context->vhost_list; vh; vh = vh->vhost_next) + /* call lws_event_loop_ops->init_vhost_listen_wsi */ + if (init_vhost_listen_wsi_sd(vh->lserv_wsi) == -1) + return -1; + + if (first) { + + if (0 > sd_event_add_time(loop, + &ptpriv->sultimer, + CLOCK_MONOTONIC, + UINT64_MAX, + 0, + sultimer_handler, + (void*) pt + )) + return -1; + + if (0 > sd_event_add_time(loop, + &ptpriv->idletimer, + CLOCK_MONOTONIC, + 0, + 0, + idle_handler, + (void *)pt)) + return -1; + + sd_event_source_set_enabled(ptpriv->idletimer, SD_EVENT_ON); + + if (0 > sd_event_source_set_priority(ptpriv->idletimer, + SD_EVENT_PRIORITY_IDLE)) + return -1; + + } + + return 0; +} + +static void +wsi_destroy_sd(struct lws *wsi) +{ + if (!wsi) + return; + + io_sd(wsi, LWS_EV_STOP | (LWS_EV_READ | LWS_EV_WRITE)); + + if (wsi_to_priv_sd(wsi)->source) { + sd_event_source_set_enabled(wsi_to_priv_sd(wsi)->source, + SD_EVENT_OFF); + sd_event_source_unref(wsi_to_priv_sd(wsi)->source); + wsi_to_priv_sd(wsi)->source = NULL; + } +} + +static int +wsi_logical_close_sd(struct lws *wsi) +{ + wsi_destroy_sd(wsi); + + return 0; +} + +static int +sock_accept_sd(struct lws *wsi) +{ + struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; + + if (wsi->role_ops->file_handle) + sd_event_add_io(pt_to_priv_sd(pt)->io_loop, + &wsi_to_priv_sd(wsi)->source, + wsi->desc.filefd, + wsi_to_priv_sd(wsi)->events, + sock_accept_handler, + wsi); + else + sd_event_add_io(pt_to_priv_sd(pt)->io_loop, + &wsi_to_priv_sd(wsi)->source, + wsi->desc.sockfd, + wsi_to_priv_sd(wsi)->events, + sock_accept_handler, + wsi); + + return 0; +} + +static void +run_pt_sd(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt); + + if (ptpriv->io_loop) + sd_event_run(ptpriv->io_loop, (uint64_t) -1); +} + +static void +destroy_pt_sd(struct lws_context *context, int tsi) +{ + struct lws_context_per_thread *pt = &context->pt[tsi]; + struct lws_pt_eventlibs_sdevent *ptpriv = pt_to_priv_sd(pt); + struct lws_vhost *vh; + + for (vh = context->vhost_list; vh; vh = vh->vhost_next) + if (vh->lserv_wsi) + wsi_logical_close_sd(vh->lserv_wsi); + + if (ptpriv->sultimer) { + sd_event_source_set_enabled(ptpriv->sultimer, + SD_EVENT_OFF); + sd_event_source_unref(ptpriv->sultimer); + ptpriv->sultimer = NULL; + } + + if (ptpriv->idletimer) { + sd_event_source_set_enabled(ptpriv->idletimer, + SD_EVENT_OFF); + sd_event_source_unref(ptpriv->idletimer); + ptpriv->idletimer = NULL; + } + + if (ptpriv->io_loop) { + sd_event_unref(ptpriv->io_loop); + ptpriv->io_loop = NULL; + } + +} + +const struct lws_event_loop_ops event_loop_ops_sdevent = { + .name = "sdevent", + .init_context = NULL, + .destroy_context1 = NULL, + .destroy_context2 = NULL, + .init_vhost_listen_wsi = init_vhost_listen_wsi_sd, + .init_pt = init_pt_sd, + .wsi_logical_close = wsi_logical_close_sd, + .check_client_connect_ok = NULL, + .close_handle_manually = NULL, + .sock_accept = sock_accept_sd, + .io = io_sd, + .run_pt = run_pt_sd, + .destroy_pt = destroy_pt_sd, + .destroy_wsi = wsi_destroy_sd, + + .flags = 0, + + .evlib_size_ctx = 0, + .evlib_size_pt = sizeof(struct lws_pt_eventlibs_sdevent), + .evlib_size_vh = 0, + .evlib_size_wsi = sizeof(struct lws_wsi_watcher_sdevent), +}; + +#if defined(LWS_WITH_EVLIB_PLUGINS) +LWS_VISIBLE +#endif +const lws_plugin_evlib_t evlib_sd = { + .hdr = { + "systemd event loop", + "lws_evlib_plugin", + LWS_PLUGIN_API_MAGIC + }, + + .ops = &event_loop_ops_sdevent +}; diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt index 5bf00224f..123e81309 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt @@ -19,7 +19,7 @@ CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defin CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBEVENT)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEVENT) CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBEV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEV) CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_GLIB)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_GLIB) - +CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_SDEVENT)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_SDEVENT) if (LWS_WITH_LIBUV) find_path(LIBUV_INCLUDE_DIRS NAMES uv.h) @@ -72,6 +72,16 @@ if (LWS_WITH_GLIB) set(extralibs ${extralibs} ${GLIB_LIBRARIES}) list(APPEND SRCS glib.c) endif() +if (LWS_WITH_SDEVENT) + find_path(LIBSYSTEMD_INCLUDE_DIRS NAMES systemd/sd-event.h) + find_library(LIBSYSTEMD_LIBRARIES NAMES systemd) + message("libsystemd include dir: ${LIBSYSTEMD_INCLUDE_DIRS}") + message("libsystemd libraries: ${LIBSYSTEMD_LIBRARIES}") + include_directories("${LIBSYSTEMD_INCLUDE_DIRS}") + set(extralibs ${extralibs} ${LIBSYSTEMD_LIBRARIES}) + list(APPEND SRCS libsdevent.c) +endif() + message("Extra libs: ${extralibs}") diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md index 4c21fa1a9..0a4aa5f89 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md @@ -6,6 +6,7 @@ Commandline option|Meaning --uv|Use the libuv event library (lws must have been configured with `-DLWS_WITH_LIBUV=1`) --event|Use the libevent library (lws must have been configured with `-DLWS_WITH_LIBEVENT=1`) --ev|Use the libev event library (lws must have been configured with `-DLWS_WITH_LIBEV=1`) +--sd|Use the systemd event library (lws must have been configured with `-DLWS_WITH_SDEVENT=1`) Notice libevent and libev cannot coexist in the one library. But all the other combinations are OK. diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/libsdevent.c b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/libsdevent.c new file mode 100644 index 000000000..bc712c433 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/libsdevent.c @@ -0,0 +1,86 @@ +/* + * lws-minimal-http-server-eventlib-foreign + * + * Written in 2020 by Christian Fuchs + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * The sdevent specific code + */ + +#include + +#include +#include + +#include + +#include "private.h" + +static struct sd_event *sd_loop; +static sd_event_source *sd_timer; +static sd_event_source *sd_signal; + +static int +timer_cb_sd(sd_event_source *source, uint64_t now, void *user) +{ + foreign_timer_service(sd_loop); + + if (sd_timer) { + sd_event_source_set_time(sd_timer, now + 1000000); + sd_event_source_set_enabled(sd_timer, SD_EVENT_ON); + } + + return 0; +} + +static int +signal_cb_sd(sd_event_source *source, const struct signalfd_siginfo *si, + void *user) +{ + signal_cb((int)si->ssi_signo); + return 0; +} + +static void +foreign_event_loop_init_and_run_libsdevent(void) +{ + uint64_t now; + + /* we create and start our "foreign loop" */ + + sd_event_default(&sd_loop); + sd_event_add_signal(sd_loop, &sd_signal, SIGINT, signal_cb_sd, NULL); + + sd_event_now(sd_loop, CLOCK_MONOTONIC, &now); + sd_event_add_time(sd_loop, &sd_timer, CLOCK_MONOTONIC, now, + (uint64_t) 1000, timer_cb_sd, NULL); + + sd_event_loop(sd_loop); +} + +static void +foreign_event_loop_stop_libsdevent(void) +{ + sd_event_exit(sd_loop, 0); +} + +static void +foreign_event_loop_cleanup_libsdevent(void) +{ + sd_event_source_set_enabled(sd_timer, SD_EVENT_OFF); + sd_timer = sd_event_source_unref(sd_timer); + + sd_event_source_set_enabled(sd_signal, SD_EVENT_OFF); + sd_signal = sd_event_source_unref(sd_signal); + + sd_loop = sd_event_unref(sd_loop); +} + +const struct ops ops_sdevent = { + foreign_event_loop_init_and_run_libsdevent, + foreign_event_loop_stop_libsdevent, + foreign_event_loop_cleanup_libsdevent +}; + diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c index de289fb4a..ff9a8973b 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c @@ -196,10 +196,17 @@ int main(int argc, const char **argv) ops = &ops_glib; lwsl_notice("%s: using glib loop\n", __func__); } else +#endif +#if defined(LWS_WITH_SDEVENT) + if (lws_cmdline_option(argc, argv, "--sd")) { + info.options |= LWS_SERVER_OPTION_SDEVENT; + ops = &ops_sdevent; + lwsl_notice("%s: using sd-event loop\n", __func__); + } else #endif { lwsl_err("This app only makes sense when used\n"); - lwsl_err(" with a foreign loop, --uv, --event, --glib, or --ev\n"); + lwsl_err(" with a foreign loop, --uv, --event, --glib, --ev or --sd\n"); return 1; } diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/private.h b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/private.h index 71dfd2191..8f41c4721 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/private.h +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/private.h @@ -12,4 +12,4 @@ extern int lifetime, reported; void foreign_timer_service(void *foreign_loop); void signal_cb(int signum); -extern const struct ops ops_libuv, ops_libevent, ops_glib, ops_libev; +extern const struct ops ops_libuv, ops_libevent, ops_glib, ops_libev, ops_sdevent;