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
51extern 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
59typedef 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
72STATIC 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])
85STATIC 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}
134MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_register_obj, 2, 3, poll_register);
135
136/// \method unregister(obj)
137STATIC 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}
155MP_DEFINE_CONST_FUN_OBJ_2(poll_unregister_obj, poll_unregister);
156
157/// \method modify(obj, eventmask)
158STATIC 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}
173MP_DEFINE_CONST_FUN_OBJ_3(poll_modify_obj, poll_modify);
174
175STATIC 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.
202STATIC 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}
233MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_poll_obj, 1, 3, poll_poll);
234
235STATIC 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}
248MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(poll_ipoll_obj, 1, 3, poll_ipoll);
249
250STATIC 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
284STATIC 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}
299MP_DEFINE_CONST_FUN_OBJ_1(poll_dump_obj, poll_dump);
300#endif
301
302STATIC 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};
312STATIC MP_DEFINE_CONST_DICT(poll_locals_dict, poll_locals_dict_table);
313
314STATIC 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
322STATIC 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}
337MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_select_poll_obj, 0, 1, select_poll);
338
339STATIC 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
348STATIC MP_DEFINE_CONST_DICT(mp_module_select_globals, mp_module_select_globals_table);
349
350const 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