| #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_wsi_err(wsi, "assert: flags %d", 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 |
| elops_listen_init_sdevent(struct lws_dll2 *d, void *user) |
| { |
| struct lws *wsi = lws_container_of(d, struct lws, listen_list); |
| |
| if (init_vhost_listen_wsi_sd(wsi) == -1) |
| return -1; |
| |
| 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; |
| int first = 1; /* first to 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_cx_err(context, "sd_event_default failed"); |
| |
| 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; |
| |
| lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_sdevent); |
| |
| 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 int |
| elops_listen_destroy_sdevent(struct lws_dll2 *d, void *user) |
| { |
| struct lws *wsi = lws_container_of(d, struct lws, listen_list); |
| |
| wsi_logical_close_sd(wsi); |
| |
| return 0; |
| } |
| |
| 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); |
| |
| lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_sdevent); |
| |
| 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_BUILD_HASH, |
| LWS_PLUGIN_API_MAGIC |
| }, |
| |
| .ops = &event_loop_ops_sdevent |
| }; |