• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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