Skip to content

Commit 1fb2bf2

Browse files
committed
poll: change eventport logic to not pre-associate
1 parent 1bf473e commit 1fb2bf2

File tree

1 file changed

+88
-130
lines changed

1 file changed

+88
-130
lines changed

main/poll/poll_backend_eventport.c

Lines changed: 88 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,18 @@
2121
#include <stdlib.h>
2222
#include <string.h>
2323
#include <errno.h>
24-
#include <poll.h>
2524

2625
typedef struct {
2726
int port_fd;
2827
port_event_t *events;
2928
int events_capacity;
30-
int active_associations;
3129
php_poll_fd_table *fd_table;
3230
} eventport_backend_data_t;
3331

32+
/* We use last_revents field to track if fd needs (re)association */
33+
#define EVENTPORT_NEEDS_ASSOC 1
34+
#define EVENTPORT_IS_ASSOCIATED 0
35+
3436
/* Convert our event flags to event port flags */
3537
static int eventport_events_to_native(uint32_t events)
3638
{
@@ -93,8 +95,6 @@ static zend_result eventport_backend_init(php_poll_ctx *ctx)
9395
return FAILURE;
9496
}
9597

96-
data->active_associations = 0;
97-
9898
/* Use hint for initial allocation if provided, otherwise start with reasonable default */
9999
int initial_capacity = ctx->max_events_hint > 0 ? ctx->max_events_hint : 64;
100100
data->events = php_poll_calloc(initial_capacity, sizeof(port_event_t), ctx->persistent);
@@ -135,7 +135,7 @@ static void eventport_backend_cleanup(php_poll_ctx *ctx)
135135
}
136136
}
137137

138-
/* Add file descriptor to event port */
138+
/* Add file descriptor to event port - just store in table */
139139
static zend_result eventport_backend_add(
140140
php_poll_ctx *ctx, int fd, uint32_t events, void *user_data)
141141
{
@@ -154,35 +154,12 @@ static zend_result eventport_backend_add(
154154

155155
entry->events = events;
156156
entry->data = user_data;
157+
entry->last_revents = EVENTPORT_NEEDS_ASSOC; /* Mark as needing association */
157158

158-
int native_events = eventport_events_to_native(events);
159-
160-
/* Associate file descriptor with event port */
161-
if (port_associate(backend_data->port_fd, PORT_SOURCE_FD, fd, native_events, user_data) == -1) {
162-
php_poll_fd_table_remove(backend_data->fd_table, fd);
163-
switch (errno) {
164-
case EEXIST:
165-
php_poll_set_error(ctx, PHP_POLL_ERR_EXISTS);
166-
break;
167-
case ENOMEM:
168-
php_poll_set_error(ctx, PHP_POLL_ERR_NOMEM);
169-
break;
170-
case EBADF:
171-
case EINVAL:
172-
php_poll_set_error(ctx, PHP_POLL_ERR_INVALID);
173-
break;
174-
default:
175-
php_poll_set_error(ctx, PHP_POLL_ERR_SYSTEM);
176-
break;
177-
}
178-
return FAILURE;
179-
}
180-
181-
backend_data->active_associations++;
182159
return SUCCESS;
183160
}
184161

185-
/* Modify file descriptor in event port */
162+
/* Modify file descriptor in event port - just update table */
186163
static zend_result eventport_backend_modify(
187164
php_poll_ctx *ctx, int fd, uint32_t events, void *user_data)
188165
{
@@ -198,27 +175,13 @@ static zend_result eventport_backend_modify(
198175
entry->events = events;
199176
entry->data = user_data;
200177

201-
/* For event ports, we need to dissociate and re-associate */
202-
/* Note: dissociate might fail if the fd was already fired and auto-dissociated */
203-
port_dissociate(backend_data->port_fd, PORT_SOURCE_FD, fd);
204-
205-
int native_events = eventport_events_to_native(events);
206-
if (port_associate(backend_data->port_fd, PORT_SOURCE_FD, fd, native_events, user_data) == -1) {
207-
switch (errno) {
208-
case ENOMEM:
209-
php_poll_set_error(ctx, PHP_POLL_ERR_NOMEM);
210-
break;
211-
case EBADF:
212-
case EINVAL:
213-
php_poll_set_error(ctx, PHP_POLL_ERR_INVALID);
214-
break;
215-
default:
216-
php_poll_set_error(ctx, PHP_POLL_ERR_SYSTEM);
217-
break;
218-
}
219-
return FAILURE;
178+
/* If currently associated, dissociate so we can re-associate with new events */
179+
if (entry->last_revents == EVENTPORT_IS_ASSOCIATED) {
180+
port_dissociate(backend_data->port_fd, PORT_SOURCE_FD, fd);
220181
}
221182

183+
entry->last_revents = EVENTPORT_NEEDS_ASSOC; /* Mark as needing re-association */
184+
222185
return SUCCESS;
223186
}
224187

@@ -227,33 +190,79 @@ static zend_result eventport_backend_remove(php_poll_ctx *ctx, int fd)
227190
{
228191
eventport_backend_data_t *backend_data = (eventport_backend_data_t *) ctx->backend_data;
229192

230-
/* Check if exists using helper */
231-
if (!php_poll_fd_table_find(backend_data->fd_table, fd)) {
193+
php_poll_fd_entry *entry = php_poll_fd_table_find(backend_data->fd_table, fd);
194+
if (!entry) {
232195
php_poll_set_error(ctx, PHP_POLL_ERR_NOTFOUND);
233196
return FAILURE;
234197
}
235198

236-
if (port_dissociate(backend_data->port_fd, PORT_SOURCE_FD, fd) == -1) {
237-
/* Only fail if it's not ENOENT (might already be dissociated) */
238-
if (!php_poll_is_not_found_error()) {
239-
php_poll_set_current_errno_error(ctx);
240-
return FAILURE;
199+
/* Only dissociate if it was actually associated */
200+
if (entry->last_revents == EVENTPORT_IS_ASSOCIATED) {
201+
if (port_dissociate(backend_data->port_fd, PORT_SOURCE_FD, fd) == -1) {
202+
/* Only fail if it's not ENOENT (might already be auto-dissociated) */
203+
if (!php_poll_is_not_found_error()) {
204+
php_poll_set_current_errno_error(ctx);
205+
return FAILURE;
206+
}
241207
}
242208
}
243209

244210
php_poll_fd_table_remove(backend_data->fd_table, fd);
245-
backend_data->active_associations--;
246211
return SUCCESS;
247212
}
248213

214+
/* Callback context for associating fds */
215+
typedef struct {
216+
eventport_backend_data_t *backend_data;
217+
php_poll_ctx *ctx;
218+
bool has_error;
219+
} eventport_associate_ctx;
220+
221+
/* Callback to associate fds that need association */
222+
static bool eventport_associate_callback(int fd, php_poll_fd_entry *entry, void *user_data)
223+
{
224+
eventport_associate_ctx *assoc_ctx = (eventport_associate_ctx *) user_data;
225+
226+
/* Only associate if marked as needing association */
227+
if (entry->last_revents == EVENTPORT_NEEDS_ASSOC) {
228+
int native_events = eventport_events_to_native(entry->events);
229+
230+
if (port_associate(assoc_ctx->backend_data->port_fd, PORT_SOURCE_FD, fd, native_events,
231+
entry->data)
232+
== -1) {
233+
/* Association failed - could set error here if needed */
234+
switch (errno) {
235+
case ENOMEM:
236+
php_poll_set_error(assoc_ctx->ctx, PHP_POLL_ERR_NOMEM);
237+
break;
238+
case EBADF:
239+
case EINVAL:
240+
php_poll_set_error(assoc_ctx->ctx, PHP_POLL_ERR_INVALID);
241+
break;
242+
default:
243+
php_poll_set_error(assoc_ctx->ctx, PHP_POLL_ERR_SYSTEM);
244+
break;
245+
}
246+
assoc_ctx->has_error = true;
247+
return false; /* Stop iteration */
248+
}
249+
250+
/* Mark as associated */
251+
entry->last_revents = EVENTPORT_IS_ASSOCIATED;
252+
}
253+
254+
return true; /* Continue iteration */
255+
}
256+
249257
/* Wait for events using event port */
250258
static int eventport_backend_wait(
251259
php_poll_ctx *ctx, php_poll_event *events, int max_events, int timeout)
252260
{
253261
eventport_backend_data_t *backend_data = (eventport_backend_data_t *) ctx->backend_data;
254262

255-
if (backend_data->active_associations == 0) {
256-
/* No active associations, but we still need to respect timeout */
263+
int fd_count = php_poll_fd_table_count(backend_data->fd_table);
264+
if (fd_count == 0) {
265+
/* No fds to monitor, but we still need to respect timeout */
257266
if (timeout > 0) {
258267
struct timespec ts;
259268
ts.tv_sec = timeout / 1000;
@@ -263,6 +272,16 @@ static int eventport_backend_wait(
263272
return 0;
264273
}
265274

275+
/* First: associate all fds that need association */
276+
eventport_associate_ctx assoc_ctx
277+
= { .backend_data = backend_data, .ctx = ctx, .has_error = false };
278+
279+
php_poll_fd_table_foreach(backend_data->fd_table, eventport_associate_callback, &assoc_ctx);
280+
281+
if (assoc_ctx.has_error) {
282+
return -1;
283+
}
284+
266285
/* Ensure we have enough space for the requested events */
267286
if (max_events > backend_data->events_capacity) {
268287
port_event_t *new_events = php_poll_realloc(
@@ -296,49 +315,29 @@ static int eventport_backend_wait(
296315
}
297316

298317
int nfds = (int) nget;
299-
int check_count = 0;
300318

301-
/* First pass: process events, identify unfired events, and re-associate */
319+
/* Process events */
302320
for (int i = 0; i < nfds; i++) {
303321
port_event_t *port_event = &backend_data->events[i];
304322

305323
/* Only handle PORT_SOURCE_FD events */
306324
if (port_event->portev_source == PORT_SOURCE_FD) {
307325
int fd = (int) port_event->portev_object;
308-
uint32_t fired = eventport_events_from_native(port_event->portev_events);
309326

310327
events[i].fd = fd;
311328
events[i].events = 0;
312-
events[i].revents = fired;
329+
events[i].revents = eventport_events_from_native(port_event->portev_events);
313330
events[i].data = port_event->portev_user;
314331

315-
/* Get entry and handle re-association */
332+
/* After event fires, the association is automatically removed by event ports */
316333
php_poll_fd_entry *entry = php_poll_fd_table_find(backend_data->fd_table, fd);
317334
if (entry) {
318-
/* Check if there are other events we're monitoring */
319-
uint32_t monitored = entry->events & (PHP_POLL_READ | PHP_POLL_WRITE);
320-
uint32_t unfired = monitored & ~fired;
321-
322-
if (unfired) {
323-
/* Store unfired events for potential second-round check */
324-
events[i].events = unfired;
325-
check_count++;
326-
}
327-
328335
if (entry->events & PHP_POLL_ONESHOT) {
329-
/* Oneshot: remove from tracking */
336+
/* Oneshot: remove from tracking completely */
330337
php_poll_fd_table_remove(backend_data->fd_table, fd);
331-
backend_data->active_associations--;
332338
} else {
333-
/* Re-associate immediately with all originally registered events */
334-
int native_events = eventport_events_to_native(entry->events);
335-
if (port_associate(backend_data->port_fd, PORT_SOURCE_FD, fd, native_events,
336-
entry->data)
337-
!= 0) {
338-
/* Re-association failed - remove from tracking */
339-
php_poll_fd_table_remove(backend_data->fd_table, fd);
340-
backend_data->active_associations--;
341-
}
339+
/* Mark for re-association on next wait() call */
340+
entry->last_revents = EVENTPORT_NEEDS_ASSOC;
342341
}
343342
}
344343
} else {
@@ -350,46 +349,6 @@ static int eventport_backend_wait(
350349
}
351350
}
352351

353-
/* Second pass: if we have unfired events, check them with poll() */
354-
if (check_count > 0) {
355-
struct pollfd *check_fds
356-
= php_poll_calloc(check_count, sizeof(struct pollfd), ctx->persistent);
357-
int *check_indices = php_poll_calloc(check_count, sizeof(int), ctx->persistent);
358-
359-
if (check_fds && check_indices) {
360-
int check_idx = 0;
361-
for (int i = 0; i < nfds; i++) {
362-
if (events[i].events != 0 && events[i].fd >= 0) {
363-
check_fds[check_idx].fd = events[i].fd;
364-
check_fds[check_idx].events = eventport_events_to_native(events[i].events);
365-
check_fds[check_idx].revents = 0;
366-
check_indices[check_idx] = i;
367-
check_idx++;
368-
events[i].events = 0; /* Clear it as it was just temporary */
369-
}
370-
}
371-
372-
/* Non-blocking poll to check if other events are ready */
373-
if (poll(check_fds, check_count, 0) > 0) {
374-
for (int j = 0; j < check_count; j++) {
375-
if (check_fds[j].revents != 0) {
376-
int evt_idx = check_indices[j];
377-
uint32_t additional = eventport_events_from_native(check_fds[j].revents);
378-
/* Add the additional ready events to revents */
379-
events[evt_idx].revents |= additional;
380-
}
381-
}
382-
}
383-
}
384-
385-
if (check_fds) {
386-
pefree(check_fds, ctx->persistent);
387-
}
388-
if (check_indices) {
389-
pefree(check_indices, ctx->persistent);
390-
}
391-
}
392-
393352
return nfds;
394353
}
395354

@@ -412,16 +371,15 @@ static int eventport_backend_get_suitable_max_events(php_poll_ctx *ctx)
412371
return -1;
413372
}
414373

415-
/* For event ports, we track exactly how many FD associations are active */
416-
int active_associations = backend_data->active_associations;
374+
int fd_count = php_poll_fd_table_count(backend_data->fd_table);
417375

418-
if (active_associations == 0) {
376+
if (fd_count == 0) {
419377
return 1;
420378
}
421379

422380
/* Event ports can return exactly one event per association,
423-
* so the suitable max_events is exactly the number of active associations */
424-
return active_associations;
381+
* so the suitable max_events is exactly the number of tracked fds */
382+
return fd_count;
425383
}
426384

427385
/* Event port backend operations structure */

0 commit comments

Comments
 (0)