diff --git a/lib/event-libs/README.md b/lib/event-libs/README.md index 1fab4f424..69ea7633f 100644 --- a/lib/event-libs/README.md +++ b/lib/event-libs/README.md @@ -2,12 +2,30 @@ ### Introduction -By default lws has built-in support for POSIX poll() as the event loop. +By default lws has built-in support for POSIX poll() as the event loop on unix, +and native WSA on windows. -However either 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, libevent or libev. +To get access to epoll() or other platform specific better poll waits, or to +integrate with existing applications already using a specific event loop, it can +be desirable for lws to use another external event library, like libuv, glib, +libevent or libev. + +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 @@ -15,89 +33,114 @@ The code specific to the event library should live in `./lib/event-libs/**lib na ### 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. +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. +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 +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 can go in the event-libs directory as you like. -However when the declarations must be accessible to other things in lws build, eg, -the event lib support adds members to `struct lws` when enabled, they should be in the -event lib support directory in a file `private-lib-event-libs-myeventlib.h`. +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`. -Search for "bring in event libs private declarations" in `./lib/core/private-lib-core.h -and add your private event lib support file there following the style used for the other -event libs, eg, +### 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) - #include "event-libs/libuv/private-lib-event-libs-libuv.h" + if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBUV)) { + extern const lws_plugin_evlib_t evlib_uv; + plev = &evlib_uv; + } #endif ``` -If the event lib support is disabled at cmake, nothing from its private.h should be used anywhere. +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 -If your event lib needs special storage in lws objects, that's no problem. But to keep -things sane, there are some rules. - - - declare a "container struct" in your private.h for everything, eg, the libuv event - lib support need to add its own assets in the perthread struct, it declares in its private.h +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 timeout_watcher; - uv_timer_t hrtimer; + uv_timer_t sultimer; uv_idle_t idle; + struct lws_signal_watcher_libuv w_sigint; }; ``` - - add your event lib content in one place in the related lws struct, protected by `#if defined(LWS_WITH_**lib name**)`, - eg, again for LWS_WITH_LIBUV +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 ``` -struct lws_context_per_thread { - -... - -#if defined(LWS_WITH_LIBUV) - struct lws_pt_eventlibs_libuv uv; -#endif - ... + /* 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), +}; ``` -### Adding to lws available event libs list +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. -Edit the NULL-terminated array `available_event_libs` at the top of `./lib/context.c` to include -a pointer to your new event lib support's ops struct, following the style already there. +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 ``` -const struct lws_event_loop_ops *available_event_libs[] = { -#if defined(LWS_WITH_POLL) - &event_loop_ops_poll, +#if defined(LWS_WITH_EVENT_LIBS) + void *evlib_wsi; /* overallocated */ #endif -#if defined(LWS_WITH_LIBUV) - &event_loop_ops_uv, -#endif -... ``` -This is used to provide a list of avilable configured backends. +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 @@ -112,13 +155,15 @@ 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. +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. +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 ops available at two different places in -the context destroy processing. +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.