1/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2021 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#include "private-lib-event-libs-uloop.h"
27
28#define pt_to_priv_uloop(_pt) ((struct lws_pt_eventlibs_uloop *)(_pt)->evlib_pt)
29#define wsi_to_priv_uloop(_w) ((struct lws_wsi_eventlibs_uloop *)(_w)->evlib_wsi)
30
31static void
32lws_uloop_hrtimer_cb(struct uloop_timeout *ti)
33{
34	struct lws_pt_eventlibs_uloop *upt = lws_container_of(ti,
35					struct lws_pt_eventlibs_uloop, hrtimer);
36	struct lws_context_per_thread *pt = upt->pt;
37	lws_usec_t us;
38
39	lws_pt_lock(pt, __func__);
40	us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
41				    lws_now_usecs());
42	if (us)
43		uloop_timeout_set(ti, us < 1000 ? 1 : (int)(us / 1000));
44
45	lws_pt_unlock(pt);
46}
47
48static void
49lws_uloop_idle_timer_cb(struct uloop_timeout *ti)
50{
51	struct lws_pt_eventlibs_uloop *upt = lws_container_of(ti,
52						struct lws_pt_eventlibs_uloop,
53						idle_timer);
54	struct lws_context_per_thread *pt = upt->pt;
55	lws_usec_t us;
56
57	if (pt->is_destroyed)
58		return;
59
60	lws_service_do_ripe_rxflow(pt);
61
62	/*
63	 * is there anybody with pending stuff that needs service forcing?
64	 */
65	if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
66		/* -1 timeout means just do forced service */
67		_lws_plat_service_forced_tsi(pt->context, pt->tid);
68		/* still somebody left who wants forced service? */
69		if (!lws_service_adjust_timeout(pt->context, 1, pt->tid)) {
70			/* yes... come back again later */
71
72			uloop_timeout_set(ti, 1 /* 1ms */);
73
74			return;
75		}
76	}
77
78	/* account for hrtimer */
79
80	lws_pt_lock(pt, __func__);
81	us = __lws_sul_service_ripe(pt->pt_sul_owner, LWS_COUNT_PT_SUL_OWNERS,
82				    lws_now_usecs());
83	if (us) {
84		uloop_timeout_cancel(&upt->hrtimer);
85		uloop_timeout_set(&upt->hrtimer,
86				  us < 1000 ? 1 : (int)(us / 1000));
87	}
88
89	lws_pt_unlock(pt);
90
91	if (pt->destroy_self)
92		lws_context_destroy(pt->context);
93}
94
95static void
96lws_uloop_cb(struct uloop_fd *ufd, unsigned int revents)
97{
98	struct lws_wsi_eventlibs_uloop *wu = lws_container_of(ufd,
99					struct lws_wsi_eventlibs_uloop, fd);
100	struct lws_context *context = wu->wsi->a.context;
101	struct lws_context_per_thread *pt;
102	struct lws_pollfd eventfd;
103
104	eventfd.fd = wu->wsi->desc.sockfd;
105	eventfd.events = 0;
106	eventfd.revents = 0;
107
108	if (revents & ULOOP_READ) {
109		eventfd.events = LWS_POLLIN;
110		eventfd.revents = LWS_POLLIN;
111	}
112	if (revents & ULOOP_WRITE) {
113		eventfd.events |= LWS_POLLOUT;
114		eventfd.revents |= LWS_POLLOUT;
115	}
116
117	pt = &context->pt[(int)wu->wsi->tsi];
118	if (pt->is_destroyed)
119		return;
120
121	lws_service_fd_tsi(context, &eventfd, wu->wsi->tsi);
122
123	if (pt->destroy_self) {
124		lwsl_cx_notice(context, "pt destroy self coming true");
125		lws_context_destroy(pt->context);
126		return;
127	}
128
129	/* set the idle timer for 1ms ahead */
130
131	uloop_timeout_cancel(&pt_to_priv_uloop(pt)->idle_timer);
132	uloop_timeout_set(&pt_to_priv_uloop(pt)->idle_timer, 1);
133}
134
135static int
136elops_listen_init_uloop(struct lws_dll2 *d, void *user)
137{
138	struct lws *wsi = lws_container_of(d, struct lws, listen_list);
139	struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi);
140
141	wu->wsi = wsi;
142	wu->fd.fd = wsi->desc.sockfd;
143	wu->fd.cb = lws_uloop_cb;
144	uloop_fd_add(&wu->fd,  ULOOP_READ);
145	wu->actual_events = ULOOP_READ;
146
147	return 0;
148}
149
150static int
151elops_init_pt_uloop(struct lws_context *context, void *v, int tsi)
152{
153	struct lws_context_per_thread *pt = &context->pt[tsi];
154	struct lws_pt_eventlibs_uloop *ptpr = pt_to_priv_uloop(pt);
155
156	ptpr->pt = pt;
157
158	lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_init_uloop);
159
160	/* static event loop objects */
161
162	ptpr->hrtimer.cb = lws_uloop_hrtimer_cb;
163	ptpr->idle_timer.cb = lws_uloop_idle_timer_cb;
164
165	uloop_timeout_add(&ptpr->hrtimer);
166	uloop_timeout_add(&ptpr->idle_timer);
167
168	uloop_timeout_set(&ptpr->hrtimer, 1);
169
170	return 0;
171}
172
173static int
174elops_accept_uloop(struct lws *wsi)
175{
176	struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi);
177
178	wu->wsi = wsi;
179	wu->fd.fd = wsi->desc.sockfd;
180	wu->fd.cb = lws_uloop_cb;
181	uloop_fd_add(&wu->fd, ULOOP_READ);
182	wu->actual_events = ULOOP_READ;
183
184	return 0;
185}
186
187static void
188elops_io_uloop(struct lws *wsi, unsigned int flags)
189{
190	struct lws_context_per_thread *pt = &wsi->a.context->pt[(int)wsi->tsi];
191	struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi);
192	unsigned int ulf = (unsigned int)(((flags & LWS_EV_WRITE) ? ULOOP_WRITE : 0) |
193			    ((flags & LWS_EV_READ) ? ULOOP_READ : 0)), u;
194
195	if (wsi->a.context->being_destroyed || pt->is_destroyed)
196		return;
197
198	assert((flags & (LWS_EV_START | LWS_EV_STOP)) &&
199	       (flags & (LWS_EV_READ | LWS_EV_WRITE)));
200
201	u = wu->actual_events;
202	if (flags & LWS_EV_START)
203		u |= ulf;
204	if (flags & LWS_EV_STOP)
205		u &= ~ulf;
206
207	uloop_fd_add(&wu->fd, u);
208	wu->actual_events = u;
209}
210
211static void
212elops_run_pt_uloop(struct lws_context *context, int tsi)
213{
214	uloop_run();
215}
216
217static int
218elops_listen_destroy_uloop(struct lws_dll2 *d, void *user)
219{
220	struct lws *wsi = lws_container_of(d, struct lws, listen_list);
221	struct lws_wsi_eventlibs_uloop *wu = wsi_to_priv_uloop(wsi);
222
223	uloop_fd_delete(&wu->fd);
224
225	return 0;
226}
227
228static void
229elops_destroy_pt_uloop(struct lws_context *context, int tsi)
230{
231	struct lws_context_per_thread *pt = &context->pt[tsi];
232	struct lws_pt_eventlibs_uloop *ptpr = pt_to_priv_uloop(pt);
233
234	lws_vhost_foreach_listen_wsi(context, NULL, elops_listen_destroy_uloop);
235
236	uloop_timeout_cancel(&ptpr->hrtimer);
237	uloop_timeout_cancel(&ptpr->idle_timer);
238}
239
240static void
241elops_destroy_wsi_uloop(struct lws *wsi)
242{
243	struct lws_context_per_thread *pt;
244
245	if (!wsi)
246		return;
247
248	pt = &wsi->a.context->pt[(int)wsi->tsi];
249	if (pt->is_destroyed)
250		return;
251
252	uloop_fd_delete(&wsi_to_priv_uloop(wsi)->fd);
253}
254
255static int
256elops_wsi_logical_close_uloop(struct lws *wsi)
257{
258	elops_destroy_wsi_uloop(wsi);
259
260	return 0;
261}
262
263static int
264elops_init_vhost_listen_wsi_uloop(struct lws *wsi)
265{
266	struct lws_wsi_eventlibs_uloop *wu;
267
268	if (!wsi) {
269		assert(0);
270		return 0;
271	}
272
273	wu = wsi_to_priv_uloop(wsi);
274	wu->wsi = wsi;
275	wu->fd.fd = wsi->desc.sockfd;
276	wu->fd.cb = lws_uloop_cb;
277	uloop_fd_add(&wu->fd,  ULOOP_READ);
278
279	wu->actual_events = ULOOP_READ;
280
281	return 0;
282}
283
284static const struct lws_event_loop_ops event_loop_ops_uloop = {
285	/* name */			"uloop",
286	/* init_context */		NULL,
287	/* destroy_context1 */		NULL,
288	/* destroy_context2 */		NULL,
289	/* init_vhost_listen_wsi */	elops_init_vhost_listen_wsi_uloop,
290	/* init_pt */			elops_init_pt_uloop,
291	/* wsi_logical_close */		elops_wsi_logical_close_uloop,
292	/* check_client_connect_ok */	NULL,
293	/* close_handle_manually */	NULL,
294	/* accept */			elops_accept_uloop,
295	/* io */			elops_io_uloop,
296	/* run_pt */			elops_run_pt_uloop,
297	/* destroy_pt */		elops_destroy_pt_uloop,
298	/* destroy wsi */		elops_destroy_wsi_uloop,
299	/* foreign_thread */		NULL,
300
301	/* flags */			0,
302
303	/* evlib_size_ctx */	0,
304	/* evlib_size_pt */	sizeof(struct lws_pt_eventlibs_uloop),
305	/* evlib_size_vh */	0,
306	/* evlib_size_wsi */	sizeof(struct lws_wsi_eventlibs_uloop),
307};
308
309#if defined(LWS_WITH_EVLIB_PLUGINS)
310LWS_VISIBLE
311#endif
312const lws_plugin_evlib_t evlib_uloop = {
313	.hdr = {
314		"uloop event loop",
315		"lws_evlib_plugin",
316		LWS_BUILD_HASH,
317		LWS_PLUGIN_API_MAGIC
318	},
319
320	.ops	= &event_loop_ops_uloop
321};
322