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