Skip to content

Commit 8b24aa3

Browse files
jimmodpgeorge
authored andcommitted
extmod/modselect: Handle growing the pollfds allocation correctly.
The poll_obj_t instances have their pollfd field point into this allocation. So if re-allocating results in a move, we need to update the existing poll_obj_t's. Update the test to cover this case. Fixes issue micropython#12887. This work was funded through GitHub Sponsors. Signed-off-by: Jim Mussared <jim.mussared@gmail.com>
1 parent e9bcd49 commit 8b24aa3

File tree

2 files changed

+48
-3
lines changed

2 files changed

+48
-3
lines changed

extmod/modselect.c

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
#if MICROPY_PY_SELECT_POSIX_OPTIMISATIONS
4343

44+
#include <string.h>
4445
#include <poll.h>
4546

4647
#if !((MP_STREAM_POLL_RD) == (POLLIN) && \
@@ -142,14 +143,47 @@ STATIC void poll_obj_set_revents(poll_obj_t *poll_obj, mp_uint_t revents) {
142143
}
143144
}
144145

146+
// How much (in pollfds) to grow the allocation for poll_set->pollfds by.
147+
#define POLL_SET_ALLOC_INCREMENT (4)
148+
145149
STATIC struct pollfd *poll_set_add_fd(poll_set_t *poll_set, int fd) {
146150
struct pollfd *free_slot = NULL;
147151

148152
if (poll_set->used == poll_set->max_used) {
149153
// No free slots below max_used, so expand max_used (and possibly allocate).
150154
if (poll_set->max_used >= poll_set->alloc) {
151-
poll_set->pollfds = m_renew(struct pollfd, poll_set->pollfds, poll_set->alloc, poll_set->alloc + 4);
152-
poll_set->alloc += 4;
155+
size_t new_alloc = poll_set->alloc + POLL_SET_ALLOC_INCREMENT;
156+
// Try to grow in-place.
157+
struct pollfd *new_fds = m_renew_maybe(struct pollfd, poll_set->pollfds, poll_set->alloc, new_alloc, false);
158+
if (!new_fds) {
159+
// Failed to grow in-place. Do a new allocation and copy over the pollfd values.
160+
new_fds = m_new(struct pollfd, new_alloc);
161+
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+
for (mp_uint_t i = 0; i < poll_set->map.alloc; ++i) {
166+
if (!mp_map_slot_is_filled(&poll_set->map, i)) {
167+
continue;
168+
}
169+
170+
poll_obj_t *poll_obj = MP_OBJ_TO_PTR(poll_set->map.table[i].value);
171+
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+
continue;
176+
}
177+
178+
poll_obj->pollfd = new_fds + (poll_obj->pollfd - poll_set->pollfds);
179+
}
180+
181+
// Delete the old allocation.
182+
m_del(struct pollfd, poll_set->pollfds, poll_set->alloc);
183+
}
184+
185+
poll_set->pollfds = new_fds;
186+
poll_set->alloc = new_alloc;
153187
}
154188
free_slot = &poll_set->pollfds[poll_set->max_used++];
155189
} else {

tests/extmod/select_poll_fd.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,22 @@
3434
# Poll for input, should return an empty list.
3535
print(poller.poll(0))
3636

37-
# Test registering a very large number of file descriptors.
37+
# Test registering a very large number of file descriptors (will trigger
38+
# EINVAL due to more than OPEN_MAX fds).
3839
poller = select.poll()
3940
for fd in range(6000):
4041
poller.register(fd)
4142
try:
4243
poller.poll()
44+
assert False
4345
except OSError as er:
4446
print(er.errno == errno.EINVAL)
47+
48+
# Register stdout/stderr, plus many extra ones to trigger the fd vector
49+
# resizing. Then unregister the excess ones and verify poll still works.
50+
poller = select.poll()
51+
for fd in range(1, 1000):
52+
poller.register(fd)
53+
for i in range(3, 1000):
54+
poller.unregister(i)
55+
print(sorted(poller.poll()))

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy