Branch data Line data Source code
1 : : /*
2 : : * This file is part of the MicroPython project, http://micropython.org/
3 : : *
4 : : * The MIT License (MIT)
5 : : *
6 : : * Copyright (c) 2014-2023 Damien P. George
7 : : * Copyright (c) 2015-2017 Paul Sokolovsky
8 : : *
9 : : * Permission is hereby granted, free of charge, to any person obtaining a copy
10 : : * of this software and associated documentation files (the "Software"), to deal
11 : : * in the Software without restriction, including without limitation the rights
12 : : * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 : : * copies of the Software, and to permit persons to whom the Software is
14 : : * furnished to do so, subject to the following conditions:
15 : : *
16 : : * The above copyright notice and this permission notice shall be included in
17 : : * all copies or substantial portions of the Software.
18 : : *
19 : : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 : : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 : : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 : : * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 : : * THE SOFTWARE.
26 : : */
27 : :
28 : : #include "py/mpconfig.h"
29 : : #include "py/runtime.h"
30 : : #include "py/obj.h"
31 : : #include "py/objlist.h"
32 : : #include "py/stream.h"
33 : : #include "py/mperrno.h"
34 : : #include "py/mphal.h"
35 : :
36 : : #if MICROPY_PY_SELECT
37 : :
38 : : #if MICROPY_PY_SELECT_SELECT && MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
39 : : #error "select.select is not supported with MICROPY_PY_SELECT_POSIX_OPTIMISATIONS"
40 : : #endif
41 : :
42 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
43 : :
44 : : #include <string.h>
45 : : #include <poll.h>
46 : :
47 : : #if !((MP_STREAM_POLL_RD) == (POLLIN) && \
48 : : (MP_STREAM_POLL_WR) == (POLLOUT) && \
49 : : (MP_STREAM_POLL_ERR) == (POLLERR) && \
50 : : (MP_STREAM_POLL_HUP) == (POLLHUP) && \
51 : : (MP_STREAM_POLL_NVAL) == (POLLNVAL))
52 : : #error "With MICROPY_PY_SELECT_POSIX_OPTIMISATIONS enabled, POLL constants must match"
53 : : #endif
54 : :
55 : : // When non-file-descriptor objects are on the list to be polled (the polling of
56 : : // which involves repeatedly calling ioctl(MP_STREAM_POLL)), this variable sets
57 : : // the period between polling these objects.
58 : : #define MICROPY_PY_SELECT_IOCTL_CALL_PERIOD_MS (1)
59 : :
60 : : #endif
61 : :
62 : : // Flags for ipoll()
63 : : #define FLAG_ONESHOT (1)
64 : :
65 : : // A single pollable object.
66 : : typedef struct _poll_obj_t {
67 : : mp_obj_t obj;
68 : : mp_uint_t (*ioctl)(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode);
69 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
70 : : // If the pollable object has an associated file descriptor, then pollfd points to an entry
71 : : // in poll_set_t::pollfds, and the events/revents fields for this object are stored in the
72 : : // pollfd entry (and the nonfd_* members are unused).
73 : : // Otherwise the object is a non-file-descriptor object and pollfd==NULL, and the events/
74 : : // revents fields are stored in the nonfd_* members (which are named as such so that code
75 : : // doesn't accidentally mix the use of these members when this optimisation is used).
76 : : struct pollfd *pollfd;
77 : : uint16_t nonfd_events;
78 : : uint16_t nonfd_revents;
79 : : #else
80 : : mp_uint_t events;
81 : : mp_uint_t revents;
82 : : #endif
83 : : } poll_obj_t;
84 : :
85 : : // A set of pollable objects.
86 : : typedef struct _poll_set_t {
87 : : // Map containing a dict with key=object to poll, value=its corresponding poll_obj_t.
88 : : mp_map_t map;
89 : :
90 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
91 : : // Array of pollfd entries for objects that have a file descriptor.
92 : : unsigned short alloc; // memory allocated for pollfds
93 : : unsigned short max_used; // maximum number of used entries in pollfds
94 : : unsigned short used; // actual number of used entries in pollfds
95 : : struct pollfd *pollfds;
96 : : #endif
97 : : } poll_set_t;
98 : :
99 : 88 : static void poll_set_init(poll_set_t *poll_set, size_t n) {
100 : 88 : mp_map_init(&poll_set->map, n);
101 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
102 : 88 : poll_set->alloc = 0;
103 : 88 : poll_set->max_used = 0;
104 : 88 : poll_set->used = 0;
105 : 88 : poll_set->pollfds = NULL;
106 : : #endif
107 : 88 : }
108 : :
109 : : #if MICROPY_PY_SELECT_SELECT
110 : : static void poll_set_deinit(poll_set_t *poll_set) {
111 : : mp_map_deinit(&poll_set->map);
112 : : }
113 : : #endif
114 : :
115 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
116 : :
117 : 133 : static mp_uint_t poll_obj_get_events(poll_obj_t *poll_obj) {
118 [ - + ]: 133 : assert(poll_obj->pollfd == NULL);
119 : 133 : return poll_obj->nonfd_events;
120 : : }
121 : :
122 : 14176 : static void poll_obj_set_events(poll_obj_t *poll_obj, mp_uint_t events) {
123 [ + + ]: 14176 : if (poll_obj->pollfd != NULL) {
124 : 14046 : poll_obj->pollfd->events = events;
125 : : } else {
126 : 130 : poll_obj->nonfd_events = events;
127 : : }
128 : 14176 : }
129 : :
130 : 223 : static mp_uint_t poll_obj_get_revents(poll_obj_t *poll_obj) {
131 [ + + ]: 223 : if (poll_obj->pollfd != NULL) {
132 : 67 : return poll_obj->pollfd->revents;
133 : : } else {
134 : 156 : return poll_obj->nonfd_revents;
135 : : }
136 : : }
137 : :
138 : 14179 : static void poll_obj_set_revents(poll_obj_t *poll_obj, mp_uint_t revents) {
139 [ + + ]: 14179 : if (poll_obj->pollfd != NULL) {
140 : 14030 : poll_obj->pollfd->revents = revents;
141 : : } else {
142 : 149 : poll_obj->nonfd_revents = revents;
143 : : }
144 : 14179 : }
145 : :
146 : : // How much (in pollfds) to grow the allocation for poll_set->pollfds by.
147 : : #define POLL_SET_ALLOC_INCREMENT (4)
148 : :
149 : 14030 : static struct pollfd *poll_set_add_fd(poll_set_t *poll_set, int fd) {
150 : 14030 : struct pollfd *free_slot = NULL;
151 : :
152 [ + + ]: 14030 : if (poll_set->used == poll_set->max_used) {
153 : : // No free slots below max_used, so expand max_used (and possibly allocate).
154 [ + + ]: 14018 : if (poll_set->max_used >= poll_set->alloc) {
155 : 3516 : size_t new_alloc = poll_set->alloc + POLL_SET_ALLOC_INCREMENT;
156 : : // Try to grow in-place.
157 : 3516 : struct pollfd *new_fds = m_renew_maybe(struct pollfd, poll_set->pollfds, poll_set->alloc, new_alloc, false);
158 [ + + ]: 3516 : if (!new_fds) {
159 : : // Failed to grow in-place. Do a new allocation and copy over the pollfd values.
160 : 96 : new_fds = m_new(struct pollfd, new_alloc);
161 : 96 : memcpy(new_fds, poll_set->pollfds, sizeof(struct pollfd) * poll_set->alloc);
162 : :
163 : : // Update existing poll_obj_t to update their pollfd field to
164 : : // point to the same offset inside the new allocation.
165 [ + + ]: 84837 : for (mp_uint_t i = 0; i < poll_set->map.alloc; ++i) {
166 [ + + ]: 84741 : if (!mp_map_slot_is_filled(&poll_set->map, i)) {
167 : 17277 : continue;
168 : : }
169 : :
170 : 67464 : poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_set->map.table[i].value);
171 [ + + ]: 67464 : if (!poll_obj) {
172 : : // This is the one we're currently adding,
173 : : // poll_set_add_obj doesn't assign elem->value until
174 : : // afterwards.
175 : 96 : continue;
176 : : }
177 : :
178 : 67368 : poll_obj->pollfd = new_fds + (poll_obj->pollfd - poll_set->pollfds);
179 : : }
180 : :
181 : : // Delete the old allocation.
182 : 96 : m_del(struct pollfd, poll_set->pollfds, poll_set->alloc);
183 : : }
184 : :
185 : 3516 : poll_set->pollfds = new_fds;
186 : 3516 : poll_set->alloc = new_alloc;
187 : : }
188 : 14018 : free_slot = &poll_set->pollfds[poll_set->max_used++];
189 : : } else {
190 : : // There should be a free slot below max_used.
191 [ + - ]: 16 : for (unsigned int i = 0; i < poll_set->max_used; ++i) {
192 : 16 : struct pollfd *slot = &poll_set->pollfds[i];
193 [ + + ]: 16 : if (slot->fd == -1) {
194 : : free_slot = slot;
195 : : break;
196 : : }
197 : : }
198 [ - + ]: 12 : assert(free_slot != NULL);
199 : : }
200 : :
201 : 14030 : free_slot->fd = fd;
202 : 14030 : ++poll_set->used;
203 : :
204 : 14030 : return free_slot;
205 : : }
206 : :
207 : 638722 : static inline bool poll_set_all_are_fds(poll_set_t *poll_set) {
208 : 638722 : return poll_set->map.used == poll_set->used;
209 : : }
210 : :
211 : : #else
212 : :
213 : : static inline mp_uint_t poll_obj_get_events(poll_obj_t *poll_obj) {
214 : : return poll_obj->events;
215 : : }
216 : :
217 : : static inline void poll_obj_set_events(poll_obj_t *poll_obj, mp_uint_t events) {
218 : : poll_obj->events = events;
219 : : }
220 : :
221 : : static inline mp_uint_t poll_obj_get_revents(poll_obj_t *poll_obj) {
222 : : return poll_obj->revents;
223 : : }
224 : :
225 : : static inline void poll_obj_set_revents(poll_obj_t *poll_obj, mp_uint_t revents) {
226 : : poll_obj->revents = revents;
227 : : }
228 : :
229 : : #endif
230 : :
231 : 14054 : static void poll_set_add_obj(poll_set_t *poll_set, const mp_obj_t *obj, mp_uint_t obj_len, mp_uint_t events, bool or_events) {
232 [ + + ]: 28106 : for (mp_uint_t i = 0; i < obj_len; i++) {
233 : 14054 : mp_map_elem_t *elem = mp_map_lookup(&poll_set->map, mp_obj_id(obj[i]), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
234 [ + + ]: 14054 : if (elem->value == MP_OBJ_NULL) {
235 : : // object not found; get its ioctl and add it to the poll list
236 : :
237 : : // If an exception is raised below when adding the new object then the map entry for that
238 : : // object remains unpopulated, and methods like poll() may crash. This case is not handled.
239 : :
240 : 14048 : poll_obj_t *poll_obj = m_new_obj(poll_obj_t);
241 : 14048 : poll_obj->obj = obj[i];
242 : :
243 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
244 : 14048 : int fd = -1;
245 [ + + + - : 14048 : if (mp_obj_is_int(obj[i])) {
- + ]
246 : : // A file descriptor integer passed in as the object, so use it directly.
247 : 14006 : fd = mp_obj_get_int(obj[i]);
248 [ + + ]: 14006 : if (fd < 0) {
249 : 2 : mp_raise_ValueError(NULL);
250 : : }
251 : 14004 : poll_obj->ioctl = NULL;
252 : : } else {
253 : : // An object passed in. Check if it has a file descriptor.
254 : 42 : const mp_stream_p_t *stream_p = mp_get_stream_raise(obj[i], MP_STREAM_OP_IOCTL);
255 : 42 : poll_obj->ioctl = stream_p->ioctl;
256 : 42 : int err;
257 : 42 : mp_uint_t res = stream_p->ioctl(obj[i], MP_STREAM_GET_FILENO, 0, &err);
258 [ + + ]: 42 : if (res != MP_STREAM_ERROR) {
259 : 26 : fd = res;
260 : : }
261 : : }
262 [ + - ]: 14046 : if (fd >= 0) {
263 : : // Object has a file descriptor so add it to pollfds.
264 : 14030 : poll_obj->pollfd = poll_set_add_fd(poll_set, fd);
265 : : } else {
266 : : // Object doesn't have a file descriptor.
267 : 16 : poll_obj->pollfd = NULL;
268 : : }
269 : : #else
270 : : const mp_stream_p_t *stream_p = mp_get_stream_raise(obj[i], MP_STREAM_OP_IOCTL);
271 : : poll_obj->ioctl = stream_p->ioctl;
272 : : #endif
273 : :
274 : 14046 : poll_obj_set_events(poll_obj, events);
275 : 14046 : poll_obj_set_revents(poll_obj, 0);
276 : 14046 : elem->value = MP_OBJ_FROM_PTR(poll_obj);
277 : : } else {
278 : : // object exists; update its events
279 : 6 : poll_obj_t *poll_obj = (poll_obj_t *)MP_OBJ_TO_PTR(elem->value);
280 : : #if MICROPY_PY_SELECT_SELECT
281 : : if (or_events) {
282 : : events |= poll_obj_get_events(poll_obj);
283 : : }
284 : : #else
285 : 6 : (void)or_events;
286 : : #endif
287 : 6 : poll_obj_set_events(poll_obj, events);
288 : : }
289 : : }
290 : 14052 : }
291 : :
292 : : // For each object in the poll set, poll it once.
293 : 133 : static mp_uint_t poll_set_poll_once(poll_set_t *poll_set, size_t *rwx_num) {
294 : 133 : mp_uint_t n_ready = 0;
295 [ + + ]: 409 : for (mp_uint_t i = 0; i < poll_set->map.alloc; ++i) {
296 [ + + ]: 278 : if (!mp_map_slot_is_filled(&poll_set->map, i)) {
297 : 145 : continue;
298 : : }
299 : :
300 : 151 : poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_set->map.table[i].value);
301 : :
302 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
303 [ + + ]: 151 : if (poll_obj->pollfd != NULL) {
304 : : // Object has file descriptor so will be polled separately by poll().
305 : 18 : continue;
306 : : }
307 : : #endif
308 : :
309 : 133 : int errcode;
310 : 133 : mp_int_t ret = poll_obj->ioctl(poll_obj->obj, MP_STREAM_POLL, poll_obj_get_events(poll_obj), &errcode);
311 : 133 : poll_obj_set_revents(poll_obj, ret);
312 : :
313 [ + + ]: 133 : if (ret == -1) {
314 : : // error doing ioctl
315 : 2 : mp_raise_OSError(errcode);
316 : : }
317 : :
318 [ + + ]: 131 : if (ret != 0) {
319 : : // object is ready
320 : 76 : n_ready += 1;
321 : : #if MICROPY_PY_SELECT_SELECT
322 : : if (rwx_num != NULL) {
323 : : if (ret & MP_STREAM_POLL_RD) {
324 : : rwx_num[0] += 1;
325 : : }
326 : : if (ret & MP_STREAM_POLL_WR) {
327 : : rwx_num[1] += 1;
328 : : }
329 : : if ((ret & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) {
330 : : rwx_num[2] += 1;
331 : : }
332 : : }
333 : : #else
334 : 131 : (void)rwx_num;
335 : : #endif
336 : : }
337 : : }
338 : 131 : return n_ready;
339 : : }
340 : :
341 : 319324 : static mp_uint_t poll_set_poll_until_ready_or_timeout(poll_set_t *poll_set, size_t *rwx_num, mp_uint_t timeout) {
342 : 319324 : mp_uint_t start_ticks = mp_hal_ticks_ms();
343 : 319324 : bool has_timeout = timeout != (mp_uint_t)-1;
344 : :
345 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
346 : :
347 : 319438 : for (;;) {
348 : 319362 : MP_THREAD_GIL_EXIT();
349 : :
350 : : // Compute the timeout.
351 : 319362 : int t = MICROPY_PY_SELECT_IOCTL_CALL_PERIOD_MS;
352 [ + + ]: 319362 : if (poll_set_all_are_fds(poll_set)) {
353 : : // All our pollables are file descriptors, so we can use a blocking
354 : : // poll and let it (the underlying system) handle the timeout.
355 [ + + ]: 319229 : if (timeout == (mp_uint_t)-1) {
356 : : t = -1;
357 : : } else {
358 : 319223 : mp_uint_t delta = mp_hal_ticks_ms() - start_ticks;
359 [ + + ]: 319223 : if (delta >= timeout) {
360 : : t = 0;
361 : : } else {
362 : 148 : t = timeout - delta;
363 : : }
364 : : }
365 : : }
366 : :
367 : : // Call system poll for those objects that have a file descriptor.
368 : 319362 : int n_ready = poll(poll_set->pollfds, poll_set->max_used, t);
369 : :
370 : 319362 : MP_THREAD_GIL_ENTER();
371 : :
372 : : // The call to poll() may have been interrupted, but per PEP 475 we must retry if the
373 : : // signal is EINTR (this implements a special case of calling MP_HAL_RETRY_SYSCALL()).
374 [ + + ]: 319362 : if (n_ready == -1) {
375 : 4 : int err = errno;
376 [ + + ]: 4 : if (err != EINTR) {
377 : 2 : mp_raise_OSError(err);
378 : : }
379 : : n_ready = 0;
380 : : }
381 : :
382 : : // Explicitly poll any objects that do not have a file descriptor.
383 [ + + ]: 319360 : if (!poll_set_all_are_fds(poll_set)) {
384 : 133 : n_ready += poll_set_poll_once(poll_set, rwx_num);
385 : : }
386 : :
387 : : // Return if an object is ready, or if the timeout expired.
388 [ + + + + : 319358 : if (n_ready > 0 || (has_timeout && mp_hal_ticks_ms() - start_ticks >= timeout)) {
+ + ]
389 : 319320 : return n_ready;
390 : : }
391 : :
392 : : // This would be mp_event_wait_ms() but the call to poll() above already includes a delay.
393 : 38 : mp_event_handle_nowait();
394 : : }
395 : :
396 : : #else
397 : :
398 : : for (;;) {
399 : : // poll the objects
400 : : mp_uint_t n_ready = poll_set_poll_once(poll_set, rwx_num);
401 : : uint32_t elapsed = mp_hal_ticks_ms() - start_ticks;
402 : : if (n_ready > 0 || (has_timeout && elapsed >= timeout)) {
403 : : return n_ready;
404 : : }
405 : : if (has_timeout) {
406 : : mp_event_wait_ms(timeout - elapsed);
407 : : } else {
408 : : mp_event_wait_indefinite();
409 : : }
410 : : }
411 : :
412 : : #endif
413 : : }
414 : :
415 : : #if MICROPY_PY_SELECT_SELECT
416 : : // select(rlist, wlist, xlist[, timeout])
417 : : static mp_obj_t select_select(size_t n_args, const mp_obj_t *args) {
418 : : // get array data from tuple/list arguments
419 : : size_t rwx_len[3];
420 : : mp_obj_t *r_array, *w_array, *x_array;
421 : : mp_obj_get_array(args[0], &rwx_len[0], &r_array);
422 : : mp_obj_get_array(args[1], &rwx_len[1], &w_array);
423 : : mp_obj_get_array(args[2], &rwx_len[2], &x_array);
424 : :
425 : : // get timeout
426 : : mp_uint_t timeout = -1;
427 : : if (n_args == 4) {
428 : : if (args[3] != mp_const_none) {
429 : : #if MICROPY_PY_BUILTINS_FLOAT
430 : : float timeout_f = mp_obj_get_float_to_f(args[3]);
431 : : if (timeout_f >= 0) {
432 : : timeout = (mp_uint_t)(timeout_f * 1000);
433 : : }
434 : : #else
435 : : timeout = mp_obj_get_int(args[3]) * 1000;
436 : : #endif
437 : : }
438 : : }
439 : :
440 : : // merge separate lists and get the ioctl function for each object
441 : : poll_set_t poll_set;
442 : : poll_set_init(&poll_set, rwx_len[0] + rwx_len[1] + rwx_len[2]);
443 : : poll_set_add_obj(&poll_set, r_array, rwx_len[0], MP_STREAM_POLL_RD, true);
444 : : poll_set_add_obj(&poll_set, w_array, rwx_len[1], MP_STREAM_POLL_WR, true);
445 : : poll_set_add_obj(&poll_set, x_array, rwx_len[2], MP_STREAM_POLL_ERR | MP_STREAM_POLL_HUP, true);
446 : :
447 : : // poll all objects
448 : : rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
449 : : poll_set_poll_until_ready_or_timeout(&poll_set, rwx_len, timeout);
450 : :
451 : : // one or more objects are ready, or we had a timeout
452 : : mp_obj_t list_array[3];
453 : : list_array[0] = mp_obj_new_list(rwx_len[0], NULL);
454 : : list_array[1] = mp_obj_new_list(rwx_len[1], NULL);
455 : : list_array[2] = mp_obj_new_list(rwx_len[2], NULL);
456 : : rwx_len[0] = rwx_len[1] = rwx_len[2] = 0;
457 : : for (mp_uint_t i = 0; i < poll_set.map.alloc; ++i) {
458 : : if (!mp_map_slot_is_filled(&poll_set.map, i)) {
459 : : continue;
460 : : }
461 : : poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_set.map.table[i].value);
462 : : if (poll_obj->revents & MP_STREAM_POLL_RD) {
463 : : ((mp_obj_list_t *)MP_OBJ_TO_PTR(list_array[0]))->items[rwx_len[0]++] = poll_obj->obj;
464 : : }
465 : : if (poll_obj->revents & MP_STREAM_POLL_WR) {
466 : : ((mp_obj_list_t *)MP_OBJ_TO_PTR(list_array[1]))->items[rwx_len[1]++] = poll_obj->obj;
467 : : }
468 : : if ((poll_obj->revents & ~(MP_STREAM_POLL_RD | MP_STREAM_POLL_WR)) != 0) {
469 : : ((mp_obj_list_t *)MP_OBJ_TO_PTR(list_array[2]))->items[rwx_len[2]++] = poll_obj->obj;
470 : : }
471 : : }
472 : : poll_set_deinit(&poll_set);
473 : : return mp_obj_new_tuple(3, list_array);
474 : : }
475 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_select_obj, 3, 4, select_select);
476 : : #endif // MICROPY_PY_SELECT_SELECT
477 : :
478 : : typedef struct _mp_obj_poll_t {
479 : : mp_obj_base_t base;
480 : : poll_set_t poll_set;
481 : : short iter_cnt;
482 : : short iter_idx;
483 : : int flags;
484 : : // callee-owned tuple
485 : : mp_obj_t ret_tuple;
486 : : } mp_obj_poll_t;
487 : :
488 : : // register(obj[, eventmask])
489 : 14054 : static mp_obj_t poll_register(size_t n_args, const mp_obj_t *args) {
490 : 14054 : mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
491 : 14054 : mp_uint_t events;
492 [ + + ]: 14054 : if (n_args == 3) {
493 : 28 : events = mp_obj_get_int(args[2]);
494 : : } else {
495 : : events = MP_STREAM_POLL_RD | MP_STREAM_POLL_WR;
496 : : }
497 : 14054 : poll_set_add_obj(&self->poll_set, &args[1], 1, events, false);
498 : 14052 : return mp_const_none;
499 : : }
500 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register);
501 : :
502 : : // unregister(obj)
503 : 2030 : static mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) {
504 : 2030 : mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
505 : 2030 : mp_map_elem_t *elem = mp_map_lookup(&self->poll_set.map, mp_obj_id(obj_in), MP_MAP_LOOKUP_REMOVE_IF_FOUND);
506 : :
507 : : #if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
508 [ + - ]: 2030 : if (elem != NULL) {
509 : 2030 : poll_obj_t *poll_obj = (poll_obj_t *)MP_OBJ_TO_PTR(elem->value);
510 [ + + ]: 2030 : if (poll_obj->pollfd != NULL) {
511 : 2014 : poll_obj->pollfd->fd = -1;
512 : 2014 : --self->poll_set.used;
513 : : }
514 : 2030 : elem->value = MP_OBJ_NULL;
515 : : }
516 : : #else
517 : : (void)elem;
518 : : #endif
519 : :
520 : : // TODO raise KeyError if obj didn't exist in map
521 : 2030 : return mp_const_none;
522 : : }
523 : : MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister);
524 : :
525 : : // modify(obj, eventmask)
526 : 124 : static mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) {
527 : 124 : mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
528 : 124 : mp_map_elem_t *elem = mp_map_lookup(&self->poll_set.map, mp_obj_id(obj_in), MP_MAP_LOOKUP);
529 [ + + ]: 124 : if (elem == NULL) {
530 : 2 : mp_raise_OSError(MP_ENOENT);
531 : : }
532 : 122 : poll_obj_set_events((poll_obj_t *)MP_OBJ_TO_PTR(elem->value), mp_obj_get_int(eventmask_in));
533 : 122 : return mp_const_none;
534 : : }
535 : : MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify);
536 : :
537 : 319324 : static mp_uint_t poll_poll_internal(uint n_args, const mp_obj_t *args) {
538 : 319324 : mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
539 : :
540 : : // work out timeout (its given already in ms)
541 : 319324 : mp_uint_t timeout = -1;
542 : 319324 : int flags = 0;
543 [ + + ]: 319324 : if (n_args >= 2) {
544 [ + - ]: 319310 : if (args[1] != mp_const_none) {
545 : 319310 : mp_int_t timeout_i = mp_obj_get_int(args[1]);
546 [ + + ]: 319310 : if (timeout_i >= 0) {
547 : 319249 : timeout = timeout_i;
548 : : }
549 : : }
550 [ + + ]: 319310 : if (n_args >= 3) {
551 : 2 : flags = mp_obj_get_int(args[2]);
552 : : }
553 : : }
554 : :
555 : 319324 : self->flags = flags;
556 : :
557 : 319324 : return poll_set_poll_until_ready_or_timeout(&self->poll_set, NULL, timeout);
558 : : }
559 : :
560 : 34 : static mp_obj_t poll_poll(size_t n_args, const mp_obj_t *args) {
561 : 34 : mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
562 : 34 : mp_uint_t n_ready = poll_poll_internal(n_args, args);
563 : :
564 : : // one or more objects are ready, or we had a timeout
565 : 30 : mp_obj_list_t *ret_list = MP_OBJ_TO_PTR(mp_obj_new_list(n_ready, NULL));
566 : 30 : n_ready = 0;
567 [ + + ]: 2543 : for (mp_uint_t i = 0; i < self->poll_set.map.alloc; ++i) {
568 [ + + ]: 2513 : if (!mp_map_slot_is_filled(&self->poll_set.map, i)) {
569 : 2467 : continue;
570 : : }
571 : 46 : poll_obj_t *poll_obj = MP_OBJ_TO_PTR(self->poll_set.map.table[i].value);
572 [ + + ]: 46 : if (poll_obj_get_revents(poll_obj) != 0) {
573 : 26 : mp_obj_t tuple[2] = {poll_obj->obj, MP_OBJ_NEW_SMALL_INT(poll_obj_get_revents(poll_obj))};
574 : 26 : ret_list->items[n_ready++] = mp_obj_new_tuple(2, tuple);
575 : : }
576 : : }
577 : 30 : return MP_OBJ_FROM_PTR(ret_list);
578 : : }
579 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 2, poll_poll);
580 : :
581 : 319290 : static mp_obj_t poll_ipoll(size_t n_args, const mp_obj_t *args) {
582 : 319290 : mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
583 : :
584 [ + + ]: 319290 : if (self->ret_tuple == MP_OBJ_NULL) {
585 : 66 : self->ret_tuple = mp_obj_new_tuple(2, NULL);
586 : : }
587 : :
588 : 319290 : int n_ready = poll_poll_internal(n_args, args);
589 : 319290 : self->iter_cnt = n_ready;
590 : 319290 : self->iter_idx = 0;
591 : :
592 : 319290 : return args[0];
593 : : }
594 : : MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_ipoll_obj, 1, 3, poll_ipoll);
595 : :
596 : 319366 : static mp_obj_t poll_iternext(mp_obj_t self_in) {
597 : 319366 : mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
598 : :
599 [ + + ]: 319366 : if (self->iter_cnt == 0) {
600 : : return MP_OBJ_STOP_ITERATION;
601 : : }
602 : :
603 : 76 : self->iter_cnt--;
604 : :
605 [ + - ]: 78 : for (mp_uint_t i = self->iter_idx; i < self->poll_set.map.alloc; ++i) {
606 : 78 : self->iter_idx++;
607 [ + + ]: 78 : if (!mp_map_slot_is_filled(&self->poll_set.map, i)) {
608 : 2 : continue;
609 : : }
610 : 76 : poll_obj_t *poll_obj = MP_OBJ_TO_PTR(self->poll_set.map.table[i].value);
611 [ + - ]: 76 : if (poll_obj_get_revents(poll_obj) != 0) {
612 : 76 : mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->ret_tuple);
613 : 76 : t->items[0] = poll_obj->obj;
614 : 76 : t->items[1] = MP_OBJ_NEW_SMALL_INT(poll_obj_get_revents(poll_obj));
615 [ + + ]: 76 : if (self->flags & FLAG_ONESHOT) {
616 : : // Don't poll next time, until new event mask will be set explicitly
617 : 2 : poll_obj_set_events(poll_obj, 0);
618 : : }
619 : 76 : return MP_OBJ_FROM_PTR(t);
620 : : }
621 : : }
622 : :
623 : 0 : assert(!"inconsistent number of poll active entries");
624 : : self->iter_cnt = 0;
625 : : return MP_OBJ_STOP_ITERATION;
626 : : }
627 : :
628 : : static const mp_rom_map_elem_t poll_locals_dict_table[] = {
629 : : { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) },
630 : : { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&poll_unregister_obj) },
631 : : { MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) },
632 : : { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) },
633 : : { MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) },
634 : : };
635 : : static MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table);
636 : :
637 : : static MP_DEFINE_CONST_OBJ_TYPE(
638 : : mp_type_poll,
639 : : MP_QSTR_poll,
640 : : MP_TYPE_FLAG_ITER_IS_ITERNEXT,
641 : : iter, poll_iternext,
642 : : locals_dict, &poll_locals_dict
643 : : );
644 : :
645 : : // poll()
646 : 88 : static mp_obj_t select_poll(void) {
647 : 88 : mp_obj_poll_t *poll = mp_obj_malloc(mp_obj_poll_t, &mp_type_poll);
648 : 88 : poll_set_init(&poll->poll_set, 0);
649 : 88 : poll->iter_cnt = 0;
650 : 88 : poll->ret_tuple = MP_OBJ_NULL;
651 : 88 : return MP_OBJ_FROM_PTR(poll);
652 : : }
653 : : MP_DEFINE_CONST_FUN_OBJ_0(mp_select_poll_obj, select_poll);
654 : :
655 : : static const mp_rom_map_elem_t mp_module_select_globals_table[] = {
656 : : { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_select) },
657 : : #if MICROPY_PY_SELECT_SELECT
658 : : { MP_ROM_QSTR(MP_QSTR_select), MP_ROM_PTR(&mp_select_select_obj) },
659 : : #endif
660 : : { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mp_select_poll_obj) },
661 : : { MP_ROM_QSTR(MP_QSTR_POLLIN), MP_ROM_INT(MP_STREAM_POLL_RD) },
662 : : { MP_ROM_QSTR(MP_QSTR_POLLOUT), MP_ROM_INT(MP_STREAM_POLL_WR) },
663 : : { MP_ROM_QSTR(MP_QSTR_POLLERR), MP_ROM_INT(MP_STREAM_POLL_ERR) },
664 : : { MP_ROM_QSTR(MP_QSTR_POLLHUP), MP_ROM_INT(MP_STREAM_POLL_HUP) },
665 : : };
666 : :
667 : : static MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table);
668 : :
669 : : const mp_obj_module_t mp_module_select = {
670 : : .base = { &mp_type_module },
671 : : .globals = (mp_obj_dict_t *)&mp_module_select_globals,
672 : : };
673 : :
674 : : MP_REGISTER_EXTENSIBLE_MODULE(MP_QSTR_select, mp_module_select);
675 : :
676 : : #endif // MICROPY_PY_SELECT
|