1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2014 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 
30 #if MICROPY_PY_USELECT_POSIX
31 
32 #if MICROPY_PY_USELECT
33 #error "Can't have both MICROPY_PY_USELECT and MICROPY_PY_USELECT_POSIX."
34 #endif
35 
36 #include <stdio.h>
37 #include <errno.h>
38 #include <poll.h>
39 
40 #include "py/runtime.h"
41 #include "py/stream.h"
42 #include "py/obj.h"
43 #include "py/objlist.h"
44 #include "py/objtuple.h"
45 #include "py/mphal.h"
46 #include "py/mpthread.h"
47 
48 #define DEBUG 0
49 
50 #if MICROPY_PY_SOCKET
51 extern const mp_obj_type_t mp_type_socket;
52 #endif
53 
54 // Flags for poll()
55 #define FLAG_ONESHOT (1)
56 
57 /// \class Poll - poll class
58 
59 typedef struct _mp_obj_poll_t {
60     mp_obj_base_t base;
61     unsigned short alloc;
62     unsigned short len;
63     struct pollfd *entries;
64     mp_obj_t *obj_map;
65     short iter_cnt;
66     short iter_idx;
67     int flags;
68     // callee-owned tuple
69     mp_obj_t ret_tuple;
70 } mp_obj_poll_t;
71 
get_fd(mp_obj_t fdlike)72 STATIC int get_fd(mp_obj_t fdlike) {
73     if (mp_obj_is_obj(fdlike)) {
74         const mp_stream_p_t *stream_p = mp_get_stream_raise(fdlike, MP_STREAM_OP_IOCTL);
75         int err;
76         mp_uint_t res = stream_p->ioctl(fdlike, MP_STREAM_GET_FILENO, 0, &err);
77         if (res != MP_STREAM_ERROR) {
78             return res;
79         }
80     }
81     return mp_obj_get_int(fdlike);
82 }
83 
84 /// \method register(obj[, eventmask])
poll_register(size_t n_args,const mp_obj_t * args)85 STATIC mp_obj_t poll_register(size_t n_args, const mp_obj_t *args) {
86     mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
87     bool is_fd = mp_obj_is_int(args[1]);
88     int fd = get_fd(args[1]);
89 
90     mp_uint_t flags;
91     if (n_args == 3) {
92         flags = mp_obj_get_int(args[2]);
93     } else {
94         flags = POLLIN | POLLOUT;
95     }
96 
97     struct pollfd *free_slot = NULL;
98 
99     struct pollfd *entry = self->entries;
100     for (int i = 0; i < self->len; i++, entry++) {
101         int entry_fd = entry->fd;
102         if (entry_fd == fd) {
103             entry->events = flags;
104             return mp_const_false;
105         }
106         if (entry_fd == -1) {
107             free_slot = entry;
108         }
109     }
110 
111     if (free_slot == NULL) {
112         if (self->len >= self->alloc) {
113             self->entries = m_renew(struct pollfd, self->entries, self->alloc, self->alloc + 4);
114             if (self->obj_map) {
115                 self->obj_map = m_renew(mp_obj_t, self->obj_map, self->alloc, self->alloc + 4);
116             }
117             self->alloc += 4;
118         }
119         free_slot = &self->entries[self->len++];
120     }
121 
122     if (!is_fd) {
123         if (self->obj_map == NULL) {
124             self->obj_map = m_new0(mp_obj_t, self->alloc);
125         }
126         self->obj_map[free_slot - self->entries] = args[1];
127     }
128 
129     free_slot->fd = fd;
130     free_slot->events = flags;
131     free_slot->revents = 0;
132     return mp_const_true;
133 }
134 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register);
135 
136 /// \method unregister(obj)
poll_unregister(mp_obj_t self_in,mp_obj_t obj_in)137 STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) {
138     mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
139     struct pollfd *entries = self->entries;
140     int fd = get_fd(obj_in);
141     for (int i = self->len - 1; i >= 0; i--) {
142         if (entries->fd == fd) {
143             entries->fd = -1;
144             if (self->obj_map) {
145                 self->obj_map[entries - self->entries] = MP_OBJ_NULL;
146             }
147             break;
148         }
149         entries++;
150     }
151 
152     // TODO raise KeyError if obj didn't exist in map
153     return mp_const_none;
154 }
155 MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister);
156 
157 /// \method modify(obj, eventmask)
poll_modify(mp_obj_t self_in,mp_obj_t obj_in,mp_obj_t eventmask_in)158 STATIC mp_obj_t poll_modify(mp_obj_t self_in, mp_obj_t obj_in, mp_obj_t eventmask_in) {
159     mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
160     struct pollfd *entries = self->entries;
161     int fd = get_fd(obj_in);
162     for (int i = self->len - 1; i >= 0; i--) {
163         if (entries->fd == fd) {
164             entries->events = mp_obj_get_int(eventmask_in);
165             return mp_const_none;
166         }
167         entries++;
168     }
169 
170     // obj doesn't exist in poller
171     mp_raise_OSError(MP_ENOENT);
172 }
173 MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify);
174 
poll_poll_internal(size_t n_args,const mp_obj_t * args)175 STATIC int poll_poll_internal(size_t n_args, const mp_obj_t *args) {
176     mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
177 
178     // work out timeout (it's given already in ms)
179     int timeout = -1;
180     int flags = 0;
181     if (n_args >= 2) {
182         if (args[1] != mp_const_none) {
183             mp_int_t timeout_i = mp_obj_get_int(args[1]);
184             if (timeout_i >= 0) {
185                 timeout = timeout_i;
186             }
187         }
188         if (n_args >= 3) {
189             flags = mp_obj_get_int(args[2]);
190         }
191     }
192 
193     self->flags = flags;
194 
195     int n_ready;
196     MP_HAL_RETRY_SYSCALL(n_ready, poll(self->entries, self->len, timeout), mp_raise_OSError(err));
197     return n_ready;
198 }
199 
200 /// \method poll([timeout])
201 /// Timeout is in milliseconds.
poll_poll(size_t n_args,const mp_obj_t * args)202 STATIC mp_obj_t poll_poll(size_t n_args, const mp_obj_t *args) {
203     int n_ready = poll_poll_internal(n_args, args);
204 
205     if (n_ready == 0) {
206         return mp_const_empty_tuple;
207     }
208 
209     mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
210 
211     mp_obj_list_t *ret_list = MP_OBJ_TO_PTR(mp_obj_new_list(n_ready, NULL));
212     int ret_i = 0;
213     struct pollfd *entries = self->entries;
214     for (int i = 0; i < self->len; i++, entries++) {
215         if (entries->revents != 0) {
216             mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(2, NULL));
217             // If there's an object stored, return it, otherwise raw fd
218             if (self->obj_map && self->obj_map[i] != MP_OBJ_NULL) {
219                 t->items[0] = self->obj_map[i];
220             } else {
221                 t->items[0] = MP_OBJ_NEW_SMALL_INT(entries->fd);
222             }
223             t->items[1] = MP_OBJ_NEW_SMALL_INT(entries->revents);
224             ret_list->items[ret_i++] = MP_OBJ_FROM_PTR(t);
225             if (self->flags & FLAG_ONESHOT) {
226                 entries->events = 0;
227             }
228         }
229     }
230 
231     return MP_OBJ_FROM_PTR(ret_list);
232 }
233 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll);
234 
poll_ipoll(size_t n_args,const mp_obj_t * args)235 STATIC mp_obj_t poll_ipoll(size_t n_args, const mp_obj_t *args) {
236     mp_obj_poll_t *self = MP_OBJ_TO_PTR(args[0]);
237 
238     if (self->ret_tuple == MP_OBJ_NULL) {
239         self->ret_tuple = mp_obj_new_tuple(2, NULL);
240     }
241 
242     int n_ready = poll_poll_internal(n_args, args);
243     self->iter_cnt = n_ready;
244     self->iter_idx = 0;
245 
246     return args[0];
247 }
248 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_ipoll_obj, 1, 3, poll_ipoll);
249 
poll_iternext(mp_obj_t self_in)250 STATIC mp_obj_t poll_iternext(mp_obj_t self_in) {
251     mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
252 
253     if (self->iter_cnt == 0) {
254         return MP_OBJ_STOP_ITERATION;
255     }
256 
257     self->iter_cnt--;
258 
259     struct pollfd *entries = self->entries + self->iter_idx;
260     for (int i = self->iter_idx; i < self->len; i++, entries++) {
261         self->iter_idx++;
262         if (entries->revents != 0) {
263             mp_obj_tuple_t *t = MP_OBJ_TO_PTR(self->ret_tuple);
264             // If there's an object stored, return it, otherwise raw fd
265             if (self->obj_map && self->obj_map[i] != MP_OBJ_NULL) {
266                 t->items[0] = self->obj_map[i];
267             } else {
268                 t->items[0] = MP_OBJ_NEW_SMALL_INT(entries->fd);
269             }
270             t->items[1] = MP_OBJ_NEW_SMALL_INT(entries->revents);
271             if (self->flags & FLAG_ONESHOT) {
272                 entries->events = 0;
273             }
274             return MP_OBJ_FROM_PTR(t);
275         }
276     }
277 
278     assert(!"inconsistent number of poll active entries");
279     self->iter_cnt = 0;
280     return MP_OBJ_STOP_ITERATION;
281 }
282 
283 #if DEBUG
poll_dump(mp_obj_t self_in)284 STATIC mp_obj_t poll_dump(mp_obj_t self_in) {
285     mp_obj_poll_t *self = MP_OBJ_TO_PTR(self_in);
286 
287     struct pollfd *entries = self->entries;
288     for (int i = self->len - 1; i >= 0; i--) {
289         printf("fd: %d ev: %x rev: %x", entries->fd, entries->events, entries->revents);
290         if (self->obj_map) {
291             printf(" obj: %p", self->obj_map[entries - self->entries]);
292         }
293         printf("\n");
294         entries++;
295     }
296 
297     return mp_const_none;
298 }
299 MP_DEFINE_CONST_FUN_OBJ_1(poll_dump_obj, poll_dump);
300 #endif
301 
302 STATIC const mp_rom_map_elem_t poll_locals_dict_table[] = {
303     { MP_ROM_QSTR(MP_QSTR_register), MP_ROM_PTR(&poll_register_obj) },
304     { MP_ROM_QSTR(MP_QSTR_unregister), MP_ROM_PTR(&poll_unregister_obj) },
305     { MP_ROM_QSTR(MP_QSTR_modify), MP_ROM_PTR(&poll_modify_obj) },
306     { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&poll_poll_obj) },
307     { MP_ROM_QSTR(MP_QSTR_ipoll), MP_ROM_PTR(&poll_ipoll_obj) },
308     #if DEBUG
309     { MP_ROM_QSTR(MP_QSTR_dump), MP_ROM_PTR(&poll_dump_obj) },
310     #endif
311 };
312 STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table);
313 
314 STATIC const mp_obj_type_t mp_type_poll = {
315     { &mp_type_type },
316     .name = MP_QSTR_poll,
317     .getiter = mp_identity_getiter,
318     .iternext = poll_iternext,
319     .locals_dict = (void *)&poll_locals_dict,
320 };
321 
select_poll(size_t n_args,const mp_obj_t * args)322 STATIC mp_obj_t select_poll(size_t n_args, const mp_obj_t *args) {
323     int alloc = 4;
324     if (n_args > 0) {
325         alloc = mp_obj_get_int(args[0]);
326     }
327     mp_obj_poll_t *poll = m_new_obj(mp_obj_poll_t);
328     poll->base.type = &mp_type_poll;
329     poll->entries = m_new(struct pollfd, alloc);
330     poll->alloc = alloc;
331     poll->len = 0;
332     poll->obj_map = NULL;
333     poll->iter_cnt = 0;
334     poll->ret_tuple = MP_OBJ_NULL;
335     return MP_OBJ_FROM_PTR(poll);
336 }
337 MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_poll_obj, 0, 1, select_poll);
338 
339 STATIC const mp_rom_map_elem_t mp_module_select_globals_table[] = {
340     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uselect) },
341     { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mp_select_poll_obj) },
342     { MP_ROM_QSTR(MP_QSTR_POLLIN), MP_ROM_INT(POLLIN) },
343     { MP_ROM_QSTR(MP_QSTR_POLLOUT), MP_ROM_INT(POLLOUT) },
344     { MP_ROM_QSTR(MP_QSTR_POLLERR), MP_ROM_INT(POLLERR) },
345     { MP_ROM_QSTR(MP_QSTR_POLLHUP), MP_ROM_INT(POLLHUP) },
346 };
347 
348 STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table);
349 
350 const mp_obj_module_t mp_module_uselect = {
351     .base = { &mp_type_module },
352     .globals = (mp_obj_dict_t *)&mp_module_select_globals,
353 };
354 
355 #endif // MICROPY_PY_USELECT_POSIX
356