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
49 static gboolean
50 lws_glib_idle_timer_cb(void *p);
51
52 static gboolean
53 lws_glib_hrtimer_cb(void *p);
54
55 static gboolean
lws_glib_check(GSource * src)56 lws_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
73 static int
lws_glib_set_idle(struct lws_context_per_thread * pt)74 lws_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
91 static int
lws_glib_set_timeout(struct lws_context_per_thread * pt,unsigned int ms)92 lws_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
109 static gboolean
lws_glib_dispatch(GSource * src,GSourceFunc x,gpointer userData)110 lws_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
153 static 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
166 static gboolean
lws_glib_hrtimer_cb(void * p)167 lws_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
194 static gboolean
lws_glib_idle_timer_cb(void * p)195 lws_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
231 void
lws_glib_sigint_cb(void * ctx)232 lws_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
247 static int
elops_init_context_glib(struct lws_context * context,const struct lws_context_creation_info * info)248 elops_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
261 static int
elops_accept_glib(struct lws * wsi)262 elops_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
296 static int
elops_listen_init_glib(struct lws_dll2 * d,void * user)297 elops_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
306 static int
elops_init_pt_glib(struct lws_context * context,void * _loop,int tsi)307 elops_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
345 static void
elops_io_glib(struct lws * wsi,unsigned int flags)346 elops_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
387 static void
elops_run_pt_glib(struct lws_context * context,int tsi)388 elops_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
396 static void
elops_destroy_wsi_glib(struct lws * wsi)397 elops_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
422 static int
elops_listen_destroy_glib(struct lws_dll2 * d,void * user)423 elops_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
432 static void
elops_destroy_pt_glib(struct lws_context * context,int tsi)433 elops_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
455 static int
elops_destroy_context2_glib(struct lws_context * context)456 elops_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
470 static int
elops_wsi_logical_close_glib(struct lws * wsi)471 elops_wsi_logical_close_glib(struct lws *wsi)
472 {
473 elops_destroy_wsi_glib(wsi);
474
475 return 0;
476 }
477
478 static 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)
504 LWS_VISIBLE
505 #endif
506 const 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