1 #include "uv.h"
2 #include "poll_oneoff.h"
3 #include "uv_mapping.h"
4 #include "uvwasi_alloc.h"
5
6
poll_cb(uv_poll_t * handle,int status,int events)7 static void poll_cb(uv_poll_t* handle, int status, int events) {
8 struct uvwasi_poll_oneoff_state_t* state;
9 struct uvwasi__poll_fdevent_t* event;
10
11 uv_poll_stop(handle);
12 event = uv_handle_get_data((uv_handle_t*) handle);
13 event->revents = events;
14
15 if (status != 0)
16 event->error = UVWASI_EIO;
17
18 state = uv_loop_get_data(handle->loop);
19 state->result++;
20 }
21
22
timeout_cb(uv_timer_t * handle)23 static void timeout_cb(uv_timer_t* handle) {
24 struct uvwasi_poll_oneoff_state_t* state;
25 uvwasi_size_t i;
26
27 state = uv_loop_get_data(handle->loop);
28
29 for (i = 0; i < state->handle_cnt; i++)
30 uv_poll_stop(&state->poll_handles[i]);
31 }
32
33
uvwasi__poll_oneoff_state_init(uvwasi_t * uvwasi,struct uvwasi_poll_oneoff_state_t * state,uvwasi_size_t max_fds)34 uvwasi_errno_t uvwasi__poll_oneoff_state_init(
35 uvwasi_t* uvwasi,
36 struct uvwasi_poll_oneoff_state_t* state,
37 uvwasi_size_t max_fds
38 ) {
39 uvwasi_errno_t err;
40 int r;
41
42 if (uvwasi == NULL || state == NULL)
43 return UVWASI_EINVAL;
44
45 state->uvwasi = NULL;
46 state->timeout = 0;
47 state->has_timer = 0;
48 state->fdevents = NULL;
49 state->poll_handles = NULL;
50 state->max_fds = 0;
51 state->fdevent_cnt = 0;
52 state->handle_cnt = 0;
53 state->result = 0;
54
55 r = uv_loop_init(&state->loop);
56 if (r != 0)
57 return uvwasi__translate_uv_error(r);
58
59 if (max_fds > 0) {
60 state->fdevents = uvwasi__calloc(uvwasi,
61 max_fds,
62 sizeof(*state->fdevents));
63 if (state->fdevents == NULL) {
64 err = UVWASI_ENOMEM;
65 goto error_exit;
66 }
67
68 state->poll_handles = uvwasi__calloc(uvwasi,
69 max_fds,
70 sizeof(*state->poll_handles));
71 if (state->poll_handles == NULL) {
72 err = UVWASI_ENOMEM;
73 goto error_exit;
74 }
75 }
76
77 uv_loop_set_data(&state->loop, (void*) state);
78 state->uvwasi = uvwasi;
79 state->max_fds = max_fds;
80
81 return UVWASI_ESUCCESS;
82
83 error_exit:
84 uv_loop_close(&state->loop);
85 uvwasi__free(state->uvwasi, state->fdevents);
86 uvwasi__free(state->uvwasi, state->poll_handles);
87 return err;
88 }
89
90
uvwasi__poll_oneoff_state_cleanup(struct uvwasi_poll_oneoff_state_t * state)91 uvwasi_errno_t uvwasi__poll_oneoff_state_cleanup(
92 struct uvwasi_poll_oneoff_state_t* state
93 ) {
94 struct uvwasi__poll_fdevent_t* event;
95 uvwasi_size_t i;
96 int r;
97
98 if (state == NULL)
99 return UVWASI_EINVAL;
100
101 if (state->has_timer != 0) {
102 state->timeout = 0;
103 state->has_timer = 0;
104 uv_close((uv_handle_t*) &state->timer, NULL);
105 }
106
107 for (i = 0; i < state->fdevent_cnt; i++) {
108 event = &state->fdevents[i];
109
110 if (event->is_duplicate_fd == 0 && event->wrap != NULL)
111 uv_mutex_unlock(&event->wrap->mutex);
112 }
113
114 for (i = 0; i < state->handle_cnt; i++)
115 uv_close((uv_handle_t*) &state->poll_handles[i], NULL);
116
117 uv_run(&state->loop, UV_RUN_NOWAIT);
118
119 state->max_fds = 0;
120 state->fdevent_cnt = 0;
121 state->handle_cnt = 0;
122
123 uvwasi__free(state->uvwasi, state->fdevents);
124 uvwasi__free(state->uvwasi, state->poll_handles);
125 state->fdevents = NULL;
126 state->poll_handles = NULL;
127 state->uvwasi = NULL;
128
129 r = uv_loop_close(&state->loop);
130 if (r != 0)
131 return uvwasi__translate_uv_error(r);
132
133 return UVWASI_ESUCCESS;
134 }
135
136
uvwasi__poll_oneoff_state_set_timer(struct uvwasi_poll_oneoff_state_t * state,uvwasi_timestamp_t timeout)137 uvwasi_errno_t uvwasi__poll_oneoff_state_set_timer(
138 struct uvwasi_poll_oneoff_state_t* state,
139 uvwasi_timestamp_t timeout
140 ) {
141 int r;
142
143 if (state == NULL)
144 return UVWASI_EINVAL;
145
146 r = uv_timer_init(&state->loop, &state->timer);
147 if (r != 0)
148 return uvwasi__translate_uv_error(r);
149
150 /* Convert WASI timeout from nanoseconds to milliseconds for libuv. */
151 state->timeout = timeout / 1000000;
152 state->has_timer = 1;
153 return UVWASI_ESUCCESS;
154 }
155
156
uvwasi__poll_oneoff_state_add_fdevent(struct uvwasi_poll_oneoff_state_t * state,uvwasi_subscription_t * subscription)157 uvwasi_errno_t uvwasi__poll_oneoff_state_add_fdevent(
158 struct uvwasi_poll_oneoff_state_t* state,
159 uvwasi_subscription_t* subscription
160 ) {
161 struct uvwasi__poll_fdevent_t* event;
162 struct uvwasi__poll_fdevent_t* dup;
163 uv_poll_t* poll_handle;
164 uvwasi_eventtype_t type;
165 uvwasi_rights_t rights;
166 uvwasi_fd_t fd;
167 uvwasi_errno_t err;
168 uvwasi_size_t i;
169 int r;
170
171 if (state == NULL)
172 return UVWASI_EINVAL;
173
174 event = &state->fdevents[state->fdevent_cnt];
175 fd = subscription->u.fd_readwrite.fd;
176 type = subscription->type;
177
178 if (type == UVWASI_EVENTTYPE_FD_READ) {
179 event->events = UV_DISCONNECT | UV_READABLE;
180 rights = UVWASI_RIGHT_POLL_FD_READWRITE | UVWASI_RIGHT_FD_READ;
181 } else if (type == UVWASI_EVENTTYPE_FD_WRITE) {
182 event->events = UV_DISCONNECT | UV_WRITABLE;
183 rights = UVWASI_RIGHT_POLL_FD_READWRITE | UVWASI_RIGHT_FD_WRITE;
184 } else {
185 return UVWASI_EINVAL;
186 }
187
188 /* Check if the same file descriptor is already being polled. If so, use the
189 wrap and poll handle from the first descriptor. The reasons are that libuv
190 does not support polling the same fd more than once at the same time, and
191 uvwasi has the fd's mutex locked. */
192 event->is_duplicate_fd = 0;
193 for (i = 0; i < state->fdevent_cnt; i++) {
194 dup = &state->fdevents[i];
195 if (dup->wrap->id == fd) {
196 event->is_duplicate_fd = 1;
197 event->wrap = dup->wrap;
198 event->poll_handle = dup->poll_handle;
199 err = event->error;
200 goto poll_config_done;
201 }
202 }
203
204 /* Get the file descriptor. If UVWASI_EBADF is returned, continue on, but
205 don't do any polling with the handle. */
206 err = uvwasi_fd_table_get(state->uvwasi->fds, fd, &event->wrap, rights, 0);
207 if (err == UVWASI_EBADF)
208 event->wrap = NULL;
209 else if (err != UVWASI_ESUCCESS)
210 return err;
211
212 if (err == UVWASI_ESUCCESS) {
213 /* The fd is valid, so setup the poll handle. */
214 poll_handle = &state->poll_handles[state->handle_cnt];
215 r = uv_poll_init(&state->loop, poll_handle, event->wrap->fd);
216
217 if (r != 0) {
218 /* If uv_poll_init() fails (for example on Windows because only sockets
219 are supported), set the error for this event to UVWASI_EBADF, but don't
220 do any polling with the handle. */
221 uv_mutex_unlock(&event->wrap->mutex);
222 return uvwasi__translate_uv_error(r);
223 } else {
224 r = uv_poll_start(poll_handle,
225 event->events,
226 poll_cb);
227 if (r != 0) {
228 uv_mutex_unlock(&event->wrap->mutex);
229 uv_close((uv_handle_t*) poll_handle, NULL);
230 return uvwasi__translate_uv_error(r);
231 }
232
233 uv_handle_set_data((uv_handle_t*) poll_handle,
234 (void*) &state->fdevents[state->fdevent_cnt]);
235 event->poll_handle = poll_handle;
236 state->handle_cnt++;
237 }
238 }
239
240 poll_config_done:
241 event->type = type;
242 event->userdata = subscription->userdata;
243 event->error = err;
244 event->revents = 0;
245 state->fdevent_cnt++;
246 return UVWASI_ESUCCESS;
247 }
248
249
uvwasi__poll_oneoff_run(struct uvwasi_poll_oneoff_state_t * state)250 uvwasi_errno_t uvwasi__poll_oneoff_run(
251 struct uvwasi_poll_oneoff_state_t* state
252 ) {
253 int r;
254
255 if (state->has_timer == 1) {
256 r = uv_timer_start(&state->timer, timeout_cb, state->timeout, 0);
257 if (r != 0)
258 return uvwasi__translate_uv_error(r);
259
260 if (state->fdevent_cnt > 0)
261 uv_unref((uv_handle_t*) &state->timer);
262 }
263
264 r = uv_run(&state->loop, UV_RUN_DEFAULT);
265 if (r != 0)
266 return uvwasi__translate_uv_error(r);
267
268 return UVWASI_ESUCCESS;
269 }
270