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