Skip to content

Commit 3c05f26

Browse files
committed
Merge pull request atomvm#760 from pguyot/w33/fix-concurrency-poll-s
Fix concurrency issue with poll and select events List of select events could be modified while building or rebuilding the set of poll fd. These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license). SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
2 parents d995f51 + 3d94729 commit 3c05f26

File tree

4 files changed

+11
-10
lines changed

4 files changed

+11
-10
lines changed

src/libAtomVM/resources.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,15 +266,14 @@ static inline void select_event_destroy(struct SelectEvent *select_event, Global
266266
free((void *) select_event);
267267
}
268268

269-
void select_event_count_and_destroy_closed(size_t *read, size_t *write, size_t *either, GlobalContext *global)
269+
void select_event_count_and_destroy_closed(struct ListHead *select_events, size_t *read, size_t *write, size_t *either, GlobalContext *global)
270270
{
271271
size_t read_count = 0;
272272
size_t write_count = 0;
273273
size_t either_count = 0;
274274

275275
struct ListHead *item;
276276
struct ListHead *tmp;
277-
struct ListHead *select_events = synclist_wrlock(&global->select_events);
278277
MUTABLE_LIST_FOR_EACH (item, tmp, select_events) {
279278
struct SelectEvent *select_event = GET_LIST_ENTRY(item, struct SelectEvent, head);
280279
if (select_event->close) {
@@ -292,7 +291,6 @@ void select_event_count_and_destroy_closed(size_t *read, size_t *write, size_t *
292291
}
293292
}
294293
}
295-
synclist_unlock(&global->select_events);
296294
if (read) {
297295
*read = read_count;
298296
}

src/libAtomVM/resources.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,13 @@ bool select_event_notify(ErlNifEvent event, bool is_read, bool is_write, GlobalC
102102
* events marked for close.
103103
* @details Convenience function that can be called by `sys_poll_events` and
104104
* iterates on events to be closed and count them.
105+
* @param select_events list of events, with a write lock
105106
* @param read on output number of events with read = 1, can be NULL
106107
* @param write on output number of events with write = 1, can be NULL
107108
* @param either on output number of events with either read = 1 or write = 1, can be NULL
108109
* @param global the global context
109110
*/
110-
void select_event_count_and_destroy_closed(size_t *read, size_t *write, size_t *either, GlobalContext *global);
111+
void select_event_count_and_destroy_closed(struct ListHead *select_events, size_t *read, size_t *write, size_t *either, GlobalContext *global);
111112

112113
/**
113114
* @brief Destroy monitors associated with a resource.

src/platforms/esp32/components/avm_sys/sys.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -627,9 +627,10 @@ static void *select_thread_loop(void *arg)
627627
int fd_index;
628628
if (select_events_poll_count < 0) {
629629
// Means it is dirty and should be rebuilt.
630+
struct ListHead *select_events = synclist_wrlock(&glb->select_events);
630631
size_t select_events_new_count;
631632
if (select_events_poll_count < 0) {
632-
select_event_count_and_destroy_closed(NULL, NULL, &select_events_new_count, glb);
633+
select_event_count_and_destroy_closed(select_events, NULL, NULL, &select_events_new_count, glb);
633634
} else {
634635
select_events_new_count = select_events_poll_count;
635636
}
@@ -643,7 +644,6 @@ static void *select_thread_loop(void *arg)
643644
fd_index = poll_count;
644645

645646
struct ListHead *item;
646-
struct ListHead *select_events = synclist_rdlock(&glb->select_events);
647647
LIST_FOR_EACH (item, select_events) {
648648
struct SelectEvent *select_event = GET_LIST_ENTRY(item, struct SelectEvent, head);
649649
if (select_event->read || select_event->write) {

src/platforms/generic_unix/lib/sys.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,9 @@ static inline void sys_poll_events_with_kqueue(GlobalContext *glb, int timeout_m
117117
int select_events_poll_count = platform->select_events_poll_count;
118118
if (select_events_poll_count < 0) {
119119
size_t either;
120-
select_event_count_and_destroy_closed(NULL, NULL, &either, glb);
120+
struct ListHead *select_events = synclist_wrlock(&glb->select_events);
121+
select_event_count_and_destroy_closed(select_events, NULL, NULL, &either, glb);
122+
synclist_unlock(&glb->select_events);
121123
select_events_poll_count = either;
122124
platform->select_events_poll_count = either;
123125
}
@@ -170,9 +172,10 @@ static inline void sys_poll_events_with_poll(GlobalContext *glb, int timeout_ms)
170172
int fd_index;
171173
if (listeners_poll_count < 0 || select_events_poll_count < 0) {
172174
// Means it is dirty and should be rebuilt.
175+
struct ListHead *select_events = synclist_wrlock(&glb->select_events);
173176
size_t select_events_new_count;
174177
if (select_events_poll_count < 0) {
175-
select_event_count_and_destroy_closed(NULL, NULL, &select_events_new_count, glb);
178+
select_event_count_and_destroy_closed(select_events, NULL, NULL, &select_events_new_count, glb);
176179
} else {
177180
select_events_new_count = select_events_poll_count;
178181
}
@@ -224,8 +227,7 @@ static inline void sys_poll_events_with_poll(GlobalContext *glb, int timeout_ms)
224227
synclist_unlock(&glb->listeners);
225228
}
226229

227-
struct ListHead *select_events = synclist_rdlock(&glb->select_events);
228-
// We put listeners first
230+
// We put select events next
229231
LIST_FOR_EACH (item, select_events) {
230232
struct SelectEvent *select_event = GET_LIST_ENTRY(item, struct SelectEvent, head);
231233
if (select_event->read || select_event->write) {

0 commit comments

Comments
 (0)