1
0
Fork 0
mirror of https://github.com/warmcat/libwebsockets.git synced 2025-03-09 00:00:04 +01:00

eventlib: add sd-event support

This commit is contained in:
Christian Fuchs 2021-01-06 14:15:53 +01:00 committed by Andy Green
parent 77055c4fd7
commit b961e5f351
16 changed files with 610 additions and 5 deletions

View file

@ -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"

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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 ---^ ******/

View file

@ -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 */

View file

@ -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()
#

View file

@ -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

View file

@ -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()

View file

@ -0,0 +1,3 @@
#include <private-lib-core.h>
extern const struct lws_event_loop_ops event_loop_ops_sdevent;

View file

@ -0,0 +1,431 @@
#include <systemd/sd-event.h>
#include <private-lib-core.h>
#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
};

View file

@ -19,7 +19,7 @@ CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defin
CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\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 <libwebsockets.h>\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 <libwebsockets.h>\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 <libwebsockets.h>\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}")

View file

@ -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.

View file

@ -0,0 +1,86 @@
/*
* lws-minimal-http-server-eventlib-foreign
*
* Written in 2020 by Christian Fuchs <christian.fuchs@scs.ch>
*
* This file is made available under the Creative Commons CC0 1.0
* Universal Public Domain Dedication.
*
* The sdevent specific code
*/
#include <libwebsockets.h>
#include <string.h>
#include <signal.h>
#include <systemd/sd-event.h>
#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
};

View file

@ -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;
}

View file

@ -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;