Update Files

This commit is contained in:
2025-01-22 17:22:38 +01:00
parent 89b9349629
commit 4c5e729485
5132 changed files with 1195369 additions and 0 deletions

View File

@ -0,0 +1,109 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
include_directories(.)
macro(create_evlib_plugin PLUGIN_NAME MAIN_SRC PLUGIN_HDR EVLIB)
set(PLUGIN_SRCS ${MAIN_SRC})
source_group("Headers Private" FILES ${PLUGIN_HDR})
source_group("Sources" FILES ${MAIN_SRC})
add_library(websockets-${PLUGIN_NAME} SHARED ${MAIN_SRC} ${PLUGIN_HDR})
if (APPLE)
set_property(TARGET websockets-${PLUGIN_NAME} PROPERTY MACOSX_RPATH YES)
endif()
foreach(libpath ${LWS_DEP_LIB_PATHS})
target_link_directories(${TEST_NAME} ${libpath})
endforeach()
target_link_libraries(websockets-${PLUGIN_NAME} websockets_shared ${EVLIB})
add_dependencies(websockets-${PLUGIN_NAME} websockets_shared)
target_compile_definitions(websockets-${PLUGIN_NAME} PRIVATE LWS_BUILDING_SHARED)
target_include_directories(websockets-${PLUGIN_NAME} PRIVATE
${PLUGIN_INCLUDE} ${LWS_LIB_BUILD_INC_PATHS})
# Set test app specific defines.
# set_property(TARGET ${PLUGIN_NAME}
# PROPERTY COMPILE_DEFINITIONS
# INSTALL_DATADIR="${CMAKE_INSTALL_PREFIX}/evlib-plugins"
#)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
install(TARGETS websockets-${PLUGIN_NAME}
EXPORT LibwebsocketsTargets
LIBRARY DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}"
COMPONENT ${PLUGIN_NAME})
list(APPEND EVLIB_PLUGINS_LIST websockets-${PLUGIN_NAME})
endmacro()
#
# poll support gets built into the lib as the default
#
if (LWS_WITH_POLL)
add_subdir_include_directories(poll)
endif()
if (LWS_WITH_LIBUV OR LWS_WITH_LIBUV_INTERNAL)
add_subdir_include_directories(libuv)
set(LWS_HAVE_UV_VERSION_H ${LWS_HAVE_UV_VERSION_H} PARENT_SCOPE)
set(LWS_HAVE_NEW_UV_VERSION_H ${LWS_HAVE_NEW_UV_VERSION_H} PARENT_SCOPE)
endif()
if (LWS_WITH_LIBEVENT)
add_subdir_include_directories(libevent)
endif()
if (LWS_WITH_GLIB)
add_subdir_include_directories(glib)
endif()
if (LWS_WITH_LIBEV)
add_subdir_include_directories(libev)
set(LWS_HAVE_EVBACKEND_LINUXAIO ${LWS_HAVE_EVBACKEND_LINUXAIO} PARENT_SCOPE)
set(LWS_HAVE_EVBACKEND_IOURING ${LWS_HAVE_EVBACKEND_IOURING} PARENT_SCOPE)
endif()
if (LWS_WITH_SDEVENT)
add_subdir_include_directories(sdevent)
endif()
if (LWS_WITH_ULOOP)
add_subdir_include_directories(uloop)
endif()
#
# Keep explicit parent scope exports at end
#
export_to_parent_intermediate()
set(EVLIB_PLUGINS_LIST ${EVLIB_PLUGINS_LIST} PARENT_SCOPE)

View File

@ -0,0 +1,169 @@
## Information for new event lib implementers
### Introduction
By default lws has built-in support for POSIX poll() as the event loop on unix,
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, 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
building runtime-loadable plugins. CMake symbol `LWS_WITH_EVLIB_PLUGINS`
decides if the support is built as plugins or included into the lws lib.
Due to their history libevent and libev have conflicting defines in the same
namespace and cannot be built together if included into the lib, however when
built as plugins they are built separately without problems.
See ./READMEs/README.event-libs.md for more details.
Despite it may be more work, lws event lib implementations must support
"foreign" loops cleanly, that is integration with an already-existing loop and
the ability to destroy the lws_context without stopping or leaving the foreign
loop in any different state than when lws found it. For most loops this is
fairly simple, but with libuv async close, it required refcounting lws libuv
handles and deferring the actual destroy until they were all really closed.
### Code placement
The code specific to the event library should live in `./lib/event-libs/**lib name**`
### Allowing control over enabling event libs
All event libs should add a cmake define `LWS_WITH_**lib name**` and make its
build dependent on it in CMakeLists.txt. Export the cmakedefine in
`./cmake/lws_config.h.in` as well so user builds can understand if the event
lib is available in the lws build it is trying to bind to.
If the event lib is disabled in cmake, nothing in its directory is built or
referenced.
### Event loop ops struct
The event lib support is defined by `struct lws_event_loop_ops` in
`lib/event-libs/private-lib-event-libs.h`,
each event lib support instantiates one of these and fills in the appropriate
ops callbacks to perform its job. By convention that lives in
`./lib/event-libs/**lib name**/**lib_name**.c`.
The ops struct must be public, not static, and must be named using `**lib_name**`,
eg
```
```
### Private event lib declarations
Truly private declarations for the event lib support that are only referenced by
that code can go in the event-libs directory as you like. The convention is
they should be in the event lib support directory in a file
`private-lib-event-libs-**lib name**.h`.
### Integration with lws
There are a couple of places to add refererences in ./lib/core/context.c, in a
table of context creation time server option flags mapped to the **lib_name**,
used for plugin mode, like this...
```
#if defined(LWS_WITH_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS)
static const struct lws_evlib_map {
uint64_t flag;
const char *name;
} map[] = {
{ LWS_SERVER_OPTION_LIBUV, "evlib_uv" },
{ LWS_SERVER_OPTION_LIBEVENT, "evlib_event" },
{ LWS_SERVER_OPTION_GLIB, "evlib_glib" },
{ LWS_SERVER_OPTION_LIBEV, "evlib_ev" },
};
```
and for backwards compatibility add a stanza to the built-in checks like this
```
#if defined(LWS_WITH_LIBUV)
if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBUV)) {
extern const lws_plugin_evlib_t evlib_uv;
plev = &evlib_uv;
}
#endif
```
Both entries are the way the main libs hook up to the selected event lib ops
struct at runtime.
### Integrating event lib assets to lws
Declare "container structs" in your private....h for anything you need at
wsi, pt, vhost and context levels, eg, the libuv event lib support need to
add its own assets in the perthread struct, it declares in its private....h
```
struct lws_pt_eventlibs_libuv {
uv_loop_t *io_loop;
struct lws_context_per_thread *pt;
uv_signal_t signals[8];
uv_timer_t sultimer;
uv_idle_t idle;
struct lws_signal_watcher_libuv w_sigint;
};
```
this is completely private and opaque, but in the ops struct there are provided
four entries to export the sizes of these event-lib specific objects
```
...
/* evlib_size_ctx */ sizeof(struct lws_context_eventlibs_libuv),
/* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libuv),
/* evlib_size_vh */ 0,
/* evlib_size_wsi */ sizeof(struct lws_io_watcher_libuv),
};
```
If the particular event lib doesn't need to have a private footprint in an
object, it can just set the size it needs there to 0.
When the context, pts, vhosts or wsis are created in lws, they over-allocate
to also allow for the event lib object, and set a pointer in the lws object
being created to point at the over-allocation. For example for the wsi
```
#if defined(LWS_WITH_EVENT_LIBS)
void *evlib_wsi; /* overallocated */
#endif
```
and similarly there are `evlib_pt` and so on for those objects, usable by the
event lib and opaque to everyone else. Once the event lib is selected at
runtime, all of these objects are guaranteed to have the right size object at
`wsi->evlib_wsi` initialized to zeroes.
### Enabling event lib adoption
You need to add a `LWS_SERVER_OPTION...` flag as necessary in `./lib/libwebsockets.h`
`enum lws_context_options`, and follow the existing code in `lws_create_context()`
to convert the flag into binding your ops struct to the context.
### Implementation of the event lib bindings
Study eg libuv implementation, using the available ops in the struct lws_event_loop_ops
as a guide.
### Destruction
Ending the event loop is generally a bit tricky, because if the event loop is
internal to the lws context, you cannot destroy it while the event loop is
running.
Don't add special exports... we tried that, it's a huge mess. The same user
code should be able work with any of the event loops including poll.
The solution we found was hide the different processing necessary for the
different cases in `lws_destroy_context()`. To help with that there are event
lib ops available that will be called at two different places in the context
destroy processing.

View File

@ -0,0 +1,77 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
set(LWS_GLIB_INCLUDE_DIRS CACHE PATH "Path to the glib include directory")
set(LWS_GLIB_LIBRARIES CACHE PATH "Path to the glib library")
include (FindPkgConfig)
if (NOT GLIB_FOUND)
find_path(GLIB_INCLUDE_DIRS NAMES glib-2.0/glib.h)
find_library(GLIB_LIBRARIES NAMES glib-2.0)
if (GLIB_INCLUDE_DIRS AND GLIB_LIBRARIES)
set(GLIB_FOUND)
endif()
if (GLIB_INCLUDE_DIRS)
set(GLIB_INCLUDE_DIRS "${GLIB_INCLUDE_DIRS}/glib-2.0" PARENT_SCOPE)
endif()
endif()
PKG_SEARCH_MODULE(LWS_GLIB2 glib-2.0)
if (LWS_GLIB2_FOUND)
list(APPEND GLIB_INCLUDE_DIRS "${LWS_GLIB2_INCLUDE_DIRS}")
endif()
message("glib include dir: ${GLIB_INCLUDE_DIRS}")
message("glib libraries: ${GLIB_LIBRARIES}")
include_directories("${GLIB_INCLUDE_DIRS}")
if (LWS_WITH_EVLIB_PLUGINS)
create_evlib_plugin(evlib_glib
glib.c
private-lib-event-libs-glib.h
${GLIB_LIBRARIES})
else()
list(APPEND LIB_LIST ${GLIB_LIBRARIES})
if (LWS_WITH_NETWORK)
list(APPEND SOURCES
event-libs/glib/glib.c)
endif()
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,516 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include <glib-unix.h>
#include "private-lib-event-libs-glib.h"
#if !defined(G_SOURCE_FUNC)
#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f))
#endif
#define pt_to_priv_glib(_pt) ((struct lws_pt_eventlibs_glib *)(_pt)->evlib_pt)
#define wsi_to_priv_glib(_w) ((struct lws_wsi_eventlibs_glib *)(_w)->evlib_wsi)
#define wsi_to_subclass(_w) (wsi_to_priv_glib(_w)->w_read.source)
#define wsi_to_gsource(_w) ((GSource *)wsi_to_subclass(_w))
#define pt_to_loop(_pt) (pt_to_priv_glib(_pt)->loop)
#define pt_to_g_main_context(_pt) g_main_loop_get_context(pt_to_loop(_pt))
#define lws_gs_valid(t) (t.gs)
#define lws_gs_destroy(t) if (lws_gs_valid(t)) { \
g_source_destroy(t.gs); \
g_source_unref(t.gs); \
t.gs = NULL; t.tag = 0; }
static gboolean
lws_glib_idle_timer_cb(void *p);
static gboolean
lws_glib_hrtimer_cb(void *p);
static gboolean
lws_glib_check(GSource *src)
{
struct lws_io_watcher_glib_subclass *sub =
(struct lws_io_watcher_glib_subclass *)src;
return !!g_source_query_unix_fd(src, sub->tag);
}
/*
* These helpers attach only to the main_context that belongs to the pt's glib
* mainloop. The simpler g_timeout_add() and g_idle_add() are forbidden
* because they implicitly choose the default main context to attach to
* instead of specifically the loop bound to the pt.
*
* https://developer.gnome.org/programming-guidelines/unstable/main-contexts.html.en#what-is-gmaincontext
*/
static int
lws_glib_set_idle(struct lws_context_per_thread *pt)
{
if (lws_gs_valid(pt_to_priv_glib(pt)->idle))
return 0;
pt_to_priv_glib(pt)->idle.gs = g_idle_source_new();
if (!pt_to_priv_glib(pt)->idle.gs)
return 1;
g_source_set_callback(pt_to_priv_glib(pt)->idle.gs,
lws_glib_idle_timer_cb, pt, NULL);
pt_to_priv_glib(pt)->idle.tag = g_source_attach(
pt_to_priv_glib(pt)->idle.gs, pt_to_g_main_context(pt));
return 0;
}
static int
lws_glib_set_timeout(struct lws_context_per_thread *pt, unsigned int ms)
{
lws_gs_destroy(pt_to_priv_glib(pt)->hrtimer);
pt_to_priv_glib(pt)->hrtimer.gs = g_timeout_source_new(ms);
if (!pt_to_priv_glib(pt)->hrtimer.gs)
return 1;
g_source_set_callback(pt_to_priv_glib(pt)->hrtimer.gs,
lws_glib_hrtimer_cb, pt, NULL);
pt_to_priv_glib(pt)->hrtimer.tag = g_source_attach(
pt_to_priv_glib(pt)->hrtimer.gs,
pt_to_g_main_context(pt));
return 0;
}
static gboolean
lws_glib_dispatch(GSource *src, GSourceFunc x, gpointer userData)
{
struct lws_io_watcher_glib_subclass *sub =
(struct lws_io_watcher_glib_subclass *)src;
struct lws_context_per_thread *pt;
struct lws_pollfd eventfd;
GIOCondition cond;
cond = g_source_query_unix_fd(src, sub->tag);
eventfd.revents = (short)cond;
/* translate from glib event namespace to platform */
if (cond & G_IO_IN)
eventfd.revents |= LWS_POLLIN;
if (cond & G_IO_OUT)
eventfd.revents |= LWS_POLLOUT;
if (cond & G_IO_ERR)
eventfd.revents |= LWS_POLLHUP;
if (cond & G_IO_HUP)
eventfd.revents |= LWS_POLLHUP;
eventfd.events = eventfd.revents;
eventfd.fd = sub->wsi->desc.sockfd;
lwsl_wsi_debug(sub->wsi, "fd %d, events %d",
eventfd.fd, eventfd.revents);
pt = &sub->wsi->a.context->pt[(int)sub->wsi->tsi];
if (pt->is_destroyed)
return G_SOURCE_CONTINUE;
lws_service_fd_tsi(sub->wsi->a.context, &eventfd, sub->wsi->tsi);
if (!lws_gs_valid(pt_to_priv_glib(pt)->idle))
lws_glib_set_idle(pt);
if (pt->destroy_self)
lws_context_destroy(pt->context);
return G_SOURCE_CONTINUE;
}
static const GSourceFuncs lws_glib_source_ops = {
.prepare = NULL,
.check = lws_glib_check,
.dispatch = lws_glib_dispatch,
.finalize = NULL,
};
/*
* This is the callback for a timer object that is set to the earliest scheduled
* lws event... it services any lws scheduled events that are ready, and then
* resets the event loop timer to the earliest remaining event, if any.
*/
static gboolean
lws_glib_hrtimer_cb(void *p)
{
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p;
unsigned int ms;
lws_usec_t us;
lws_pt_lock(pt, __func__);
lws_gs_destroy(pt_to_priv_glib(pt)->hrtimer);
us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
lws_now_usecs());
if (us) {
ms = (unsigned int)(us / LWS_US_PER_MS);
if (!ms)
ms = 1;
lws_glib_set_timeout(pt, ms);
}
lws_pt_unlock(pt);
lws_glib_set_idle(pt);
return FALSE; /* stop it repeating */
}
static gboolean
lws_glib_idle_timer_cb(void *p)
{
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p;
if (pt->is_destroyed)
return FALSE;
lws_service_do_ripe_rxflow(pt);
lws_glib_hrtimer_cb(pt);
/*
* 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);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid))
return TRUE;
}
if (pt->destroy_self)
lws_context_destroy(pt->context);
/*
* For glib, this disables the idle callback. Otherwise we keep
* coming back here immediately endlessly.
*
* We reenable the idle callback on the next network or scheduled event
*/
lws_gs_destroy(pt_to_priv_glib(pt)->idle);
return FALSE;
}
void
lws_glib_sigint_cb(void *ctx)
{
struct lws_context_per_thread *pt = ctx;
pt->inside_service = 1;
if (pt->context->eventlib_signal_cb) {
pt->context->eventlib_signal_cb(NULL, 0);
return;
}
if (!pt->event_loop_foreign)
g_main_loop_quit(pt_to_loop(pt));
}
static int
elops_init_context_glib(struct lws_context *context,
const struct lws_context_creation_info *info)
{
// int n;
context->eventlib_signal_cb = info->signal_cb;
// for (n = 0; n < context->count_threads; n++)
// pt_to_priv_glib(&context->pt[n])->w_sigint.context = context;
return 0;
}
static int
elops_accept_glib(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
struct lws_wsi_eventlibs_glib *wsipr = wsi_to_priv_glib(wsi);
int fd;
assert(!wsi_to_subclass(wsi));
wsi_to_subclass(wsi) = (struct lws_io_watcher_glib_subclass *)
g_source_new((GSourceFuncs *)&lws_glib_source_ops,
sizeof(*wsi_to_subclass(wsi)));
if (!wsi_to_subclass(wsi))
return 1;
wsipr->w_read.context = wsi->a.context;
wsi_to_subclass(wsi)->wsi = wsi;
if (wsi->role_ops->file_handle)
fd = wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
wsi_to_subclass(wsi)->tag = g_source_add_unix_fd(wsi_to_gsource(wsi),
fd, (GIOCondition)LWS_POLLIN);
wsipr->w_read.actual_events = LWS_POLLIN;
g_source_set_callback(wsi_to_gsource(wsi),
G_SOURCE_FUNC(lws_service_fd), wsi->a.context, NULL);
g_source_attach(wsi_to_gsource(wsi), pt_to_g_main_context(pt));
return 0;
}
static int
elops_listen_init_glib(struct lws_dll2 *d, void *user)
{
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
elops_accept_glib(wsi);
return 0;
}
static int
elops_init_pt_glib(struct lws_context *context, void *_loop, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_glib *ptpr = pt_to_priv_glib(pt);
GMainLoop *loop = (GMainLoop *)_loop;
if (!loop)
loop = g_main_loop_new(NULL, 0);
else
context->pt[tsi].event_loop_foreign = 1;
if (!loop) {
lwsl_cx_err(context, "creating glib loop failed");
return -1;
}
ptpr->loop = loop;
lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_glib);
lws_glib_set_idle(pt);
/* Register the signal watcher unless it's a foreign loop */
if (pt->event_loop_foreign)
return 0;
ptpr->sigint.tag = g_unix_signal_add(SIGINT,
G_SOURCE_FUNC(lws_glib_sigint_cb), pt);
return 0;
}
/*
* We are changing the event wait for this guy
*/
static void
elops_io_glib(struct lws *wsi, unsigned int flags)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
struct lws_wsi_eventlibs_glib *wsipr = wsi_to_priv_glib(wsi);
GIOCondition cond = wsipr->w_read.actual_events | G_IO_ERR;
if (!pt_to_loop(pt) || wsi->a.context->being_destroyed ||
pt->is_destroyed)
return;
if (!wsi_to_subclass(wsi))
return;
/*
* We are being given individual set / clear operations using
* LWS_EV_ common namespace, convert them to glib namespace bitfield
*/
if (flags & LWS_EV_READ) {
if (flags & LWS_EV_STOP)
cond &= (unsigned int)~(G_IO_IN | G_IO_HUP);
else
cond |= G_IO_IN | G_IO_HUP;
}
if (flags & LWS_EV_WRITE) {
if (flags & LWS_EV_STOP)
cond &= (unsigned int)~G_IO_OUT;
else
cond |= G_IO_OUT;
}
wsipr->w_read.actual_events = (uint8_t)cond;
lwsl_wsi_debug(wsi, "fd %d, 0x%x/0x%x", wsi->desc.sockfd,
flags, (int)cond);
g_source_modify_unix_fd(wsi_to_gsource(wsi), wsi_to_subclass(wsi)->tag,
cond);
}
static void
elops_run_pt_glib(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
if (pt_to_loop(pt))
g_main_loop_run(pt_to_loop(pt));
}
static void
elops_destroy_wsi_glib(struct lws *wsi)
{
struct lws_context_per_thread *pt;
if (!wsi)
return;
pt = &wsi->a.context->pt[(int)wsi->tsi];
if (pt->is_destroyed)
return;
if (!wsi_to_gsource(wsi))
return;
if (wsi_to_subclass(wsi)->tag) {
g_source_remove_unix_fd(wsi_to_gsource(wsi),
wsi_to_subclass(wsi)->tag);
wsi_to_subclass(wsi)->tag = NULL;
}
g_source_destroy(wsi_to_gsource(wsi));
g_source_unref(wsi_to_gsource(wsi));
wsi_to_subclass(wsi) = NULL;
}
static int
elops_listen_destroy_glib(struct lws_dll2 *d, void *user)
{
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
elops_destroy_wsi_glib(wsi);
return 0;
}
static void
elops_destroy_pt_glib(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_glib *ptpr = pt_to_priv_glib(pt);
if (!pt_to_loop(pt))
return;
lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_glib);
lws_gs_destroy(ptpr->idle);
lws_gs_destroy(ptpr->hrtimer);
if (!pt->event_loop_foreign) {
g_main_loop_quit(pt_to_loop(pt));
lws_gs_destroy(ptpr->sigint);
g_main_loop_unref(pt_to_loop(pt));
}
pt_to_loop(pt) = NULL;
}
static int
elops_destroy_context2_glib(struct lws_context *context)
{
struct lws_context_per_thread *pt = &context->pt[0];
int n;
for (n = 0; n < (int)context->count_threads; n++) {
if (!pt->event_loop_foreign)
g_main_loop_quit(pt_to_loop(pt));
pt++;
}
return 0;
}
static int
elops_wsi_logical_close_glib(struct lws *wsi)
{
elops_destroy_wsi_glib(wsi);
return 0;
}
static const struct lws_event_loop_ops event_loop_ops_glib = {
/* name */ "glib",
/* init_context */ elops_init_context_glib,
/* destroy_context1 */ NULL,
/* destroy_context2 */ elops_destroy_context2_glib,
/* init_vhost_listen_wsi */ elops_accept_glib,
/* init_pt */ elops_init_pt_glib,
/* wsi_logical_close */ elops_wsi_logical_close_glib,
/* check_client_connect_ok */ NULL,
/* close_handle_manually */ NULL,
/* accept */ elops_accept_glib,
/* io */ elops_io_glib,
/* run_pt */ elops_run_pt_glib,
/* destroy_pt */ elops_destroy_pt_glib,
/* destroy wsi */ elops_destroy_wsi_glib,
/* foreign_thread */ NULL,
/* fake_POLLIN */ NULL,
/* flags */ LELOF_DESTROY_FINAL,
/* evlib_size_ctx */ 0,
/* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_glib),
/* evlib_size_vh */ 0,
/* evlib_size_wsi */ sizeof(struct lws_io_watcher_glib),
};
#if defined(LWS_WITH_EVLIB_PLUGINS)
LWS_VISIBLE
#endif
const lws_plugin_evlib_t evlib_glib = {
.hdr = {
"glib event loop",
"lws_evlib_plugin",
LWS_BUILD_HASH,
LWS_PLUGIN_API_MAGIC
},
.ops = &event_loop_ops_glib
};

View File

@ -0,0 +1,61 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <glib.h>
typedef struct lws_glib_tag {
GSource *gs;
guint tag;
} lws_glib_tag_t;
struct lws_pt_eventlibs_glib {
GMainLoop *loop;
lws_glib_tag_t hrtimer;
lws_glib_tag_t sigint;
lws_glib_tag_t idle;
//struct lws_signal_watcher_libuv w_sigint;
};
struct lws_io_watcher_glib_subclass {
GSource base;
struct lws *wsi;
gpointer tag;
};
/*
* One of these is embedded in each wsi
*/
struct lws_io_watcher_glib {
struct lws_io_watcher_glib_subclass *source; /* these are created and destroyed by glib */
struct lws_context *context;
uint8_t actual_events;
};
struct lws_wsi_eventlibs_glib {
struct lws_io_watcher_glib w_read;
};

View File

@ -0,0 +1,88 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
set(LWS_LIBEV_LIBRARIES CACHE PATH "Path to the libev library")
set(LWS_LIBEV_INCLUDE_DIRS CACHE PATH "Path to the libev include directory")
if (NOT LIBEV_FOUND)
find_path(LIBEV_INCLUDE_DIRS NAMES ev.h)
find_library(LIBEV_LIBRARIES NAMES ev)
endif()
message("libev include dir: ${LIBEV_INCLUDE_DIRS}")
message("libev libraries: ${LIBEV_LIBRARIES}")
include_directories("${LIBEV_INCLUDE_DIRS}")
if ("${LWS_LIBEV_LIBRARIES}" STREQUAL "" OR "${LWS_LIBEV_INCLUDE_DIRS}" STREQUAL "")
else()
set(LIBEV_LIBRARIES ${LWS_LIBEV_LIBRARIES})
set(LIBEV_INCLUDE_DIRS ${LWS_LIBEV_INCLUDE_DIRS})
endif()
if (LWS_WITH_EVLIB_PLUGINS)
create_evlib_plugin(
evlib_ev
libev.c
private-lib-event-libs-libev.h
${LIBEV_LIBRARIES})
else()
list(APPEND LIB_LIST ${LIBEV_LIBRARIES})
list(APPEND SOURCES
event-libs/libev/libev.c)
# see README.build.md for discussion of why of the supported event libs,
# only libev cannot cope with -Werror
set_source_files_properties(event-libs/libev/libev.c
PROPERTIES COMPILE_FLAGS "-Wno-error" )
endif()
set(CMAKE_REQUIRED_LIBRARIES ${LIB_LIST})
CHECK_C_SOURCE_COMPILES(
"#include <ev.h>
int main(int argc, char **argv) { return EVBACKEND_LINUXAIO; }
" LWS_HAVE_EVBACKEND_LINUXAIO)
CHECK_C_SOURCE_COMPILES(
"#include <ev.h>
int main(int argc, char **argv) { return EVBACKEND_IOURING; }
" LWS_HAVE_EVBACKEND_IOURING)
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()
set(LWS_HAVE_EVBACKEND_LINUXAIO ${LWS_HAVE_EVBACKEND_LINUXAIO} PARENT_SCOPE)
set(LWS_HAVE_EVBACKEND_IOURING ${LWS_HAVE_EVBACKEND_IOURING} PARENT_SCOPE)

View File

@ -0,0 +1,468 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-event-libs-libev.h"
#define pt_to_priv_ev(_pt) ((struct lws_pt_eventlibs_libev *)(_pt)->evlib_pt)
#define vh_to_priv_ev(_vh) ((struct lws_vh_eventlibs_libev *)(_vh)->evlib_vh)
#define wsi_to_priv_ev(_w) ((struct lws_wsi_eventlibs_libev *)(_w)->evlib_wsi)
static void
lws_ev_hrtimer_cb(struct ev_loop *loop, struct ev_timer *watcher, int revents)
{
struct lws_pt_eventlibs_libev *ptpr = lws_container_of(watcher,
struct lws_pt_eventlibs_libev, hrtimer);
struct lws_context_per_thread *pt = ptpr->pt;
lws_usec_t us;
lws_pt_lock(pt, __func__);
us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
lws_now_usecs());
if (us) {
ev_timer_set(&ptpr->hrtimer, ((float)us) / 1000000.0, 0);
ev_timer_start(ptpr->io_loop, &ptpr->hrtimer);
}
lws_pt_unlock(pt);
}
static void
lws_ev_idle_cb(struct ev_loop *loop, struct ev_idle *handle, int revents)
{
struct lws_pt_eventlibs_libev *ptpr = lws_container_of(handle,
struct lws_pt_eventlibs_libev, idle);
struct lws_context_per_thread *pt = ptpr->pt;
int reschedule = 0;
lws_usec_t us;
lws_service_do_ripe_rxflow(pt);
/*
* 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 */
reschedule = _lws_plat_service_forced_tsi(pt->context, pt->tid);
/* account for hrtimer */
lws_pt_lock(pt, __func__);
us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
lws_now_usecs());
if (us) {
ev_timer_set(&ptpr->hrtimer, ((float)us) / 1000000.0, 0);
ev_timer_start(ptpr->io_loop, &ptpr->hrtimer);
}
lws_pt_unlock(pt);
/* there is nobody who needs service forcing, shut down idle */
if (!reschedule)
ev_idle_stop(loop, handle);
if (pt->destroy_self)
lws_context_destroy(pt->context);
}
static void
lws_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents)
{
struct lws_io_watcher_libev *lws_io = lws_container_of(watcher,
struct lws_io_watcher_libev, watcher);
struct lws_context *context = lws_io->context;
struct lws_pt_eventlibs_libev *ptpr;
struct lws_context_per_thread *pt;
struct lws_pollfd eventfd;
struct lws *wsi;
int tsi = 0;
if (revents & EV_ERROR)
return;
eventfd.fd = watcher->fd;
eventfd.events = 0;
eventfd.revents = EV_NONE;
if (revents & EV_READ) {
eventfd.events |= LWS_POLLIN;
eventfd.revents |= LWS_POLLIN;
}
if (revents & EV_WRITE) {
eventfd.events |= LWS_POLLOUT;
eventfd.revents |= LWS_POLLOUT;
}
wsi = wsi_from_fd(context, watcher->fd);
if (wsi)
tsi = (int)wsi->tsi;
pt = &context->pt[tsi];
ptpr = pt_to_priv_ev(pt);
lws_service_fd_tsi(context, &eventfd, tsi);
ev_idle_start(ptpr->io_loop, &ptpr->idle);
}
void
lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents)
{
struct lws_context *context = watcher->data;
if (context->eventlib_signal_cb) {
context->eventlib_signal_cb((void *)watcher, watcher->signum);
return;
}
ev_break(loop, EVBREAK_ALL);
}
static int
elops_listen_init_ev(struct lws_dll2 *d, void *user)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
struct lws_context *context = (struct lws_context *)user;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt);
struct lws_wsi_eventlibs_libev *w = wsi_to_priv_ev(wsi);
struct lws_vhost *vh = wsi->a.vhost;
w->w_read.context = context;
w->w_write.context = context;
vh_to_priv_ev(vh)->w_accept.context = context;
ev_io_init(&vh_to_priv_ev(vh)->w_accept.watcher,
lws_accept_cb, wsi->desc.sockfd, EV_READ);
ev_io_start(ptpr->io_loop, &vh_to_priv_ev(vh)->w_accept.watcher);
#endif
return 0;
}
static int
elops_init_pt_ev(struct lws_context *context, void *_loop, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt);
struct ev_signal *w_sigint = &ptpr->w_sigint.watcher;
struct ev_loop *loop = (struct ev_loop *)_loop;
const char *backend_name;
unsigned int backend;
int status = 0;
lwsl_cx_info(context, "loop %p", _loop);
ptpr->pt = pt;
if (!loop)
loop = ev_loop_new(0);
else
context->pt[tsi].event_loop_foreign = 1;
if (!loop) {
lwsl_cx_err(context, "creating event base failed");
return -1;
}
ptpr->io_loop = loop;
lws_vhost_foreach_listen_wsi(context, context, elops_listen_init_ev);
/* Register the signal watcher unless it's a foreign loop */
if (!context->pt[tsi].event_loop_foreign) {
ev_signal_init(w_sigint, lws_ev_sigint_cb, SIGINT);
w_sigint->data = context;
ev_signal_start(loop, w_sigint);
}
backend = ev_backend(loop);
switch (backend) {
case EVBACKEND_SELECT:
backend_name = "select";
break;
case EVBACKEND_POLL:
backend_name = "poll";
break;
case EVBACKEND_EPOLL:
backend_name = "epoll";
break;
#if defined(LWS_HAVE_EVBACKEND_LINUXAIO)
case EVBACKEND_LINUXAIO:
backend_name = "Linux AIO";
break;
#endif
#if defined(LWS_HAVE_EVBACKEND_IOURING)
case EVBACKEND_IOURING:
backend_name = "Linux io_uring";
break;
#endif
case EVBACKEND_KQUEUE:
backend_name = "kqueue";
break;
case EVBACKEND_DEVPOLL:
backend_name = "/dev/poll";
break;
case EVBACKEND_PORT:
backend_name = "Solaris 10 \"port\"";
break;
default:
backend_name = "Unknown libev backend";
break;
}
lwsl_cx_info(context, " libev backend: %s", backend_name);
(void)backend_name;
ev_timer_init(&ptpr->hrtimer, lws_ev_hrtimer_cb, 0, 0);
ptpr->hrtimer.data = pt;
ev_idle_init(&ptpr->idle, lws_ev_idle_cb);
return status;
}
static int
elops_listen_destroy_ev(struct lws_dll2 *d, void *user)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
struct lws_context *context = (struct lws_context *)user;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt);
struct lws_vhost *vh = wsi->a.vhost;
ev_io_stop(ptpr->io_loop, &vh_to_priv_ev(vh)->w_accept.watcher);
#endif
return 0;
}
static void
elops_destroy_pt_ev(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt);
lws_vhost_foreach_listen_wsi(context, context, elops_listen_destroy_ev);
/* static assets */
ev_timer_stop(ptpr->io_loop, &ptpr->hrtimer);
ev_idle_stop(ptpr->io_loop, &ptpr->idle);
if (!pt->event_loop_foreign)
ev_signal_stop(ptpr->io_loop, &ptpr->w_sigint.watcher);
}
static int
elops_init_context_ev(struct lws_context *context,
const struct lws_context_creation_info *info)
{
int n;
context->eventlib_signal_cb = info->signal_cb;
for (n = 0; n < context->count_threads; n++)
pt_to_priv_ev(&context->pt[n])->w_sigint.context = context;
return 0;
}
static int
elops_accept_ev(struct lws *wsi)
{
struct lws_wsi_eventlibs_libev *w = wsi_to_priv_ev(wsi);
int fd;
if (wsi->role_ops->file_handle)
fd = wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
w->w_read.context = wsi->a.context;
w->w_write.context = wsi->a.context;
ev_io_init(&w->w_read.watcher, lws_accept_cb, fd, EV_READ);
ev_io_init(&w->w_write.watcher, lws_accept_cb, fd, EV_WRITE);
return 0;
}
static void
elops_io_ev(struct lws *wsi, unsigned int flags)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt);
struct lws_wsi_eventlibs_libev *w = wsi_to_priv_ev(wsi);
lwsl_wsi_debug(wsi, "%s flags 0x%x %p %d", wsi->role_ops->name, flags,
ptpr->io_loop,
pt->is_destroyed);
if (!ptpr->io_loop || pt->is_destroyed)
return;
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
if (flags & LWS_EV_START) {
if (flags & LWS_EV_WRITE)
ev_io_start(ptpr->io_loop, &w->w_write.watcher);
if (flags & LWS_EV_READ)
ev_io_start(ptpr->io_loop, &w->w_read.watcher);
} else {
if (flags & LWS_EV_WRITE)
ev_io_stop(ptpr->io_loop, &w->w_write.watcher);
if (flags & LWS_EV_READ)
ev_io_stop(ptpr->io_loop, &w->w_read.watcher);
}
if (pt->destroy_self)
lws_context_destroy(pt->context);
}
static void
elops_run_pt_ev(struct lws_context *context, int tsi)
{
if (pt_to_priv_ev(&context->pt[tsi])->io_loop)
ev_run(pt_to_priv_ev(&context->pt[tsi])->io_loop, 0);
}
static int
elops_destroy_context2_ev(struct lws_context *context)
{
struct lws_context_per_thread *pt;
struct lws_pt_eventlibs_libev *ptpr;
int n, m;
for (n = 0; n < context->count_threads; n++) {
int budget = 1000;
pt = &context->pt[n];
ptpr = pt_to_priv_ev(pt);
/* only for internal loops... */
if (pt->event_loop_foreign || !ptpr->io_loop)
continue;
if (!context->evlib_finalize_destroy_after_int_loops_stop) {
ev_break(ptpr->io_loop, EVBREAK_ONE);
continue;
}
while (budget-- &&
(m = ev_run(ptpr->io_loop, 0)))
;
ev_loop_destroy(ptpr->io_loop);
}
return 0;
}
static int
elops_init_vhost_listen_wsi_ev(struct lws *wsi)
{
struct lws_wsi_eventlibs_libev *w;
int fd;
if (!wsi) {
assert(0);
return 0;
}
w = wsi_to_priv_ev(wsi);
w->w_read.context = wsi->a.context;
w->w_write.context = wsi->a.context;
if (wsi->role_ops->file_handle)
fd = wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
ev_io_init(&w->w_read.watcher, lws_accept_cb, fd, EV_READ);
//ev_io_init(&w->w_write.watcher, lws_accept_cb, fd, EV_WRITE);
elops_io_ev(wsi, LWS_EV_START | LWS_EV_READ);
return 0;
}
static void
elops_destroy_wsi_ev(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libev *ptpr = pt_to_priv_ev(pt);
struct lws_wsi_eventlibs_libev *w = wsi_to_priv_ev(wsi);
ev_io_stop(ptpr->io_loop, &w->w_read.watcher);
ev_io_stop(ptpr->io_loop, &w->w_write.watcher);
}
static int
elops_wsi_logical_close_ev(struct lws *wsi)
{
elops_destroy_wsi_ev(wsi);
return 0;
}
static const struct lws_event_loop_ops event_loop_ops_ev = {
/* name */ "libev",
/* init_context */ elops_init_context_ev,
/* destroy_context1 */ NULL,
/* destroy_context2 */ elops_destroy_context2_ev,
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_ev,
/* init_pt */ elops_init_pt_ev,
/* wsi_logical_close */ elops_wsi_logical_close_ev,
/* check_client_connect_ok */ NULL,
/* close_handle_manually */ NULL,
/* accept */ elops_accept_ev,
/* io */ elops_io_ev,
/* run_pt */ elops_run_pt_ev,
/* destroy_pt */ elops_destroy_pt_ev,
/* destroy wsi */ elops_destroy_wsi_ev,
/* foreign_thread */ NULL,
/* fake_POLLIN */ NULL,
/* flags */ 0,
/* evlib_size_ctx */ 0,
/* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libev),
/* evlib_size_vh */ sizeof(struct lws_vh_eventlibs_libev),
/* evlib_size_wsi */ sizeof(struct lws_wsi_eventlibs_libev),
};
#if defined(LWS_WITH_EVLIB_PLUGINS)
LWS_VISIBLE
#endif
const lws_plugin_evlib_t evlib_ev = {
.hdr = {
"libev event loop",
"lws_evlib_plugin",
LWS_BUILD_HASH,
LWS_PLUGIN_API_MAGIC
},
.ops = &event_loop_ops_ev
};

View File

@ -0,0 +1,62 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <ev.h>
#define LWS_EV_REFCOUNT_STATIC_HANDLE_NEW(_x, _ctx) \
{ (_x)->data = _ctx; \
_ctx->count_event_loop_static_asset_handles++; }
#define LWS_EV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x) \
((struct lws_context *)(_x)->data)))
#define LWS_EV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \
(--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(_x)-> \
count_event_loop_static_asset_handles))
struct lws_signal_watcher_libev {
ev_signal watcher;
struct lws_context *context;
};
struct lws_pt_eventlibs_libev {
struct ev_loop *io_loop;
struct ev_timer hrtimer;
struct ev_idle idle;
struct lws_signal_watcher_libev w_sigint;
struct lws_context_per_thread *pt;
};
struct lws_io_watcher_libev {
ev_io watcher;
struct lws_context *context;
};
struct lws_vh_eventlibs_libev {
struct lws_io_watcher_libev w_accept;
};
struct lws_wsi_eventlibs_libev {
struct lws_io_watcher_libev w_read;
struct lws_io_watcher_libev w_write;
};

View File

@ -0,0 +1,72 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
set(LWS_LIBEVENT_INCLUDE_DIRS CACHE PATH "Path to the libevent include directory")
set(LWS_LIBEVENT_LIBRARIES CACHE PATH "Path to the libevent library")
if (NOT LIBEVENT_FOUND)
find_path(LIBEVENT_INCLUDE_DIRS NAMES event2/event.h)
find_library(LIBEVENT_LIBRARIES NAMES event)
endif()
message("libevent include dir: ${LIBEVENT_INCLUDE_DIRS}")
message("libevent libraries: ${LIBEVENT_LIBRARIES}")
include_directories("${LIBEVENT_INCLUDE_DIRS}")
if ("${LWS_LIBEVENT_LIBRARIES}" STREQUAL "" OR "${LWS_LIBEVENT_INCLUDE_DIRS}" STREQUAL "")
else()
set(LIBEVENT_LIBRARIES ${LWS_LIBEVENT_LIBRARIES})
set(LIBEVENT_INCLUDE_DIRS ${LWS_LIBEVENT_INCLUDE_DIRS})
endif()
if (LWS_WITH_EVLIB_PLUGINS)
create_evlib_plugin(evlib_event
libevent.c
private-lib-event-libs-libevent.h
${LIBEVENT_LIBRARIES})
else()
list(APPEND LIB_LIST ${LIBEVENT_LIBRARIES})
set(LIBEVENT_FOUND 1 PARENT_SCOPE)
if (LWS_WITH_NETWORK)
list(APPEND SOURCES
event-libs/libevent/libevent.c)
endif()
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,520 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-event-libs-libevent.h"
#define pt_to_priv_event(_pt) ((struct lws_pt_eventlibs_libevent *)(_pt)->evlib_pt)
#define wsi_to_priv_event(_w) ((struct lws_wsi_eventlibs_libevent *)(_w)->evlib_wsi)
static void
lws_event_hrtimer_cb(evutil_socket_t fd, short event, void *p)
{
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p;
struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt);
struct timeval tv;
lws_usec_t us;
lws_pt_lock(pt, __func__);
us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
lws_now_usecs());
if (us) {
#if defined(__APPLE__)
tv.tv_sec = (int)(us / LWS_US_PER_SEC);
tv.tv_usec = (int)(us - (tv.tv_sec * LWS_US_PER_SEC));
#else
tv.tv_sec = (long)(us / LWS_US_PER_SEC);
tv.tv_usec = (long)(us - (tv.tv_sec * LWS_US_PER_SEC));
#endif
evtimer_add(ptpr->hrtimer, &tv);
}
lws_pt_unlock(pt);
}
static void
lws_event_idle_timer_cb(evutil_socket_t fd, short event, void *p)
{
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p;
struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt);
struct timeval tv;
lws_usec_t us;
if (pt->is_destroyed)
return;
lws_service_do_ripe_rxflow(pt);
/*
* 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);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
/* yes... come back again later */
tv.tv_sec = 0;
tv.tv_usec = 1000;
evtimer_add(ptpr->idle_timer, &tv);
return;
}
}
/* account for hrtimer */
lws_pt_lock(pt, __func__);
us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
lws_now_usecs());
if (us) {
tv.tv_sec = (suseconds_t)(us / LWS_US_PER_SEC);
tv.tv_usec = (suseconds_t)(us - (tv.tv_sec * LWS_US_PER_SEC));
evtimer_add(ptpr->hrtimer, &tv);
}
lws_pt_unlock(pt);
if (pt->destroy_self)
lws_context_destroy(pt->context);
}
static void
lws_event_cb(evutil_socket_t sock_fd, short revents, void *ctx)
{
struct lws_signal_watcher_libevent *lws_io =
(struct lws_signal_watcher_libevent *)ctx;
struct lws_context *context = lws_io->context;
struct lws_context_per_thread *pt;
struct lws_pollfd eventfd;
struct timeval tv;
struct lws *wsi;
if (revents & EV_TIMEOUT)
return;
/* !!! EV_CLOSED doesn't exist in libevent2 */
#if LIBEVENT_VERSION_NUMBER < 0x02000000
if (revents & EV_CLOSED) {
event_del(lws_io->event.watcher);
event_free(lws_io->event.watcher);
return;
}
#endif
eventfd.fd = sock_fd;
eventfd.events = 0;
eventfd.revents = 0;
if (revents & EV_READ) {
eventfd.events |= LWS_POLLIN;
eventfd.revents |= LWS_POLLIN;
}
if (revents & EV_WRITE) {
eventfd.events |= LWS_POLLOUT;
eventfd.revents |= LWS_POLLOUT;
}
wsi = wsi_from_fd(context, sock_fd);
if (!wsi)
return;
pt = &context->pt[(int)wsi->tsi];
if (pt->is_destroyed)
return;
lws_service_fd_tsi(context, &eventfd, wsi->tsi);
if (pt->destroy_self) {
lwsl_cx_notice(context, "pt destroy self coming true");
lws_context_destroy(pt->context);
return;
}
/* set the idle timer for 1ms ahead */
tv.tv_sec = 0;
tv.tv_usec = 1000;
evtimer_add(pt_to_priv_event(pt)->idle_timer, &tv);
}
void
lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, void *ctx)
{
struct lws_context_per_thread *pt = ctx;
struct event *signal = pt_to_priv_event(pt)->w_sigint.watcher;
if (pt->context->eventlib_signal_cb) {
pt->context->eventlib_signal_cb((void *)(lws_intptr_t)sock_fd,
event_get_signal(signal));
return;
}
if (!pt->event_loop_foreign)
event_base_loopbreak(pt_to_priv_event(pt)->io_loop);
}
static int
elops_listen_init_event(struct lws_dll2 *d, void *user)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
struct lws_context *context = (struct lws_context *)user;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt);
struct lws_io_watcher_libevent *w_read =
&(wsi_to_priv_event(wsi)->w_read);
w_read->context = context;
w_read->watcher = event_new(ptpr->io_loop, wsi->desc.sockfd,
(EV_READ | EV_PERSIST), lws_event_cb, w_read);
event_add(w_read->watcher, NULL);
w_read->set = 1;
#endif
return 0;
}
static int
elops_init_pt_event(struct lws_context *context, void *_loop, int tsi)
{
struct event_base *loop = (struct event_base *)_loop;
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt);
lwsl_cx_info(context, "loop %p", _loop);
if (!loop)
loop = event_base_new();
else
context->pt[tsi].event_loop_foreign = 1;
if (!loop) {
lwsl_cx_err(context, "creating event base failed");
return -1;
}
ptpr->io_loop = loop;
lws_vhost_foreach_listen_wsi(context, context, elops_listen_init_event);
/* static event loop objects */
ptpr->hrtimer = event_new(loop, -1, EV_PERSIST,
lws_event_hrtimer_cb, pt);
ptpr->idle_timer = event_new(loop, -1, 0,
lws_event_idle_timer_cb, pt);
{
struct timeval tv;
tv.tv_sec = (long)0;
tv.tv_usec = (long)1000;
evtimer_add(ptpr->hrtimer, &tv);
}
/* Register the signal watcher unless it's a foreign loop */
if (pt->event_loop_foreign)
return 0;
ptpr->w_sigint.watcher = evsignal_new(loop, SIGINT,
lws_event_sigint_cb, pt);
event_add(ptpr->w_sigint.watcher, NULL);
return 0;
}
static int
elops_init_context_event(struct lws_context *context,
const struct lws_context_creation_info *info)
{
int n;
context->eventlib_signal_cb = info->signal_cb;
for (n = 0; n < context->count_threads; n++)
pt_to_priv_event(&context->pt[n])->w_sigint.context = context;
return 0;
}
static int
elops_accept_event(struct lws *wsi)
{
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt;
struct lws_pt_eventlibs_libevent *ptpr;
struct lws_wsi_eventlibs_libevent *wpr = wsi_to_priv_event(wsi);
evutil_socket_t fd;
wpr->w_read.context = context;
wpr->w_write.context = context;
// Initialize the event
pt = &context->pt[(int)wsi->tsi];
ptpr = pt_to_priv_event(pt);
if (wsi->role_ops->file_handle)
fd = (evutil_socket_t)(ev_intptr_t) wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
wpr->w_read.watcher = event_new(ptpr->io_loop, fd,
(EV_READ | EV_PERSIST), lws_event_cb, &wpr->w_read);
wpr->w_write.watcher = event_new(ptpr->io_loop, fd,
(EV_WRITE | EV_PERSIST), lws_event_cb, &wpr->w_write);
return 0;
}
static void
elops_io_event(struct lws *wsi, unsigned int flags)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt);
struct lws_wsi_eventlibs_libevent *wpr = wsi_to_priv_event(wsi);
if (!ptpr->io_loop || wsi->a.context->being_destroyed ||
pt->is_destroyed)
return;
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
if (flags & LWS_EV_START) {
if ((flags & LWS_EV_WRITE) && !wpr->w_write.set) {
event_add(wpr->w_write.watcher, NULL);
wpr->w_write.set = 1;
}
if ((flags & LWS_EV_READ) && !wpr->w_read.set) {
event_add(wpr->w_read.watcher, NULL);
wpr->w_read.set = 1;
}
} else {
if ((flags & LWS_EV_WRITE) && wpr->w_write.set) {
event_del(wpr->w_write.watcher);
wpr->w_write.set = 0;
}
if ((flags & LWS_EV_READ) && wpr->w_read.set) {
event_del(wpr->w_read.watcher);
wpr->w_read.set = 0;
}
}
}
static void
elops_run_pt_event(struct lws_context *context, int tsi)
{
/* Run / Dispatch the event_base loop */
if (pt_to_priv_event(&context->pt[tsi])->io_loop)
event_base_dispatch(
pt_to_priv_event(&context->pt[tsi])->io_loop);
}
static int
elops_listen_destroy_event(struct lws_dll2 *d, void *user)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
struct lws_wsi_eventlibs_libevent *w = wsi_to_priv_event(wsi);
event_free(w->w_read.watcher);
w->w_read.watcher = NULL;
event_free(w->w_write.watcher);
w->w_write.watcher = NULL;
#endif
return 0;
}
static void
elops_destroy_pt_event(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_libevent *ptpr = pt_to_priv_event(pt);
if (!ptpr->io_loop)
return;
lws_vhost_foreach_listen_wsi(context, context, elops_listen_destroy_event);
event_free(ptpr->hrtimer);
event_free(ptpr->idle_timer);
if (!pt->event_loop_foreign) {
event_del(ptpr->w_sigint.watcher);
event_free(ptpr->w_sigint.watcher);
event_base_loopexit(ptpr->io_loop, NULL);
// event_base_free(pt->event.io_loop);
// pt->event.io_loop = NULL;
lwsl_cx_notice(context, "set to exit loop");
}
}
static void
elops_destroy_wsi_event(struct lws *wsi)
{
struct lws_context_per_thread *pt;
struct lws_wsi_eventlibs_libevent *w;
if (!wsi)
return;
pt = &wsi->a.context->pt[(int)wsi->tsi];
if (pt->is_destroyed)
return;
w = wsi_to_priv_event(wsi);
if (w->w_read.watcher) {
event_free(w->w_read.watcher);
w->w_read.watcher = NULL;
}
if (w->w_write.watcher) {
event_free(w->w_write.watcher);
w->w_write.watcher = NULL;
}
}
static int
elops_wsi_logical_close_event(struct lws *wsi)
{
elops_destroy_wsi_event(wsi);
return 0;
}
static int
elops_init_vhost_listen_wsi_event(struct lws *wsi)
{
struct lws_context_per_thread *pt;
struct lws_pt_eventlibs_libevent *ptpr;
struct lws_wsi_eventlibs_libevent *w;
evutil_socket_t fd;
if (!wsi) {
assert(0);
return 0;
}
w = wsi_to_priv_event(wsi);
w->w_read.context = wsi->a.context;
w->w_write.context = wsi->a.context;
pt = &wsi->a.context->pt[(int)wsi->tsi];
ptpr = pt_to_priv_event(pt);
if (wsi->role_ops->file_handle)
fd = (evutil_socket_t) wsi->desc.filefd;
else
fd = wsi->desc.sockfd;
w->w_read.watcher = event_new(ptpr->io_loop, fd, (EV_READ | EV_PERSIST),
lws_event_cb, &w->w_read);
w->w_write.watcher = event_new(ptpr->io_loop, fd,
(EV_WRITE | EV_PERSIST),
lws_event_cb, &w->w_write);
elops_io_event(wsi, LWS_EV_START | LWS_EV_READ);
return 0;
}
static int
elops_destroy_context2_event(struct lws_context *context)
{
struct lws_context_per_thread *pt;
struct lws_pt_eventlibs_libevent *ptpr;
int n, m;
for (n = 0; n < context->count_threads; n++) {
int budget = 1000;
pt = &context->pt[n];
ptpr = pt_to_priv_event(pt);
/* only for internal loops... */
if (pt->event_loop_foreign || !ptpr->io_loop)
continue;
if (!context->evlib_finalize_destroy_after_int_loops_stop) {
event_base_loopexit(ptpr->io_loop, NULL);
continue;
}
while (budget-- &&
(m = event_base_loop(ptpr->io_loop, EVLOOP_NONBLOCK)))
;
lwsl_cx_info(context, "event_base_free");
event_base_free(ptpr->io_loop);
ptpr->io_loop = NULL;
}
return 0;
}
static const struct lws_event_loop_ops event_loop_ops_event = {
/* name */ "libevent",
/* init_context */ elops_init_context_event,
/* destroy_context1 */ NULL,
/* destroy_context2 */ elops_destroy_context2_event,
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_event,
/* init_pt */ elops_init_pt_event,
/* wsi_logical_close */ elops_wsi_logical_close_event,
/* check_client_connect_ok */ NULL,
/* close_handle_manually */ NULL,
/* accept */ elops_accept_event,
/* io */ elops_io_event,
/* run_pt */ elops_run_pt_event,
/* destroy_pt */ elops_destroy_pt_event,
/* destroy wsi */ elops_destroy_wsi_event,
/* foreign_thread */ NULL,
/* fake_POLLIN */ NULL,
/* flags */ 0,
/* evlib_size_ctx */ 0,
/* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libevent),
/* evlib_size_vh */ 0,
/* evlib_size_wsi */ sizeof(struct lws_wsi_eventlibs_libevent),
};
#if defined(LWS_WITH_EVLIB_PLUGINS)
LWS_VISIBLE
#endif
const lws_plugin_evlib_t evlib_event = {
.hdr = {
"libevent event loop",
"lws_evlib_plugin",
LWS_BUILD_HASH,
LWS_PLUGIN_API_MAGIC
},
.ops = &event_loop_ops_event
};

View File

@ -0,0 +1,49 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <event2/event.h>
struct lws_signal_watcher_libevent {
struct event *watcher;
struct lws_context *context;
};
struct lws_pt_eventlibs_libevent {
struct event_base *io_loop;
struct event *hrtimer;
struct event *idle_timer;
struct lws_signal_watcher_libevent w_sigint;
};
struct lws_io_watcher_libevent {
struct event *watcher;
struct lws_context *context;
uint8_t actual_events;
char set;
};
struct lws_wsi_eventlibs_libevent {
struct lws_io_watcher_libevent w_read;
struct lws_io_watcher_libevent w_write;
};

View File

@ -0,0 +1,86 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
set(LWS_LIBUV_LIBRARIES CACHE PATH "Path to the libuv library")
set(LWS_LIBUV_INCLUDE_DIRS CACHE PATH "Path to the libuv include directory")
if ("${LWS_LIBUV_LIBRARIES}" STREQUAL "" OR "${LWS_LIBUV_INCLUDE_DIRS}" STREQUAL "")
if (NOT LIBUV_FOUND)
find_path(LIBUV_INCLUDE_DIRS NAMES uv.h)
find_library(LIBUV_LIBRARIES NAMES uv)
endif()
else()
set(LIBUV_LIBRARIES ${LWS_LIBUV_LIBRARIES})
set(LIBUV_INCLUDE_DIRS ${LWS_LIBUV_INCLUDE_DIRS})
endif()
message("libuv include dir: ${LIBUV_INCLUDE_DIRS}")
message("libuv libraries: ${LIBUV_LIBRARIES}")
include_directories("${LIBUV_INCLUDE_DIRS}")
CHECK_INCLUDE_FILE(uv-version.h LWS_HAVE_UV_VERSION_H)
# libuv changed the location in 1.21.0. Retain both
# checks temporarily to ensure a smooth transition.
if (NOT LWS_HAVE_UV_VERSION_H)
CHECK_INCLUDE_FILE(uv/version.h LWS_HAVE_NEW_UV_VERSION_H)
endif()
if (LWS_WITH_EVLIB_PLUGINS AND LWS_WITH_LIBUV)
create_evlib_plugin(evlib_uv
libuv.c
private-lib-event-libs-libuv.h
${LIBUV_LIBRARIES})
endif()
# wanting libuv in the library is a separate question than
# wanting libuv as a selectable event loop plugin
# we only came here because LWS_WITH_LIBUV or LWS_WITH_LIBUV_INTERNAL
if ((NOT LWS_WITH_EVLIB_PLUGINS) OR LWS_WITH_LIBUV_INTERNAL)
list(APPEND LIB_LIST ${LIBUV_LIBRARIES})
if (LWS_WITH_NETWORK)
list(APPEND SOURCES
event-libs/libuv/libuv.c)
endif()
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()
set(LWS_HAVE_UV_VERSION_H ${LWS_HAVE_UV_VERSION_H} PARENT_SCOPE)
set(LWS_HAVE_NEW_UV_VERSION_H ${LWS_HAVE_NEW_UV_VERSION_H} PARENT_SCOPE)
set(LIBUV_INCLUDE_DIRS ${LIBUV_INCLUDE_DIRS} PARENT_SCOPE)

View File

@ -0,0 +1,952 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-event-libs-libuv.h"
#define pt_to_priv_uv(_pt) ((struct lws_pt_eventlibs_libuv *)(_pt)->evlib_pt)
#define wsi_to_priv_uv(_w) ((struct lws_wsi_eventlibs_libuv *)(_w)->evlib_wsi)
static void
lws_uv_sultimer_cb(uv_timer_t *timer
#if UV_VERSION_MAJOR == 0
, int status
#endif
)
{
struct lws_pt_eventlibs_libuv *ptpr = lws_container_of(timer,
struct lws_pt_eventlibs_libuv, sultimer);
struct lws_context_per_thread *pt = ptpr->pt;
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)
uv_timer_start(&pt_to_priv_uv(pt)->sultimer, lws_uv_sultimer_cb,
LWS_US_TO_MS((uint64_t)us), 0);
lws_pt_unlock(pt);
lws_context_unlock(pt->context);
}
static void
lws_uv_idle(uv_idle_t *handle
#if UV_VERSION_MAJOR == 0
, int status
#endif
)
{ struct lws_pt_eventlibs_libuv *ptpr = lws_container_of(handle,
struct lws_pt_eventlibs_libuv, idle);
struct lws_context_per_thread *pt = ptpr->pt;
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)
uv_timer_start(&pt_to_priv_uv(pt)->sultimer, lws_uv_sultimer_cb,
LWS_US_TO_MS((uint64_t)us), 0);
/* if there is nobody who needs service forcing, shut down idle */
if (lws_service_adjust_timeout(pt->context, 1, pt->tid))
uv_idle_stop(handle);
lws_pt_unlock(pt);
lws_context_unlock(pt->context);
}
static void
lws_io_cb(uv_poll_t *watcher, int status, int revents)
{
struct lws *wsi = (struct lws *)((uv_handle_t *)watcher)->data;
struct lws_context *context = wsi->a.context;
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt);
struct lws_pollfd eventfd;
lws_context_lock(pt->context, __func__);
lws_pt_lock(pt, __func__);
if (pt->is_destroyed)
goto bail;
if (!ptpriv->thread_valid) {
/* record the thread id that gave us our first event */
ptpriv->uv_thread = uv_thread_self();
ptpriv->thread_valid = 1;
}
#if defined(WIN32) || defined(_WIN32)
eventfd.fd = watcher->socket;
#else
eventfd.fd = watcher->io_watcher.fd;
#endif
eventfd.events = 0;
eventfd.revents = 0;
if (status < 0) {
/*
* At this point status will be an UV error, like UV_EBADF,
* we treat all errors as LWS_POLLHUP
*
* You might want to return; instead of servicing the fd in
* some cases */
if (status == UV_EAGAIN)
goto bail;
eventfd.events |= LWS_POLLHUP;
eventfd.revents |= LWS_POLLHUP;
} else {
if (revents & UV_READABLE) {
eventfd.events |= LWS_POLLIN;
eventfd.revents |= LWS_POLLIN;
}
if (revents & UV_WRITABLE) {
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;
}
uv_idle_start(&ptpriv->idle, lws_uv_idle);
return;
bail:
lws_pt_unlock(pt);
lws_context_unlock(pt->context);
}
/*
* This does not actually stop the event loop. The reason is we have to pass
* libuv handle closures through its event loop. So this tries to close all
* wsi, and set a flag; when all the wsi closures are finalized then we
* actually stop the libuv event loops.
*/
static void
lws_libuv_stop(struct lws_context *context)
{
if (context->requested_stop_internal_loops) {
lwsl_cx_err(context, "ignoring");
return;
}
context->requested_stop_internal_loops = 1;
lws_context_destroy(context);
}
static void
lws_uv_signal_handler(uv_signal_t *watcher, int signum)
{
struct lws_context_per_thread *pt = (struct lws_context_per_thread *)
watcher->data;
if (pt->context->eventlib_signal_cb) {
pt->context->eventlib_signal_cb((void *)watcher, signum);
return;
}
lwsl_cx_err(pt->context, "internal signal handler caught signal %d",
signum);
lws_libuv_stop(pt->context);
}
static int
lws_uv_finalize_pt(struct lws_context_per_thread *pt)
{
pt->event_loop_pt_unused = 1;
lwsl_cx_info(pt->context, "thr %d", (int)(pt - pt->context->pt));
lws_context_lock(pt->context, __func__);
if (!--pt->context->undestroyed_threads) {
struct lws_vhost *vh = pt->context->vhost_list;
/*
* eventually, we emptied all the pts...
*/
lwsl_cx_debug(pt->context, "all pts down now");
/* protocols may have initialized libuv objects */
while (vh) {
lws_vhost_destroy1(vh);
vh = vh->vhost_next;
}
if (!pt->count_event_loop_static_asset_handles &&
pt->event_loop_foreign) {
lwsl_cx_info(pt->context, "resuming context_destroy");
lws_context_unlock(pt->context);
lws_context_destroy(pt->context);
/*
* For foreign, we're being called from the foreign
* thread context the loop is associated with, we must
* return to it cleanly even though we are done with it.
*/
return 1;
}
} else
lwsl_cx_debug(pt->context, "still %d undestroyed",
pt->context->undestroyed_threads);
lws_context_unlock(pt->context);
return 0;
}
// static void lws_uv_walk_cb(uv_handle_t *handle, void *arg)
// {
// if (!uv_is_closing(handle))
// lwsl_err("%s: handle %p still alive on loop\n", __func__, handle);
// }
static const int sigs[] = { SIGINT, SIGTERM, SIGSEGV, SIGFPE, SIGHUP };
/*
* Closing Phase 2: Close callback for a static UV asset
*/
static void
lws_uv_close_cb_sa(uv_handle_t *handle)
{
struct lws_context_per_thread *pt =
LWS_UV_REFCOUNT_STATIC_HANDLE_TO_PT(handle);
struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt);
struct lws_context *context = pt->context;
#if !defined(LWS_WITH_NO_LOGS) && defined(_DEBUG)
int tsi = (int)(pt - &context->pt[0]);
#endif
lwsl_cx_info(context, "thr %d: sa left %d: dyn left: %d (rk %d)",
tsi,
pt->count_event_loop_static_asset_handles - 1,
ptpriv->extant_handles,
context->requested_stop_internal_loops);
/* any static assets left? */
if (LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(handle) ||
ptpriv->extant_handles)
return;
/*
* So we believe nothing of ours left on the loop. Let's sanity
* check it to count what's still on the loop
*/
// uv_walk(pt_to_priv_uv(pt)->io_loop, lws_uv_walk_cb, NULL);
/*
* That's it... all wsi were down, and now every
* static asset lws had a UV handle for is down.
*
* Stop the loop so we can get out of here.
*/
lwsl_cx_info(context, "thr %d: seen final static handle gone", tsi);
if (!pt->event_loop_foreign)
lws_context_destroy(context);
lws_uv_finalize_pt(pt);
lwsl_cx_info(context, "all done");
}
/*
* These must be called by protocols that want to use libuv objects directly...
*
* .... when the libuv object is created...
*/
void
lws_libuv_static_refcount_add(uv_handle_t *h, struct lws_context *context,
int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(h, pt);
}
/*
* ... and in the close callback when the object is closed.
*/
void
lws_libuv_static_refcount_del(uv_handle_t *h)
{
lws_uv_close_cb_sa(h);
}
void
lws_libuv_stop_without_kill(const struct lws_context *context, int tsi)
{
if (pt_to_priv_uv(&context->pt[tsi])->io_loop)
uv_stop(pt_to_priv_uv(&context->pt[tsi])->io_loop);
}
uv_loop_t *
lws_uv_getloop(struct lws_context *context, int tsi)
{
if (pt_to_priv_uv(&context->pt[tsi])->io_loop)
return pt_to_priv_uv(&context->pt[tsi])->io_loop;
return NULL;
}
int
lws_libuv_check_watcher_active(struct lws *wsi)
{
uv_handle_t *h = (uv_handle_t *)wsi_to_priv_uv(wsi)->w_read.pwatcher;
if (!h)
return 0;
return uv_is_active(h);
}
static int
elops_init_context_uv(struct lws_context *context,
const struct lws_context_creation_info *info)
{
int n;
context->eventlib_signal_cb = info->signal_cb;
for (n = 0; n < context->count_threads; n++)
pt_to_priv_uv(&context->pt[n])->w_sigint.context = context;
return 0;
}
static int
elops_destroy_context1_uv(struct lws_context *context)
{
struct lws_context_per_thread *pt;
int n, m = 0;
for (n = 0; n < context->count_threads; n++) {
int budget = 10000;
pt = &context->pt[n];
/* only for internal loops... */
if (!pt->event_loop_foreign) {
while (budget-- && (m = uv_run(pt_to_priv_uv(pt)->io_loop,
UV_RUN_NOWAIT)))
;
if (m)
lwsl_cx_info(context, "tsi %d: unclosed", n);
}
}
/* call destroy2 if internal loop */
return !context->pt[0].event_loop_foreign;
}
static int
elops_destroy_context2_uv(struct lws_context *context)
{
struct lws_context_per_thread *pt;
int n, internal = 0;
for (n = 0; n < context->count_threads; n++) {
pt = &context->pt[n];
/* only for internal loops... */
if (!pt->event_loop_foreign && pt_to_priv_uv(pt)->io_loop) {
internal = 1;
if (!context->evlib_finalize_destroy_after_int_loops_stop)
uv_stop(pt_to_priv_uv(pt)->io_loop);
else {
#if UV_VERSION_MAJOR > 0
uv_loop_close(pt_to_priv_uv(pt)->io_loop);
#endif
lws_free_set_NULL(pt_to_priv_uv(pt)->io_loop);
}
}
}
return internal;
}
static int
elops_wsi_logical_close_uv(struct lws *wsi)
{
if (!lws_socket_is_valid(wsi->desc.sockfd) &&
wsi->role_ops && strcmp(wsi->role_ops->name, "raw-file") &&
!wsi_to_priv_uv(wsi)->w_read.pwatcher)
return 0;
if (wsi->listener || wsi->event_pipe) {
lwsl_wsi_debug(wsi, "%d %d stop listener / pipe poll",
wsi->listener,
wsi->event_pipe);
if (wsi_to_priv_uv(wsi)->w_read.pwatcher)
uv_poll_stop(wsi_to_priv_uv(wsi)->w_read.pwatcher);
}
lwsl_wsi_debug(wsi, "lws_libuv_closehandle");
/*
* libuv has to do his own close handle processing asynchronously
*/
lws_libuv_closehandle(wsi);
return 1; /* do not complete the wsi close, uv close cb will do it */
}
static int
elops_check_client_connect_ok_uv(struct lws *wsi)
{
if (lws_libuv_check_watcher_active(wsi)) {
lwsl_wsi_warn(wsi, "Waiting for libuv watcher to close");
return 1;
}
return 0;
}
static void
lws_libuv_closewsi_m(uv_handle_t* handle)
{
lws_sockfd_type sockfd = (lws_sockfd_type)(lws_intptr_t)handle->data;
lwsl_debug("%s: sockfd %d\n", __func__, sockfd);
compatible_close(sockfd);
lws_free(handle);
}
static void
elops_close_handle_manually_uv(struct lws *wsi)
{
uv_handle_t *h = (uv_handle_t *)wsi_to_priv_uv(wsi)->w_read.pwatcher;
lwsl_wsi_debug(wsi, "lws_libuv_closehandle");
/*
* the "manual" variant only closes the handle itself and the
* related fd. handle->data is the fd.
*/
h->data = (void *)(lws_intptr_t)wsi->desc.sockfd;
/*
* We take responsibility to close / destroy these now.
* Remove any trace from the wsi.
*/
wsi->desc.sockfd = LWS_SOCK_INVALID;
wsi_to_priv_uv(wsi)->w_read.pwatcher = NULL;
wsi->told_event_loop_closed = 1;
uv_close(h, lws_libuv_closewsi_m);
}
static int
elops_accept_uv(struct lws *wsi)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt);
struct lws_io_watcher_libuv *w_read = &wsi_to_priv_uv(wsi)->w_read;
int n;
if (!ptpriv->thread_valid) {
/* record the thread id that gave us our first event */
ptpriv->uv_thread = uv_thread_self();
ptpriv->thread_valid = 1;
}
w_read->context = wsi->a.context;
w_read->pwatcher = lws_malloc(sizeof(*w_read->pwatcher), "uvh");
if (!w_read->pwatcher)
return -1;
if (wsi->role_ops->file_handle)
n = uv_poll_init(pt_to_priv_uv(pt)->io_loop, w_read->pwatcher,
(int)(lws_intptr_t)wsi->desc.filefd);
else
n = uv_poll_init_socket(pt_to_priv_uv(pt)->io_loop,
w_read->pwatcher, wsi->desc.sockfd);
if (n) {
lwsl_wsi_err(wsi, "uv_poll_init failed %d, sockfd=%p", n,
(void *)(lws_intptr_t)wsi->desc.sockfd);
lws_free(w_read->pwatcher);
w_read->pwatcher = NULL;
return -1;
}
((uv_handle_t *)w_read->pwatcher)->data = (void *)wsi;
ptpriv->extant_handles++;
lwsl_wsi_debug(wsi, "thr %d: sa left %d: dyn left: %d",
(int)(pt - &pt->context->pt[0]),
pt->count_event_loop_static_asset_handles,
ptpriv->extant_handles);
return 0;
}
static void
elops_io_uv(struct lws *wsi, unsigned int flags)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
struct lws_io_watcher_libuv *w = &(wsi_to_priv_uv(wsi)->w_read);
int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE);
lwsl_wsi_debug(wsi, "%d", flags);
/* w->context is set after the loop is initialized */
if (!pt_to_priv_uv(pt)->io_loop || !w->context) {
lwsl_wsi_info(wsi, "no io loop yet");
return;
}
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);
}
if (!w->pwatcher || wsi->told_event_loop_closed) {
lwsl_wsi_info(wsi, "no watcher");
return;
}
if (flags & LWS_EV_START) {
if (flags & LWS_EV_WRITE)
current_events |= UV_WRITABLE;
if (flags & LWS_EV_READ)
current_events |= UV_READABLE;
uv_poll_start(w->pwatcher, current_events, lws_io_cb);
} else {
if (flags & LWS_EV_WRITE)
current_events &= ~UV_WRITABLE;
if (flags & LWS_EV_READ)
current_events &= ~UV_READABLE;
if (!(current_events & (UV_READABLE | UV_WRITABLE)))
uv_poll_stop(w->pwatcher);
else
uv_poll_start(w->pwatcher, current_events, lws_io_cb);
}
w->actual_events = (uint8_t)current_events;
}
static int
elops_init_vhost_listen_wsi_uv(struct lws *wsi)
{
struct lws_context_per_thread *pt;
struct lws_pt_eventlibs_libuv *ptpriv;
struct lws_io_watcher_libuv *w_read;
int n;
if (!wsi)
return 0;
w_read = &wsi_to_priv_uv(wsi)->w_read;
if (w_read->context)
return 0;
pt = &wsi->a.context->pt[(int)wsi->tsi];
ptpriv = pt_to_priv_uv(pt);
if (!ptpriv->io_loop)
return 0;
w_read->context = wsi->a.context;
w_read->pwatcher = lws_malloc(sizeof(*w_read->pwatcher), "uvh");
if (!w_read->pwatcher)
return -1;
n = uv_poll_init_socket(pt_to_priv_uv(pt)->io_loop,
w_read->pwatcher, wsi->desc.sockfd);
if (n) {
lwsl_wsi_err(wsi, "uv_poll_init failed %d, sockfd=%p", n,
(void *)(lws_intptr_t)wsi->desc.sockfd);
return -1;
}
ptpriv->extant_handles++;
lwsl_wsi_debug(wsi, "thr %d: sa left %d: dyn left: %d",
(int)(pt - &pt->context->pt[0]),
pt->count_event_loop_static_asset_handles,
ptpriv->extant_handles);
((uv_handle_t *)w_read->pwatcher)->data = (void *)wsi;
elops_io_uv(wsi, LWS_EV_START | LWS_EV_READ);
return 0;
}
static void
elops_run_pt_uv(struct lws_context *context, int tsi)
{
if (pt_to_priv_uv(&context->pt[tsi])->io_loop)
uv_run(pt_to_priv_uv(&context->pt[tsi])->io_loop, 0);
}
static void
elops_destroy_pt_uv(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt);
int m, ns;
if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV))
return;
if (!ptpriv->io_loop)
return;
if (pt->event_loop_destroy_processing_done) {
if (!pt->event_loop_foreign) {
lwsl_warn("%s: stopping event loop\n", __func__);
uv_stop(pt_to_priv_uv(pt)->io_loop);
}
return;
}
pt->event_loop_destroy_processing_done = 1;
// lwsl_cx_debug(context, "%d", tsi);
if (!pt->event_loop_foreign) {
uv_signal_stop(&pt_to_priv_uv(pt)->w_sigint.watcher);
ns = LWS_ARRAY_SIZE(sigs);
if (lws_check_opt(context->options,
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
ns = 2;
for (m = 0; m < ns; m++) {
uv_signal_stop(&pt_to_priv_uv(pt)->signals[m]);
uv_close((uv_handle_t *)&pt_to_priv_uv(pt)->signals[m],
lws_uv_close_cb_sa);
}
} else
lwsl_cx_debug(context, "not closing pt signals");
uv_timer_stop(&pt_to_priv_uv(pt)->sultimer);
uv_close((uv_handle_t *)&pt_to_priv_uv(pt)->sultimer, lws_uv_close_cb_sa);
uv_idle_stop(&pt_to_priv_uv(pt)->idle);
uv_close((uv_handle_t *)&pt_to_priv_uv(pt)->idle, lws_uv_close_cb_sa);
}
static int
elops_listen_init_uv(struct lws_dll2 *d, void *user)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
if (elops_init_vhost_listen_wsi_uv(wsi) == -1)
return -1;
#endif
return 0;
}
/*
* This needs to be called after vhosts have been defined.
*
* If later, after server start, another vhost is added, this must be
* called again to bind the vhost
*/
int
elops_init_pt_uv(struct lws_context *context, void *_loop, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt);
int status = 0, n, ns, first = 1;
uv_loop_t *loop = (uv_loop_t *)_loop;
ptpriv->pt = pt;
if (!ptpriv->io_loop) {
if (!loop) {
loop = lws_malloc(sizeof(*loop), "libuv loop");
if (!loop) {
lwsl_cx_err(context, "OOM");
return -1;
}
#if UV_VERSION_MAJOR > 0
uv_loop_init(loop);
#else
lwsl_cx_err(context, "This libuv is too old to work...");
return 1;
#endif
pt->event_loop_foreign = 0;
} else {
lwsl_cx_notice(context, " Using foreign event loop...");
pt->event_loop_foreign = 1;
}
ptpriv->io_loop = loop;
uv_idle_init(loop, &ptpriv->idle);
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&ptpriv->idle, pt);
uv_idle_start(&ptpriv->idle, lws_uv_idle);
ns = LWS_ARRAY_SIZE(sigs);
if (lws_check_opt(context->options,
LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN))
ns = 2;
if (!pt->event_loop_foreign) {
assert(ns <= (int)LWS_ARRAY_SIZE(ptpriv->signals));
for (n = 0; n < ns; n++) {
uv_signal_init(loop, &ptpriv->signals[n]);
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(
&ptpriv->signals[n], pt);
ptpriv->signals[n].data = pt;
uv_signal_start(&ptpriv->signals[n],
lws_uv_signal_handler, sigs[n]);
}
}
} else
first = 0;
/*
* Initialize the accept wsi read watcher with all the listening sockets
* and register a callback for read operations
*
* We have to do it here because the uv loop(s) are not
* initialized until after context creation.
*/
lws_vhost_foreach_listen_wsi(context, context, elops_listen_init_uv);
if (!first)
return status;
uv_timer_init(ptpriv->io_loop, &ptpriv->sultimer);
LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(&ptpriv->sultimer, pt);
return status;
}
static void
lws_libuv_closewsi(uv_handle_t* handle)
{
struct lws *wsi = (struct lws *)handle->data;
struct lws_context *context = lws_get_context(wsi);
struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi];
struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt);
#if defined(LWS_WITH_SERVER)
int lspd = 0;
#endif
// lwsl_wsi_notice(wsi, "in");
lws_context_lock(context, __func__);
/*
* We get called back here for every wsi that closes
*/
#if defined(LWS_WITH_SERVER)
if (wsi->role_ops && !strcmp(wsi->role_ops->name, "listen") &&
wsi->a.context->deprecated) {
lspd = 1;
context->deprecation_pending_listen_close_count--;
if (!context->deprecation_pending_listen_close_count)
lspd = 2;
}
#endif
lws_pt_lock(pt, __func__);
lwsl_wsi_info(wsi, "thr %d: sa left %d: dyn left: %d (rk %d)",
(int)(pt - &pt->context->pt[0]),
pt->count_event_loop_static_asset_handles,
ptpriv->extant_handles - 1,
context->requested_stop_internal_loops);
__lws_close_free_wsi_final(wsi);
assert(ptpriv->extant_handles);
ptpriv->extant_handles--;
lws_pt_unlock(pt);
/* it's our job to close the handle finally */
lws_free(handle);
#if defined(LWS_WITH_SERVER)
if (lspd == 2 && context->deprecation_cb) {
lwsl_cx_notice(context, "calling deprecation callback");
context->deprecation_cb();
}
#endif
/*
* eventually, we closed all the wsi...
*/
if (context->requested_stop_internal_loops &&
!ptpriv->extant_handles &&
!pt->count_event_loop_static_asset_handles) {
/*
* we closed everything on this pt
*/
lws_context_unlock(context);
lws_uv_finalize_pt(pt);
return;
}
lws_context_unlock(context);
}
void
lws_libuv_closehandle(struct lws *wsi)
{
uv_handle_t* handle;
struct lws_io_watcher_libuv *w_read = &wsi_to_priv_uv(wsi)->w_read;
if (!w_read->pwatcher)
return;
if (wsi->told_event_loop_closed)
return;
// lwsl_wsi_debug(wsi, "in");
wsi->told_event_loop_closed = 1;
/*
* The normal close path attaches the related wsi as the
* handle->data.
*/
handle = (uv_handle_t *)w_read->pwatcher;
/* ensure we can only do this once */
w_read->pwatcher = NULL;
uv_close(handle, lws_libuv_closewsi);
}
static int
elops_foreign_thread_uv(struct lws_context *cx, int tsi)
{
struct lws_context_per_thread *pt = &cx->pt[tsi];
struct lws_pt_eventlibs_libuv *ptpriv = pt_to_priv_uv(pt);
uv_thread_t th = uv_thread_self();
if (!ptpriv->thread_valid)
/*
* We can't judge it until we get the first event from the loop
*/
return 0;
/*
* This is the same thread that gave us the first event on this loop?
* Return 0 if so.
*/
return !uv_thread_equal(&th, &ptpriv->uv_thread);
}
static const struct lws_event_loop_ops event_loop_ops_uv = {
/* name */ "libuv",
/* init_context */ elops_init_context_uv,
/* destroy_context1 */ elops_destroy_context1_uv,
/* destroy_context2 */ elops_destroy_context2_uv,
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_uv,
/* init_pt */ elops_init_pt_uv,
/* wsi_logical_close */ elops_wsi_logical_close_uv,
/* check_client_connect_ok */ elops_check_client_connect_ok_uv,
/* close_handle_manually */ elops_close_handle_manually_uv,
/* accept */ elops_accept_uv,
/* io */ elops_io_uv,
/* run_pt */ elops_run_pt_uv,
/* destroy_pt */ elops_destroy_pt_uv,
/* destroy wsi */ NULL,
/* foreign_thread */ elops_foreign_thread_uv,
/* fake_POLLIN */ NULL,
/* flags */ 0,
/* evlib_size_ctx */ sizeof(struct lws_context_eventlibs_libuv),
/* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libuv),
/* evlib_size_vh */ 0,
/* evlib_size_wsi */ sizeof(struct lws_io_watcher_libuv),
};
#if defined(LWS_WITH_EVLIB_PLUGINS)
LWS_VISIBLE
#endif
const lws_plugin_evlib_t evlib_uv = {
.hdr = {
"libuv event loop",
"lws_evlib_plugin",
LWS_BUILD_HASH,
LWS_PLUGIN_API_MAGIC
},
.ops = &event_loop_ops_uv
};

View File

@ -0,0 +1,90 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <uv.h>
/*
* libuv's async destroy cb means that asking to close something doesn't mean
* you can destroy it or parent things until after the close completes.
*
* So we must reference-count creation and close completions with libuv.
*
* All "static" (per-pt or per-context) uv handles must
*
* - have their .data set to point to the context
*
* - contribute to context->uv_count_static_asset_handles
* counting
*/
#define LWS_UV_REFCOUNT_STATIC_HANDLE_NEW(_x, _pt) \
{ uv_handle_t *_uht = (uv_handle_t *)(_x); _uht->data = _pt; \
_pt->count_event_loop_static_asset_handles++; }
#define LWS_UV_REFCOUNT_STATIC_HANDLE_TO_PT(_x) \
((struct lws_context_per_thread *)((uv_handle_t *)((_x)->data)))
#define LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(_x) \
(--(LWS_UV_REFCOUNT_STATIC_HANDLE_TO_PT(_x)-> \
count_event_loop_static_asset_handles))
struct lws_signal_watcher_libuv {
uv_signal_t watcher;
struct lws_context *context;
};
struct lws_pt_eventlibs_libuv {
uv_loop_t *io_loop;
struct lws_context_per_thread *pt;
uv_signal_t signals[8];
uv_timer_t sultimer;
uv_idle_t idle;
uv_thread_t uv_thread;
struct lws_signal_watcher_libuv w_sigint;
int extant_handles;
char thread_valid;
};
struct lws_context_eventlibs_libuv {
uv_loop_t loop;
};
struct lws_io_watcher_libuv {
uv_poll_t *pwatcher;
struct lws_context *context;
uint8_t actual_events;
};
struct lws_wsi_eventlibs_libuv {
struct lws_io_watcher_libuv w_read;
};
uv_loop_t *
lws_uv_getloop(struct lws_context *context, int tsi);
int
lws_uv_plugins_init(struct lws_context *context, const char * const *d);
int
lws_uv_plugins_destroy(struct lws_context *context);

View File

@ -0,0 +1,42 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - includes via include_directories
#
# and keep everything else private
include_directories(../poll)
if (LWS_WITH_NETWORK)
list(APPEND SOURCES
event-libs/poll/poll.c)
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,62 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE
*/
#include <private-lib-core.h>
#include "private-lib-event-libs-poll.h"
static int
elops_foreign_thread_poll(struct lws_context *cx, int tsi)
{
struct lws_context_per_thread *pt = &cx->pt[tsi];
volatile struct lws_context_per_thread *vpt =
(volatile struct lws_context_per_thread *)pt;
/*
* To avoid mandating a specific threading library, we can check
* probabilistically by seeing if the lws default wait is still asleep
* at the time we are checking, if it is then we cannot be being called
* by the event loop loop thread.
*/
return vpt->inside_poll;
}
struct lws_event_loop_ops event_loop_ops_poll = {
.name = "poll",
.foreign_thread = elops_foreign_thread_poll,
.flags = LELOF_ISPOLL,
};
const lws_plugin_evlib_t evlib_poll = {
.hdr = {
"poll",
"lws_evlib_plugin",
"n/a",
LWS_PLUGIN_API_MAGIC
},
.ops = &event_loop_ops_poll
};

View File

@ -0,0 +1,25 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
extern struct lws_event_loop_ops event_loop_ops_poll;

View File

@ -0,0 +1,27 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*
* This is included from private-lib-core.h
*/

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,446 @@
#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)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
if (init_vhost_listen_wsi_sd(wsi) == -1)
return -1;
#endif
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)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
wsi_logical_close_sd(wsi);
#endif
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
};

View File

@ -0,0 +1,72 @@
#
# libwebsockets - small server side websockets and web server implementation
#
# Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
# The strategy is to only export to PARENT_SCOPE
#
# - changes to LIB_LIST
# - includes via include_directories
#
# and keep everything else private
include_directories(.)
set(LWS_ULOOP_INCLUDE_DIRS CACHE PATH "Path to the libubox / uloop.h include directory")
set(LWS_ULOOP_LIBRARIES CACHE PATH "Path to the libubox library")
if (NOT ULOOP_FOUND)
find_path(ULOOP_INCLUDE_DIRS NAMES libubox/uloop.h)
find_library(ULOOP_LIBRARIES NAMES ubox)
endif()
message("libubox include dir: ${ULOOP_INCLUDE_DIRS}")
message("libubox libraries: ${ULOOP_LIBRARIES}")
include_directories("${ULOOP_INCLUDE_DIRS}")
if ("${LWS_ULOOP_LIBRARIES}" STREQUAL "" OR "${LWS_ULOOP_INCLUDE_DIRS}" STREQUAL "")
else()
set(ULOOP_LIBRARIES ${LWS_ULOOP_LIBRARIES})
set(ULOOP_INCLUDE_DIRS ${LWS_ULOOP_INCLUDE_DIRS})
endif()
if (LWS_WITH_EVLIB_PLUGINS)
create_evlib_plugin(evlib_uloop
uloop.c
private-lib-event-libs-uloop.h
${ULOOP_LIBRARIES})
else()
list(APPEND LIB_LIST ${ULOOP_LIBRARIES})
set(ULOOP_FOUND 1 PARENT_SCOPE)
if (LWS_WITH_NETWORK)
list(APPEND SOURCES
event-libs/uloop/uloop.c)
endif()
endif()
#
# Keep explicit parent scope exports at end
#
exports_to_parent_scope()

View File

@ -0,0 +1,37 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <libubox/uloop.h>
struct lws_pt_eventlibs_uloop {
struct lws_context_per_thread *pt;
struct uloop_timeout hrtimer;
struct uloop_timeout idle_timer;
};
struct lws_wsi_eventlibs_uloop {
struct lws *wsi;
struct uloop_fd fd;
unsigned int actual_events;
};

View File

@ -0,0 +1,326 @@
/*
* libwebsockets - small server side websockets and web server implementation
*
* Copyright (C) 2010 - 2021 Andy Green <andy@warmcat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include "private-lib-core.h"
#include "private-lib-event-libs-uloop.h"
#define pt_to_priv_uloop(_pt) ((struct lws_pt_eventlibs_uloop *)(_pt)->evlib_pt)
#define wsi_to_priv_uloop(_w) ((struct lws_wsi_eventlibs_uloop *)(_w)->evlib_wsi)
static void
lws_uloop_hrtimer_cb(struct uloop_timeout *ti)
{
struct lws_pt_eventlibs_uloop *upt = lws_container_of(ti,
struct lws_pt_eventlibs_uloop, hrtimer);
struct lws_context_per_thread *pt = upt->pt;
lws_usec_t us;
lws_pt_lock(pt, __func__);
us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
lws_now_usecs());
if (us)
uloop_timeout_set(ti, us < 1000 ? 1 : (int)(us / 1000));
lws_pt_unlock(pt);
}
static void
lws_uloop_idle_timer_cb(struct uloop_timeout *ti)
{
struct lws_pt_eventlibs_uloop *upt = lws_container_of(ti,
struct lws_pt_eventlibs_uloop,
idle_timer);
struct lws_context_per_thread *pt = upt->pt;
lws_usec_t us;
if (pt->is_destroyed)
return;
lws_service_do_ripe_rxflow(pt);
/*
* 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);
/* still somebody left who wants forced service? */
if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
/* yes... come back again later */
uloop_timeout_set(ti, 1 /* 1ms */);
return;
}
}
/* account for hrtimer */
lws_pt_lock(pt, __func__);
us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
lws_now_usecs());
if (us) {
uloop_timeout_cancel(&upt->hrtimer);
uloop_timeout_set(&upt->hrtimer,
us < 1000 ? 1 : (int)(us / 1000));
}
lws_pt_unlock(pt);
if (pt->destroy_self)
lws_context_destroy(pt->context);
}
static void
lws_uloop_cb(struct uloop_fd *ufd, unsigned int revents)
{
struct lws_wsi_eventlibs_uloop *wu = lws_container_of(ufd,
struct lws_wsi_eventlibs_uloop, fd);
struct lws_context *context = wu->wsi->a.context;
struct lws_context_per_thread *pt;
struct lws_pollfd eventfd;
eventfd.fd = wu->wsi->desc.sockfd;
eventfd.events = 0;
eventfd.revents = 0;
if (revents & ULOOP_READ) {
eventfd.events = LWS_POLLIN;
eventfd.revents = LWS_POLLIN;
}
if (revents & ULOOP_WRITE) {
eventfd.events |= LWS_POLLOUT;
eventfd.revents |= LWS_POLLOUT;
}
pt = &context->pt[(int)wu->wsi->tsi];
if (pt->is_destroyed)
return;
lws_service_fd_tsi(context, &eventfd, wu->wsi->tsi);
if (pt->destroy_self) {
lwsl_cx_notice(context, "pt destroy self coming true");
lws_context_destroy(pt->context);
return;
}
/* set the idle timer for 1ms ahead */
uloop_timeout_cancel(&pt_to_priv_uloop(pt)->idle_timer);
uloop_timeout_set(&pt_to_priv_uloop(pt)->idle_timer, 1);
}
static int
elops_listen_init_uloop(struct lws_dll2 *d, void *user)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi);
wu->wsi = wsi;
wu->fd.fd = wsi->desc.sockfd;
wu->fd.cb = lws_uloop_cb;
uloop_fd_add(&wu->fd, ULOOP_READ);
wu->actual_events = ULOOP_READ;
#endif
return 0;
}
static int
elops_init_pt_uloop(struct lws_context *context, void *v, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_uloop *ptpr = pt_to_priv_uloop(pt);
ptpr->pt = pt;
lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_uloop);
/* static event loop objects */
ptpr->hrtimer.cb = lws_uloop_hrtimer_cb;
ptpr->idle_timer.cb = lws_uloop_idle_timer_cb;
uloop_timeout_add(&ptpr->hrtimer);
uloop_timeout_add(&ptpr->idle_timer);
uloop_timeout_set(&ptpr->hrtimer, 1);
return 0;
}
static int
elops_accept_uloop(struct lws *wsi)
{
struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi);
wu->wsi = wsi;
wu->fd.fd = wsi->desc.sockfd;
wu->fd.cb = lws_uloop_cb;
uloop_fd_add(&wu->fd, ULOOP_READ);
wu->actual_events = ULOOP_READ;
return 0;
}
static void
elops_io_uloop(struct lws *wsi, unsigned int flags)
{
struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi);
unsigned int ulf = (unsigned int)(((flags & LWS_EV_WRITE) ? ULOOP_WRITE : 0) |
((flags & LWS_EV_READ) ? ULOOP_READ : 0)), u;
if (wsi->a.context->being_destroyed || pt->is_destroyed)
return;
assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
(flags & (LWS_EV_READ | LWS_EV_WRITE)));
u = wu->actual_events;
if (flags & LWS_EV_START)
u |= ulf;
if (flags & LWS_EV_STOP)
u &= ~ulf;
uloop_fd_add(&wu->fd, u);
wu->actual_events = u;
}
static void
elops_run_pt_uloop(struct lws_context *context, int tsi)
{
uloop_run();
}
static int
elops_listen_destroy_uloop(struct lws_dll2 *d, void *user)
{
#if defined(LWS_WITH_SERVER)
struct lws *wsi = lws_container_of(d, struct lws, listen_list);
struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi);
uloop_fd_delete(&wu->fd);
#endif
return 0;
}
static void
elops_destroy_pt_uloop(struct lws_context *context, int tsi)
{
struct lws_context_per_thread *pt = &context->pt[tsi];
struct lws_pt_eventlibs_uloop *ptpr = pt_to_priv_uloop(pt);
lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_uloop);
uloop_timeout_cancel(&ptpr->hrtimer);
uloop_timeout_cancel(&ptpr->idle_timer);
}
static void
elops_destroy_wsi_uloop(struct lws *wsi)
{
struct lws_context_per_thread *pt;
if (!wsi)
return;
pt = &wsi->a.context->pt[(int)wsi->tsi];
if (pt->is_destroyed)
return;
uloop_fd_delete(&wsi_to_priv_uloop(wsi)->fd);
}
static int
elops_wsi_logical_close_uloop(struct lws *wsi)
{
elops_destroy_wsi_uloop(wsi);
return 0;
}
static int
elops_init_vhost_listen_wsi_uloop(struct lws *wsi)
{
struct lws_wsi_eventlibs_uloop *wu;
if (!wsi) {
assert(0);
return 0;
}
wu = wsi_to_priv_uloop(wsi);
wu->wsi = wsi;
wu->fd.fd = wsi->desc.sockfd;
wu->fd.cb = lws_uloop_cb;
uloop_fd_add(&wu->fd, ULOOP_READ);
wu->actual_events = ULOOP_READ;
return 0;
}
static const struct lws_event_loop_ops event_loop_ops_uloop = {
/* name */ "uloop",
/* init_context */ NULL,
/* destroy_context1 */ NULL,
/* destroy_context2 */ NULL,
/* init_vhost_listen_wsi */ elops_init_vhost_listen_wsi_uloop,
/* init_pt */ elops_init_pt_uloop,
/* wsi_logical_close */ elops_wsi_logical_close_uloop,
/* check_client_connect_ok */ NULL,
/* close_handle_manually */ NULL,
/* accept */ elops_accept_uloop,
/* io */ elops_io_uloop,
/* run_pt */ elops_run_pt_uloop,
/* destroy_pt */ elops_destroy_pt_uloop,
/* destroy wsi */ elops_destroy_wsi_uloop,
/* foreign_thread */ NULL,
/* fake_POLLIN */ NULL,
/* flags */ 0,
/* evlib_size_ctx */ 0,
/* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_uloop),
/* evlib_size_vh */ 0,
/* evlib_size_wsi */ sizeof(struct lws_wsi_eventlibs_uloop),
};
#if defined(LWS_WITH_EVLIB_PLUGINS)
LWS_VISIBLE
#endif
const lws_plugin_evlib_t evlib_uloop = {
.hdr = {
"uloop event loop",
"lws_evlib_plugin",
LWS_BUILD_HASH,
LWS_PLUGIN_API_MAGIC
},
.ops = &event_loop_ops_uloop
};