8000 extmod/modselect: Properly track number of poll objects that are fd's. · micropython/micropython@f6af484 · GitHub
[go: up one dir, main page]

Skip to content

Commit f6af484

Browse files
committed
extmod/modselect: Properly track number of poll objects that are fd's.
Signed-off-by: Damien George <damien@micropython.org>
1 parent 24a6e95 commit f6af484

File tree

3 files changed

+49
-16
lines changed

3 files changed

+49
-16
lines changed

extmod/modselect.c

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ typedef struct _poll_set_t {
8888

8989
#if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
9090
// Array of pollfd entries for objects that have a file descriptor.
91-
unsigned short alloc;
92-
unsigned short len;
91+
unsigned short alloc; // memory allocated for pollfds
92+
unsigned short max_used; // maximum number of used entries in pollfds
93+
unsigned short used; // actual number of used entries in pollfds
9394
struct pollfd *pollfds;
9495
#endif
9596
} poll_set_t;
@@ -98,7 +99,8 @@ STATIC void poll_set_init(poll_set_t *poll_set, size_t n) {
9899
mp_map_init(&poll_set->map, n);
99100
#if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
100101
poll_set->alloc = 0;
101-
poll_set->len = 0;
102+
poll_set->max_used = 0;
103+
poll_set->used = 0;
102104
poll_set->pollfds = NULL;
103105
#endif
104106
}
@@ -142,29 +144,34 @@ STATIC void poll_obj_set_revents(poll_obj_t *poll_obj, mp_uint_t revents) {
142144

143145
STATIC struct pollfd *poll_set_add_fd(poll_set_t *poll_set, int fd) {
144146
struct pollfd *free_slot = NULL;
145-
for (unsigned int i = 0; i < poll_set->len; ++i) {
146-
struct pollfd *slot = &poll_set->pollfds[i];
147-
if (slot->fd == -1) {
148-
free_slot = slot;
149-
break;
150-
}
151-
}
152147

153-
if (free_slot == NULL) {
154-
if (poll_set->len >= poll_set->alloc) {
148+
if (poll_set->used == poll_set->max_used) {
149+
// No free slots below max_used, so expand max_used (and possibly allocate).
150+
if (poll_set->max_used >= poll_set->alloc) {
155151
poll_set->pollfds = m_renew(struct pollfd, poll_set->pollfds, poll_set->alloc, poll_set->alloc + 4);
156152
poll_set->alloc += 4;
157153
}
158-
free_slot = &poll_set->pollfds[poll_set->len++];
154+
free_slot = &poll_set->pollfds[poll_set->max_used++];
155+
} else {
156+
// There should be a free slot below max_used.
157+
for (unsigned int i = 0; i < poll_set->max_used; ++i) {
158+
struct pollfd *slot = &poll_set->pollfds[i];
159+
if (slot->fd == -1) {
160+
free_slot = slot;
161+
break;
162+
}
163+
}
164+
assert(free_slot != NULL);
159165
}
160166

161167
free_slot->fd = fd;
168+
++poll_set->used;
162169

163170
return free_slot;
164171
}
165172

166173
static inline bool poll_set_all_are_fds(poll_set_t *poll_set) {
167-
return poll_set->map.used == poll_set->len;
174+
return poll_set->map.used == poll_set->used;
168175
}
169176

170177
#else
@@ -323,7 +330,7 @@ STATIC mp_uint_t poll_set_poll_until_ready_or_timeout(poll_set_t *poll_set, size
323330
}
324331

325332
// Call system poll for those objects that have a file descriptor.
326-
int n_ready = poll(poll_set->pollfds, poll_set->len, t);
333+
int n_ready = poll(poll_set->pollfds, poll_set->max_used, t);
327334

328335
MP_THREAD_GIL_ENTER();
329336

@@ -462,6 +469,7 @@ STATIC mp_obj_t poll_unregister(mp_obj_t self_in, mp_obj_t obj_in) {
462469
poll_obj_t *poll_obj = (poll_obj_t *)MP_OBJ_TO_PTR(elem->value);
463470
if (poll_obj->pollfd != NULL) {
464471
poll_obj->pollfd->fd = -1;
472+
--self->poll_set.used;
465473
}
466474
elem->value = MP_OBJ_NULL;
467475
}

tests/extmod/select_poll_custom.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,26 @@ def ioctl(self, cmd, arg):
7777
except OSError as er:
7878
print("OSError", er.errno)
7979

80+
# Register then unregister a socket (a native stream), then test
81+
# that the Python object is still pollable.
82+
s2 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
83+
x.poll_state = _MP_STREAM_POLL_RD
84+
poller.register(s2)
85+
poller.unregister(s2)
86+
print_poll_output(poller.poll())
87+
88+
# Test registering and unregistering multiple times.
89+
for _ in range(2):
90+
poller.unregister(s)
91+
poller.unregister(x)
92+
poller.register(s2)
93+
poller.register(s, select.POLLIN)
94+
poller.register(x, select.POLLIN)
95+
poller.unregister(s2)
96+
print_poll_output(poller.poll())
97+
98+
# Clean up.
8099
poller.unregister(x)
81100
poller.unregister(s)
82-
101+
s2.close()
83102
s.close()

tests/extmod/select_poll_custom.py.exp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,9 @@ CustomPollable.ioctl 3 1
99
[(<class 'CustomPollable'>, 1)]
1010
CustomPollable.ioctl 3 1
1111
OSError 1000
12+
CustomPollable.ioctl 3 1
13+
[(<class 'CustomPollable'>, 1)]
14+
CustomPollable.ioctl 3 1
15+
[(<class 'CustomPollable'>, 1)]
16+
CustomPollable.ioctl 3 1
17+
[(<class 'CustomPollable'>, 1)]

0 commit comments

Comments
 (0)
0