1/* 2 * libwebsockets - small server side websockets and web server implementation 3 * 4 * Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 * IN THE SOFTWARE. 23 */ 24 25#include "private-lib-core.h" 26 27#include <glib-unix.h> 28 29#include "private-lib-event-libs-glib.h" 30 31#if !defined(G_SOURCE_FUNC) 32#define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f)) 33#endif 34 35#define pt_to_priv_glib(_pt) ((struct lws_pt_eventlibs_glib *)(_pt)->evlib_pt) 36#define wsi_to_priv_glib(_w) ((struct lws_wsi_eventlibs_glib *)(_w)->evlib_wsi) 37 38#define wsi_to_subclass(_w) (wsi_to_priv_glib(_w)->w_read.source) 39#define wsi_to_gsource(_w) ((GSource *)wsi_to_subclass(_w)) 40#define pt_to_loop(_pt) (pt_to_priv_glib(_pt)->loop) 41#define pt_to_g_main_context(_pt) g_main_loop_get_context(pt_to_loop(_pt)) 42 43#define lws_gs_valid(t) (t.gs) 44#define lws_gs_destroy(t) if (lws_gs_valid(t)) { \ 45 g_source_destroy(t.gs); \ 46 g_source_unref(t.gs); \ 47 t.gs = NULL; t.tag = 0; } 48 49static gboolean 50lws_glib_idle_timer_cb(void *p); 51 52static gboolean 53lws_glib_hrtimer_cb(void *p); 54 55static gboolean 56lws_glib_check(GSource *src) 57{ 58 struct lws_io_watcher_glib_subclass *sub = 59 (struct lws_io_watcher_glib_subclass *)src; 60 61 return !!g_source_query_unix_fd(src, sub->tag); 62} 63 64/* 65 * These helpers attach only to the main_context that belongs to the pt's glib 66 * mainloop. The simpler g_timeout_add() and g_idle_add() are forbidden 67 * because they implicitly choose the default main context to attach to 68 * instead of specifically the loop bound to the pt. 69 * 70 * https://developer.gnome.org/programming-guidelines/unstable/main-contexts.html.en#what-is-gmaincontext 71 */ 72 73static int 74lws_glib_set_idle(struct lws_context_per_thread *pt) 75{ 76 if (lws_gs_valid(pt_to_priv_glib(pt)->idle)) 77 return 0; 78 79 pt_to_priv_glib(pt)->idle.gs = g_idle_source_new(); 80 if (!pt_to_priv_glib(pt)->idle.gs) 81 return 1; 82 83 g_source_set_callback(pt_to_priv_glib(pt)->idle.gs, 84 lws_glib_idle_timer_cb, pt, NULL); 85 pt_to_priv_glib(pt)->idle.tag = g_source_attach( 86 pt_to_priv_glib(pt)->idle.gs, pt_to_g_main_context(pt)); 87 88 return 0; 89} 90 91static int 92lws_glib_set_timeout(struct lws_context_per_thread *pt, unsigned int ms) 93{ 94 lws_gs_destroy(pt_to_priv_glib(pt)->hrtimer); 95 96 pt_to_priv_glib(pt)->hrtimer.gs = g_timeout_source_new(ms); 97 if (!pt_to_priv_glib(pt)->hrtimer.gs) 98 return 1; 99 100 g_source_set_callback(pt_to_priv_glib(pt)->hrtimer.gs, 101 lws_glib_hrtimer_cb, pt, NULL); 102 pt_to_priv_glib(pt)->hrtimer.tag = g_source_attach( 103 pt_to_priv_glib(pt)->hrtimer.gs, 104 pt_to_g_main_context(pt)); 105 106 return 0; 107} 108 109static gboolean 110lws_glib_dispatch(GSource *src, GSourceFunc x, gpointer userData) 111{ 112 struct lws_io_watcher_glib_subclass *sub = 113 (struct lws_io_watcher_glib_subclass *)src; 114 struct lws_context_per_thread *pt; 115 struct lws_pollfd eventfd; 116 GIOCondition cond; 117 118 cond = g_source_query_unix_fd(src, sub->tag); 119 eventfd.revents = (short)cond; 120 121 /* translate from glib event namespace to platform */ 122 123 if (cond & G_IO_IN) 124 eventfd.revents |= LWS_POLLIN; 125 if (cond & G_IO_OUT) 126 eventfd.revents |= LWS_POLLOUT; 127 if (cond & G_IO_ERR) 128 eventfd.revents |= LWS_POLLHUP; 129 if (cond & G_IO_HUP) 130 eventfd.revents |= LWS_POLLHUP; 131 132 eventfd.events = eventfd.revents; 133 eventfd.fd = sub->wsi->desc.sockfd; 134 135 lwsl_wsi_debug(sub->wsi, "fd %d, events %d", 136 eventfd.fd, eventfd.revents); 137 138 pt = &sub->wsi->a.context->pt[(int)sub->wsi->tsi]; 139 if (pt->is_destroyed) 140 return G_SOURCE_CONTINUE; 141 142 lws_service_fd_tsi(sub->wsi->a.context, &eventfd, sub->wsi->tsi); 143 144 if (!lws_gs_valid(pt_to_priv_glib(pt)->idle)) 145 lws_glib_set_idle(pt); 146 147 if (pt->destroy_self) 148 lws_context_destroy(pt->context); 149 150 return G_SOURCE_CONTINUE; 151} 152 153static const GSourceFuncs lws_glib_source_ops = { 154 .prepare = NULL, 155 .check = lws_glib_check, 156 .dispatch = lws_glib_dispatch, 157 .finalize = NULL, 158}; 159 160/* 161 * This is the callback for a timer object that is set to the earliest scheduled 162 * lws event... it services any lws scheduled events that are ready, and then 163 * resets the event loop timer to the earliest remaining event, if any. 164 */ 165 166static gboolean 167lws_glib_hrtimer_cb(void *p) 168{ 169 struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; 170 unsigned int ms; 171 lws_usec_t us; 172 173 lws_pt_lock(pt, __func__); 174 175 lws_gs_destroy(pt_to_priv_glib(pt)->hrtimer); 176 177 us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS, 178 lws_now_usecs()); 179 if (us) { 180 ms = (unsigned int)(us / LWS_US_PER_MS); 181 if (!ms) 182 ms = 1; 183 184 lws_glib_set_timeout(pt, ms); 185 } 186 187 lws_pt_unlock(pt); 188 189 lws_glib_set_idle(pt); 190 191 return FALSE; /* stop it repeating */ 192} 193 194static gboolean 195lws_glib_idle_timer_cb(void *p) 196{ 197 struct lws_context_per_thread *pt = (struct lws_context_per_thread *)p; 198 199 if (pt->is_destroyed) 200 return FALSE; 201 202 lws_service_do_ripe_rxflow(pt); 203 lws_glib_hrtimer_cb(pt); 204 205 /* 206 * is there anybody with pending stuff that needs service forcing? 207 */ 208 if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) { 209 /* -1 timeout means just do forced service */ 210 _lws_plat_service_forced_tsi(pt->context, pt->tid); 211 /* still somebody left who wants forced service? */ 212 if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) 213 return TRUE; 214 } 215 216 if (pt->destroy_self) 217 lws_context_destroy(pt->context); 218 219 /* 220 * For glib, this disables the idle callback. Otherwise we keep 221 * coming back here immediately endlessly. 222 * 223 * We reenable the idle callback on the next network or scheduled event 224 */ 225 226 lws_gs_destroy(pt_to_priv_glib(pt)->idle); 227 228 return FALSE; 229} 230 231void 232lws_glib_sigint_cb(void *ctx) 233{ 234 struct lws_context_per_thread *pt = ctx; 235 236 pt->inside_service = 1; 237 238 if (pt->context->eventlib_signal_cb) { 239 pt->context->eventlib_signal_cb(NULL, 0); 240 241 return; 242 } 243 if (!pt->event_loop_foreign) 244 g_main_loop_quit(pt_to_loop(pt)); 245} 246 247static int 248elops_init_context_glib(struct lws_context *context, 249 const struct lws_context_creation_info *info) 250{ 251// int n; 252 253 context->eventlib_signal_cb = info->signal_cb; 254 255// for (n = 0; n < context->count_threads; n++) 256// pt_to_priv_glib(&context->pt[n])->w_sigint.context = context; 257 258 return 0; 259} 260 261static int 262elops_accept_glib(struct lws *wsi) 263{ 264 struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; 265 struct lws_wsi_eventlibs_glib *wsipr = wsi_to_priv_glib(wsi); 266 int fd; 267 268 assert(!wsi_to_subclass(wsi)); 269 270 wsi_to_subclass(wsi) = (struct lws_io_watcher_glib_subclass *) 271 g_source_new((GSourceFuncs *)&lws_glib_source_ops, 272 sizeof(*wsi_to_subclass(wsi))); 273 if (!wsi_to_subclass(wsi)) 274 return 1; 275 276 wsipr->w_read.context = wsi->a.context; 277 wsi_to_subclass(wsi)->wsi = wsi; 278 279 if (wsi->role_ops->file_handle) 280 fd = wsi->desc.filefd; 281 else 282 fd = wsi->desc.sockfd; 283 284 wsi_to_subclass(wsi)->tag = g_source_add_unix_fd(wsi_to_gsource(wsi), 285 fd, (GIOCondition)LWS_POLLIN); 286 wsipr->w_read.actual_events = LWS_POLLIN; 287 288 g_source_set_callback(wsi_to_gsource(wsi), 289 G_SOURCE_FUNC(lws_service_fd), wsi->a.context, NULL); 290 291 g_source_attach(wsi_to_gsource(wsi), pt_to_g_main_context(pt)); 292 293 return 0; 294} 295 296static int 297elops_listen_init_glib(struct lws_dll2 *d, void *user) 298{ 299 struct lws *wsi = lws_container_of(d, struct lws, listen_list); 300 301 elops_accept_glib(wsi); 302 303 return 0; 304} 305 306static int 307elops_init_pt_glib(struct lws_context *context, void *_loop, int tsi) 308{ 309 struct lws_context_per_thread *pt = &context->pt[tsi]; 310 struct lws_pt_eventlibs_glib *ptpr = pt_to_priv_glib(pt); 311 GMainLoop *loop = (GMainLoop *)_loop; 312 313 if (!loop) 314 loop = g_main_loop_new(NULL, 0); 315 else 316 context->pt[tsi].event_loop_foreign = 1; 317 318 if (!loop) { 319 lwsl_cx_err(context, "creating glib loop failed"); 320 321 return -1; 322 } 323 324 ptpr->loop = loop; 325 326 lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_glib); 327 328 lws_glib_set_idle(pt); 329 330 /* Register the signal watcher unless it's a foreign loop */ 331 332 if (pt->event_loop_foreign) 333 return 0; 334 335 ptpr->sigint.tag = g_unix_signal_add(SIGINT, 336 G_SOURCE_FUNC(lws_glib_sigint_cb), pt); 337 338 return 0; 339} 340 341/* 342 * We are changing the event wait for this guy 343 */ 344 345static void 346elops_io_glib(struct lws *wsi, unsigned int flags) 347{ 348 struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi]; 349 struct lws_wsi_eventlibs_glib *wsipr = wsi_to_priv_glib(wsi); 350 GIOCondition cond = wsipr->w_read.actual_events | G_IO_ERR; 351 352 if (!pt_to_loop(pt) || wsi->a.context->being_destroyed || 353 pt->is_destroyed) 354 return; 355 356 if (!wsi_to_subclass(wsi)) 357 return; 358 359 /* 360 * We are being given individual set / clear operations using 361 * LWS_EV_ common namespace, convert them to glib namespace bitfield 362 */ 363 364 if (flags & LWS_EV_READ) { 365 if (flags & LWS_EV_STOP) 366 cond &= (unsigned int)~(G_IO_IN | G_IO_HUP); 367 else 368 cond |= G_IO_IN | G_IO_HUP; 369 } 370 371 if (flags & LWS_EV_WRITE) { 372 if (flags & LWS_EV_STOP) 373 cond &= (unsigned int)~G_IO_OUT; 374 else 375 cond |= G_IO_OUT; 376 } 377 378 wsipr->w_read.actual_events = (uint8_t)cond; 379 380 lwsl_wsi_debug(wsi, "fd %d, 0x%x/0x%x", wsi->desc.sockfd, 381 flags, (int)cond); 382 383 g_source_modify_unix_fd(wsi_to_gsource(wsi), wsi_to_subclass(wsi)->tag, 384 cond); 385} 386 387static void 388elops_run_pt_glib(struct lws_context *context, int tsi) 389{ 390 struct lws_context_per_thread *pt = &context->pt[tsi]; 391 392 if (pt_to_loop(pt)) 393 g_main_loop_run(pt_to_loop(pt)); 394} 395 396static void 397elops_destroy_wsi_glib(struct lws *wsi) 398{ 399 struct lws_context_per_thread *pt; 400 401 if (!wsi) 402 return; 403 404 pt = &wsi->a.context->pt[(int)wsi->tsi]; 405 if (pt->is_destroyed) 406 return; 407 408 if (!wsi_to_gsource(wsi)) 409 return; 410 411 if (wsi_to_subclass(wsi)->tag) { 412 g_source_remove_unix_fd(wsi_to_gsource(wsi), 413 wsi_to_subclass(wsi)->tag); 414 wsi_to_subclass(wsi)->tag = NULL; 415 } 416 417 g_source_destroy(wsi_to_gsource(wsi)); 418 g_source_unref(wsi_to_gsource(wsi)); 419 wsi_to_subclass(wsi) = NULL; 420} 421 422static int 423elops_listen_destroy_glib(struct lws_dll2 *d, void *user) 424{ 425 struct lws *wsi = lws_container_of(d, struct lws, listen_list); 426 427 elops_destroy_wsi_glib(wsi); 428 429 return 0; 430} 431 432static void 433elops_destroy_pt_glib(struct lws_context *context, int tsi) 434{ 435 struct lws_context_per_thread *pt = &context->pt[tsi]; 436 struct lws_pt_eventlibs_glib *ptpr = pt_to_priv_glib(pt); 437 438 if (!pt_to_loop(pt)) 439 return; 440 441 lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_glib); 442 443 lws_gs_destroy(ptpr->idle); 444 lws_gs_destroy(ptpr->hrtimer); 445 446 if (!pt->event_loop_foreign) { 447 g_main_loop_quit(pt_to_loop(pt)); 448 lws_gs_destroy(ptpr->sigint); 449 g_main_loop_unref(pt_to_loop(pt)); 450 } 451 452 pt_to_loop(pt) = NULL; 453} 454 455static int 456elops_destroy_context2_glib(struct lws_context *context) 457{ 458 struct lws_context_per_thread *pt = &context->pt[0]; 459 int n; 460 461 for (n = 0; n < (int)context->count_threads; n++) { 462 if (!pt->event_loop_foreign) 463 g_main_loop_quit(pt_to_loop(pt)); 464 pt++; 465 } 466 467 return 0; 468} 469 470static int 471elops_wsi_logical_close_glib(struct lws *wsi) 472{ 473 elops_destroy_wsi_glib(wsi); 474 475 return 0; 476} 477 478static const struct lws_event_loop_ops event_loop_ops_glib = { 479 /* name */ "glib", 480 /* init_context */ elops_init_context_glib, 481 /* destroy_context1 */ NULL, 482 /* destroy_context2 */ elops_destroy_context2_glib, 483 /* init_vhost_listen_wsi */ elops_accept_glib, 484 /* init_pt */ elops_init_pt_glib, 485 /* wsi_logical_close */ elops_wsi_logical_close_glib, 486 /* check_client_connect_ok */ NULL, 487 /* close_handle_manually */ NULL, 488 /* accept */ elops_accept_glib, 489 /* io */ elops_io_glib, 490 /* run_pt */ elops_run_pt_glib, 491 /* destroy_pt */ elops_destroy_pt_glib, 492 /* destroy wsi */ elops_destroy_wsi_glib, 493 /* foreign_thread */ NULL, 494 495 /* flags */ LELOF_DESTROY_FINAL, 496 497 /* evlib_size_ctx */ 0, 498 /* evlib_size_pt */ sizeof(struct lws_pt_eventlibs_glib), 499 /* evlib_size_vh */ 0, 500 /* evlib_size_wsi */ sizeof(struct lws_io_watcher_glib), 501}; 502 503#if defined(LWS_WITH_EVLIB_PLUGINS) 504LWS_VISIBLE 505#endif 506const lws_plugin_evlib_t evlib_glib = { 507 .hdr = { 508 "glib event loop", 509 "lws_evlib_plugin", 510 LWS_BUILD_HASH, 511 LWS_PLUGIN_API_MAGIC 512 }, 513 514 .ops = &event_loop_ops_glib 515}; 516