1## Information for new event lib implementers 2 3### Introduction 4 5By default lws has built-in support for POSIX poll() as the event loop on unix, 6and native WSA on windows. 7 8To get access to epoll() or other platform specific better poll waits, or to 9integrate with existing applications already using a specific event loop, it can 10be desirable for lws to use another external event library, like libuv, glib, 11libevent, libev, or sdevent. 12 13Lws supports wholesale replacement of its wait selectable at runtime, either by 14building support for one or more event lib into the libwebsockets library, or by 15building runtime-loadable plugins. CMake symbol `LWS_WITH_EVLIB_PLUGINS` 16decides if the support is built as plugins or included into the lws lib. 17 18Due to their history libevent and libev have conflicting defines in the same 19namespace and cannot be built together if included into the lib, however when 20built as plugins they are built separately without problems. 21See ./READMEs/README.event-libs.md for more details. 22 23Despite it may be more work, lws event lib implementations must support 24"foreign" loops cleanly, that is integration with an already-existing loop and 25the ability to destroy the lws_context without stopping or leaving the foreign 26loop in any different state than when lws found it. For most loops this is 27fairly simple, but with libuv async close, it required refcounting lws libuv 28handles and deferring the actual destroy until they were all really closed. 29 30### Code placement 31 32The code specific to the event library should live in `./lib/event-libs/**lib name**` 33 34### Allowing control over enabling event libs 35 36All event libs should add a cmake define `LWS_WITH_**lib name**` and make its 37build dependent on it in CMakeLists.txt. Export the cmakedefine in 38`./cmake/lws_config.h.in` as well so user builds can understand if the event 39lib is available in the lws build it is trying to bind to. 40 41If the event lib is disabled in cmake, nothing in its directory is built or 42referenced. 43 44### Event loop ops struct 45 46The event lib support is defined by `struct lws_event_loop_ops` in 47`lib/event-libs/private-lib-event-libs.h`, 48each event lib support instantiates one of these and fills in the appropriate 49ops callbacks to perform its job. By convention that lives in 50`./lib/event-libs/**lib name**/**lib_name**.c`. 51 52The ops struct must be public, not static, and must be named using `**lib_name**`, 53eg 54 55``` 56``` 57 58### Private event lib declarations 59 60Truly private declarations for the event lib support that are only referenced by 61that code can go in the event-libs directory as you like. The convention is 62they should be in the event lib support directory in a file 63`private-lib-event-libs-**lib name**.h`. 64 65### Integration with lws 66 67There are a couple of places to add refererences in ./lib/core/context.c, in a 68table of context creation time server option flags mapped to the **lib_name**, 69used for plugin mode, like this... 70 71``` 72#if defined(LWS_WITH_EVLIB_PLUGINS) && defined(LWS_WITH_EVENT_LIBS) 73static const struct lws_evlib_map { 74 uint64_t flag; 75 const char *name; 76} map[] = { 77 { LWS_SERVER_OPTION_LIBUV, "evlib_uv" }, 78 { LWS_SERVER_OPTION_LIBEVENT, "evlib_event" }, 79 { LWS_SERVER_OPTION_GLIB, "evlib_glib" }, 80 { LWS_SERVER_OPTION_LIBEV, "evlib_ev" }, 81}; 82``` 83 84and for backwards compatibility add a stanza to the built-in checks like this 85 86``` 87#if defined(LWS_WITH_LIBUV) 88 if (lws_check_opt(info->options, LWS_SERVER_OPTION_LIBUV)) { 89 extern const lws_plugin_evlib_t evlib_uv; 90 plev = &evlib_uv; 91 } 92#endif 93``` 94 95Both entries are the way the main libs hook up to the selected event lib ops 96struct at runtime. 97 98### Integrating event lib assets to lws 99 100Declare "container structs" in your private....h for anything you need at 101wsi, pt, vhost and context levels, eg, the libuv event lib support need to 102add its own assets in the perthread struct, it declares in its private....h 103 104``` 105struct lws_pt_eventlibs_libuv { 106 uv_loop_t *io_loop; 107 struct lws_context_per_thread *pt; 108 uv_signal_t signals[8]; 109 uv_timer_t sultimer; 110 uv_idle_t idle; 111 struct lws_signal_watcher_libuv w_sigint; 112}; 113``` 114 115this is completely private and opaque, but in the ops struct there are provided 116four entries to export the sizes of these event-lib specific objects 117 118``` 119... 120 /* evlib_size_ctx */ sizeof(struct lws_context_eventlibs_libuv), 121 /* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_libuv), 122 /* evlib_size_vh */ 0, 123 /* evlib_size_wsi */ sizeof(struct lws_io_watcher_libuv), 124}; 125``` 126 127If the particular event lib doesn't need to have a private footprint in an 128object, it can just set the size it needs there to 0. 129 130When the context, pts, vhosts or wsis are created in lws, they over-allocate 131to also allow for the event lib object, and set a pointer in the lws object 132being created to point at the over-allocation. For example for the wsi 133 134``` 135#if defined(LWS_WITH_EVENT_LIBS) 136 void *evlib_wsi; /* overallocated */ 137#endif 138``` 139 140and similarly there are `evlib_pt` and so on for those objects, usable by the 141event lib and opaque to everyone else. Once the event lib is selected at 142runtime, all of these objects are guaranteed to have the right size object at 143`wsi->evlib_wsi` initialized to zeroes. 144 145### Enabling event lib adoption 146 147You need to add a `LWS_SERVER_OPTION...` flag as necessary in `./lib/libwebsockets.h` 148`enum lws_context_options`, and follow the existing code in `lws_create_context()` 149to convert the flag into binding your ops struct to the context. 150 151### Implementation of the event lib bindings 152 153Study eg libuv implementation, using the available ops in the struct lws_event_loop_ops 154as a guide. 155 156### Destruction 157 158Ending the event loop is generally a bit tricky, because if the event loop is 159internal to the lws context, you cannot destroy it while the event loop is 160running. 161 162Don't add special exports... we tried that, it's a huge mess. The same user 163code should be able work with any of the event loops including poll. 164 165The solution we found was hide the different processing necessary for the 166different cases in `lws_destroy_context()`. To help with that there are event 167lib ops available that will be called at two different places in the context 168destroy processing. 169 170