Skip to content

Commit 22dd6d1

Browse files
committed
poll: refactore poll logic to use more optimally fd table
1 parent c495865 commit 22dd6d1

File tree

1 file changed

+99
-129
lines changed

1 file changed

+99
-129
lines changed

main/poll/poll_backend_poll.c

Lines changed: 99 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121
#include <string.h>
2222

2323
typedef struct {
24-
struct pollfd *fds;
25-
int fds_capacity;
26-
int fds_used;
2724
php_poll_fd_table *fd_table;
25+
/* Temporary arrays allocated during poll_backend_wait() */
26+
struct pollfd *temp_fds;
27+
int temp_fds_capacity;
2828
} poll_backend_data_t;
2929

3030
static uint32_t poll_events_to_native(uint32_t events)
@@ -63,28 +63,6 @@ static uint32_t poll_events_from_native(uint32_t native)
6363
return events;
6464
}
6565

66-
/* Find pollfd slot */
67-
static struct pollfd *poll_find_pollfd_slot(poll_backend_data_t *data, int fd)
68-
{
69-
for (int i = 0; i < data->fds_capacity; i++) {
70-
if (data->fds[i].fd == fd) {
71-
return &data->fds[i];
72-
}
73-
}
74-
return NULL;
75-
}
76-
77-
/* Get empty pollfd slot */
78-
static struct pollfd *poll_get_empty_pollfd_slot(poll_backend_data_t *data)
79-
{
80-
for (int i = 0; i < data->fds_capacity; i++) {
81-
if (data->fds[i].fd == -1) {
82-
return &data->fds[i];
83-
}
84-
}
85-
return NULL;
86-
}
87-
8866
static zend_result poll_backend_init(php_poll_ctx *ctx)
8967
{
9068
poll_backend_data_t *data = php_poll_calloc(1, sizeof(poll_backend_data_t), ctx->persistent);
@@ -93,30 +71,24 @@ static zend_result poll_backend_init(php_poll_ctx *ctx)
9371
return FAILURE;
9472
}
9573

96-
/* Use hint for initial allocation if provided, otherwise start with reasonable default */
9774
int initial_capacity = ctx->max_events_hint > 0 ? ctx->max_events_hint : 64;
9875

99-
data->fds = php_poll_calloc(initial_capacity, sizeof(struct pollfd), ctx->persistent);
100-
if (!data->fds) {
76+
data->fd_table = php_poll_fd_table_init(initial_capacity, ctx->persistent);
77+
if (!data->fd_table) {
10178
pefree(data, ctx->persistent);
10279
php_poll_set_error(ctx, PHP_POLL_ERR_NOMEM);
10380
return FAILURE;
10481
}
105-
data->fds_capacity = initial_capacity;
106-
data->fds_used = 0;
10782

108-
data->fd_table = php_poll_fd_table_init(initial_capacity, ctx->persistent);
109-
if (!data->fd_table) {
110-
pefree(data->fds, ctx->persistent);
83+
/* Pre-allocate temporary pollfd array */
84+
data->temp_fds = php_poll_calloc(initial_capacity, sizeof(struct pollfd), ctx->persistent);
85+
if (!data->temp_fds) {
86+
php_poll_fd_table_cleanup(data->fd_table);
11187
pefree(data, ctx->persistent);
11288
php_poll_set_error(ctx, PHP_POLL_ERR_NOMEM);
11389
return FAILURE;
11490
}
115-
116-
/* Initialize all fds to -1 */
117-
for (int i = 0; i < initial_capacity; i++) {
118-
data->fds[i].fd = -1;
119-
}
91+
data->temp_fds_capacity = initial_capacity;
12092

12193
ctx->backend_data = data;
12294
return SUCCESS;
@@ -126,8 +98,8 @@ static void poll_backend_cleanup(php_poll_ctx *ctx)
12698
{
12799
poll_backend_data_t *data = (poll_backend_data_t *) ctx->backend_data;
128100
if (data) {
129-
pefree(data->fds, ctx->persistent);
130101
php_poll_fd_table_cleanup(data->fd_table);
102+
pefree(data->temp_fds, ctx->persistent);
131103
pefree(data, ctx->persistent);
132104
ctx->backend_data = NULL;
133105
}
@@ -151,59 +123,22 @@ static zend_result poll_backend_add(php_poll_ctx *ctx, int fd, uint32_t events,
151123
entry->events = events;
152124
entry->data = user_data;
153125

154-
/* Find empty pollfd slot */
155-
struct pollfd *pfd = poll_get_empty_pollfd_slot(backend_data);
156-
if (!pfd) {
157-
/* Need to grow the pollfd array */
158-
int new_capacity = backend_data->fds_capacity * 2;
159-
struct pollfd *new_fds = php_poll_realloc(
160-
backend_data->fds, new_capacity * sizeof(struct pollfd), ctx->persistent);
161-
if (!new_fds) {
162-
php_poll_fd_table_remove(backend_data->fd_table, fd);
163-
php_poll_set_error(ctx, PHP_POLL_ERR_NOMEM);
164-
return FAILURE;
165-
}
166-
167-
/* Initialize new slots */
168-
for (int i = backend_data->fds_capacity; i < new_capacity; i++) {
169-
new_fds[i].fd = -1;
170-
}
171-
172-
backend_data->fds = new_fds;
173-
pfd = &backend_data->fds[backend_data->fds_capacity];
174-
backend_data->fds_capacity = new_capacity;
175-
}
176-
177-
/* Set up pollfd */
178-
pfd->fd = fd;
179-
pfd->events = poll_events_to_native(events & ~(PHP_POLL_ET | PHP_POLL_ONESHOT));
180-
pfd->revents = 0;
181-
backend_data->fds_used++;
182-
183126
return SUCCESS;
184127
}
185128

186129
static zend_result poll_backend_modify(php_poll_ctx *ctx, int fd, uint32_t events, void *user_data)
187130
{
188131
poll_backend_data_t *backend_data = (poll_backend_data_t *) ctx->backend_data;
189132

190-
/* Find existing entry using helper - O(1) instead of O(n) */
191133
php_poll_fd_entry *entry = php_poll_fd_table_find(backend_data->fd_table, fd);
192134
if (!entry) {
193135
php_poll_set_error(ctx, PHP_POLL_ERR_NOTFOUND);
194136
return FAILURE;
195137
}
196138

197-
/* Update entry */
198139
entry->events = events;
199140
entry->data = user_data;
200-
entry->last_revents = 0; /* Reset on modify */
201-
202-
/* Find pollfd and update */
203-
struct pollfd *pfd = poll_find_pollfd_slot(backend_data, fd);
204-
if (pfd) {
205-
pfd->events = poll_events_to_native(events & ~(PHP_POLL_ET | PHP_POLL_ONESHOT));
206-
}
141+
entry->last_revents = 0;
207142

208143
return SUCCESS;
209144
}
@@ -217,40 +152,70 @@ static zend_result poll_backend_remove(php_poll_ctx *ctx, int fd)
217152
return FAILURE;
218153
}
219154

220-
/* Find and clear pollfd */
221-
struct pollfd *pfd = poll_find_pollfd_slot(backend_data, fd);
222-
if (pfd) {
223-
pfd->fd = -1;
224-
pfd->events = 0;
225-
pfd->revents = 0;
226-
backend_data->fds_used--;
227-
}
228-
229155
php_poll_fd_table_remove(backend_data->fd_table, fd);
230-
231156
return SUCCESS;
232157
}
233158

234-
/* Handle oneshot removal after event */
235-
static void poll_handle_oneshot_removal(poll_backend_data_t *backend_data, int fd)
159+
/* Context for building pollfd array */
160+
typedef struct {
161+
struct pollfd *fds;
162+
int index;
163+
} poll_build_context;
164+
165+
/* Callback to build pollfd array from fd_table */
166+
static bool poll_build_fds_callback(int fd, php_poll_fd_entry *entry, void *user_data)
236167
{
237-
/* Mark pollfd as disabled */
238-
struct pollfd *pfd = poll_find_pollfd_slot(backend_data, fd);
239-
if (pfd) {
240-
pfd->fd = -1;
241-
pfd->events = 0;
242-
pfd->revents = 0;
243-
backend_data->fds_used--;
168+
poll_build_context *ctx = (poll_build_context *) user_data;
169+
170+
ctx->fds[ctx->index].fd = fd;
171+
ctx->fds[ctx->index].events
172+
= poll_events_to_native(entry->events & ~(PHP_POLL_ET | PHP_POLL_ONESHOT));
173+
ctx->fds[ctx->index].revents = 0;
174+
ctx->index++;
175+
176+
return true;
177+
}
178+
179+
/* Context for processing poll results */
180+
typedef struct {
181+
poll_backend_data_t *backend_data;
182+
struct pollfd *pollfds;
183+
php_poll_event *events;
184+
int max_events;
185+
int event_count;
186+
} poll_result_context;
187+
188+
/* Callback to process poll results */
189+
static bool poll_process_results_callback(int fd, php_poll_fd_entry *entry, void *user_data)
190+
{
191+
poll_result_context *ctx = (poll_result_context *) user_data;
192+
193+
if (ctx->event_count >= ctx->max_events) {
194+
return false; /* Stop if events array is full */
244195
}
245-
php_poll_fd_table_remove(backend_data->fd_table, fd);
196+
197+
/* Find the corresponding pollfd entry */
198+
for (int i = 0; i < php_poll_fd_table_count(ctx->backend_data->fd_table); i++) {
199+
if (ctx->pollfds[i].fd == fd && ctx->pollfds[i].revents != 0) {
200+
ctx->events[ctx->event_count].fd = fd;
201+
ctx->events[ctx->event_count].events = entry->events;
202+
ctx->events[ctx->event_count].revents
203+
= poll_events_from_native(ctx->pollfds[i].revents);
204+
ctx->events[ctx->event_count].data = entry->data;
205+
ctx->event_count++;
206+
break;
207+
}
208+
}
209+
210+
return true;
246211
}
247212

248213
static int poll_backend_wait(php_poll_ctx *ctx, php_poll_event *events, int max_events, int timeout)
249214
{
250215
poll_backend_data_t *backend_data = (poll_backend_data_t *) ctx->backend_data;
251216

252-
if (backend_data->fds_used == 0) {
253-
/* No FDs to monitor, but respect timeout */
217+
int fd_count = php_poll_fd_table_count(backend_data->fd_table);
218+
if (fd_count == 0) {
254219
if (timeout > 0) {
255220
struct timespec ts;
256221
ts.tv_sec = timeout / 1000;
@@ -260,35 +225,45 @@ static int poll_backend_wait(php_poll_ctx *ctx, php_poll_event *events, int max_
260225
return 0;
261226
}
262227

263-
int nfds = poll(backend_data->fds, backend_data->fds_capacity, timeout);
264-
265-
if (nfds > 0) {
266-
int event_count = 0;
267-
for (int i = 0; i < backend_data->fds_capacity && event_count < max_events; i++) {
268-
if (backend_data->fds[i].fd != -1 && backend_data->fds[i].revents != 0) {
269-
php_poll_fd_entry *entry
270-
= php_poll_fd_table_find(backend_data->fd_table, backend_data->fds[i].fd);
271-
if (entry) {
272-
events[event_count].fd = backend_data->fds[i].fd;
273-
events[event_count].events = entry->events;
274-
events[event_count].revents
275-
= poll_events_from_native(backend_data->fds[i].revents);
276-
events[event_count].data = entry->data;
277-
event_count++;
278-
}
279-
280-
backend_data->fds[i].revents = 0; /* Clear for next poll */
281-
}
228+
/* Ensure temp_fds array is large enough */
229+
if (fd_count > backend_data->temp_fds_capacity) {
230+
struct pollfd *new_fds = php_poll_realloc(
231+
backend_data->temp_fds, fd_count * sizeof(struct pollfd), ctx->persistent);
232+
if (!new_fds) {
233+
php_poll_set_error(ctx, PHP_POLL_ERR_NOMEM);
234+
return -1;
282235
}
236+
backend_data->temp_fds = new_fds;
237+
backend_data->temp_fds_capacity = fd_count;
238+
}
239+
240+
/* Build pollfd array from fd_table */
241+
poll_build_context build_ctx = { .fds = backend_data->temp_fds, .index = 0 };
242+
php_poll_fd_table_foreach(backend_data->fd_table, poll_build_fds_callback, &build_ctx);
283243

284-
/* Apply edge-trigger simulation using helper */
244+
/* Call poll() */
245+
int nfds = poll(backend_data->temp_fds, fd_count, timeout);
246+
247+
if (nfds > 0) {
248+
/* Process results */
249+
poll_result_context result_ctx = { .backend_data = backend_data,
250+
.pollfds = backend_data->temp_fds,
251+
.events = events,
252+
.max_events = max_events,
253+
.event_count = 0 };
254+
255+
php_poll_fd_table_foreach(
256+
backend_data->fd_table, poll_process_results_callback, &result_ctx);
257+
int event_count = result_ctx.event_count;
258+
259+
/* Apply edge-trigger simulation */
285260
nfds = php_poll_simulate_edge_trigger(backend_data->fd_table, events, event_count);
286261

287-
/* Handle oneshot removals after simulation */
262+
/* Handle oneshot removals */
288263
for (int i = 0; i < nfds; i++) {
289264
php_poll_fd_entry *entry = php_poll_fd_table_find(backend_data->fd_table, events[i].fd);
290265
if (entry && (entry->events & PHP_POLL_ONESHOT) && events[i].revents != 0) {
291-
poll_handle_oneshot_removal(backend_data, events[i].fd);
266+
php_poll_fd_table_remove(backend_data->fd_table, events[i].fd);
292267
}
293268
}
294269
}
@@ -298,7 +273,7 @@ static int poll_backend_wait(php_poll_ctx *ctx, php_poll_event *events, int max_
298273

299274
static bool poll_backend_is_available(void)
300275
{
301-
return true; /* poll() is always available */
276+
return true;
302277
}
303278

304279
static int poll_backend_get_suitable_max_events(php_poll_ctx *ctx)
@@ -310,12 +285,7 @@ static int poll_backend_get_suitable_max_events(php_poll_ctx *ctx)
310285
}
311286

312287
int active_fds = php_poll_fd_table_count(backend_data->fd_table);
313-
314-
if (active_fds == 0) {
315-
return 1;
316-
}
317-
318-
return active_fds;
288+
return active_fds == 0 ? 1 : active_fds;
319289
}
320290

321291
const php_poll_backend_ops php_poll_backend_poll_ops = {
@@ -329,7 +299,7 @@ const php_poll_backend_ops php_poll_backend_poll_ops = {
329299
.wait = poll_backend_wait,
330300
.is_available = poll_backend_is_available,
331301
.get_suitable_max_events = poll_backend_get_suitable_max_events,
332-
.supports_et = false, /* poll() doesn't support ET natively, but we simulate it */
302+
.supports_et = false,
333303
};
334304

335305
#endif /* HAVE_POLL */

0 commit comments

Comments
 (0)