Skip to content

Commit f599d65

Browse files
committed
Add internal polling API
This introduces various IO polling backend including epoll, kqueue, event ports, poll and WSAPoll. It makes the usage compatible between backends.
1 parent 13b83a4 commit f599d65

File tree

15 files changed

+2837
-91
lines changed

15 files changed

+2837
-91
lines changed

build/php.m4

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,50 @@ int main(void) {
13821382
])
13831383
])
13841384

1385+
AC_DEFUN([PHP_POLL_MECHANISMS],
1386+
[
1387+
AC_MSG_CHECKING([for polling mechanisms])
1388+
poll_mechanisms=""
1389+
1390+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
1391+
#include <sys/epoll.h>
1392+
], [
1393+
int fd = epoll_create(1);
1394+
return fd;
1395+
])], [
1396+
AC_DEFINE([HAVE_EPOLL], [1], [Define if epoll is available])
1397+
poll_mechanisms="$poll_mechanisms epoll"
1398+
1399+
AC_CHECK_FUNCS([epoll_pwait2], [], [], [#include <sys/epoll.h>])
1400+
])
1401+
1402+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
1403+
#include <sys/event.h>
1404+
#include <sys/time.h>
1405+
], [
1406+
int kq = kqueue();
1407+
return kq;
1408+
])], [
1409+
AC_DEFINE([HAVE_KQUEUE], [1], [Define if kqueue is available])
1410+
poll_mechanisms="$poll_mechanisms kqueue"
1411+
])
1412+
1413+
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
1414+
#include <port.h>
1415+
], [
1416+
int port = port_create();
1417+
return port;
1418+
])], [
1419+
AC_DEFINE([HAVE_EVENT_PORTS], [1], [Define if event ports are available])
1420+
poll_mechanisms="$poll_mechanisms eventport"
1421+
])
1422+
1423+
dnl Set poll mechanisms including poll that is always available
1424+
poll_mechanisms="$poll_mechanisms poll"
1425+
1426+
AC_MSG_RESULT([$poll_mechanisms])
1427+
])
1428+
13851429
dnl ----------------------------------------------------------------------------
13861430
dnl Library/function existence and build sanity checks.
13871431
dnl ----------------------------------------------------------------------------

configure.ac

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ AC_CHECK_HEADERS(m4_normalize([
444444
])
445445

446446
PHP_FOPENCOOKIE
447+
PHP_POLL_MECHANISMS
447448
PHP_BROKEN_GETCWD
448449
AS_VAR_IF([GCC], [yes], [PHP_BROKEN_GCC_STRLEN_OPT])
449450

@@ -1682,6 +1683,17 @@ PHP_ADD_SOURCES_X([main],
16821683
[PHP_FASTCGI_OBJS],
16831684
[no])
16841685

1686+
PHP_ADD_SOURCES([main/poll], m4_normalize([
1687+
poll_backend_epoll.c
1688+
poll_backend_eventport.c
1689+
poll_backend_kqueue.c
1690+
poll_backend_poll.c
1691+
poll_core.c
1692+
poll_fd_table.c
1693+
poll_handle.c
1694+
]),
1695+
[-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1])
1696+
16851697
PHP_ADD_SOURCES([main/streams], m4_normalize([
16861698
cast.c
16871699
filter.c

main/php_poll.h

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| Copyright © The PHP Group and Contributors. |
4+
+----------------------------------------------------------------------+
5+
| This source file is subject to the Modified BSD License that is |
6+
| bundled with this package in the file LICENSE, and is available |
7+
| through the World Wide Web at <https://www.php.net/license/>. |
8+
| |
9+
| SPDX-License-Identifier: BSD-3-Clause |
10+
+----------------------------------------------------------------------+
11+
| Authors: Jakub Zelenka <bukka@php.net> |
12+
+----------------------------------------------------------------------+
13+
*/
14+
15+
#ifndef PHP_POLL_H
16+
#define PHP_POLL_H
17+
18+
#include "php.h"
19+
#include "php_network.h"
20+
21+
#include <time.h>
22+
23+
/* ----- Public generic API ----- */
24+
25+
/* clang-format off */
26+
27+
/* Event types */
28+
#define PHP_POLL_READ 0x01
29+
#define PHP_POLL_WRITE 0x02
30+
#define PHP_POLL_ERROR 0x04
31+
#define PHP_POLL_HUP 0x08
32+
#define PHP_POLL_RDHUP 0x10
33+
#define PHP_POLL_ONESHOT 0x20
34+
#define PHP_POLL_ET 0x40 /* Edge-triggered */
35+
36+
/* Poll flags */
37+
#define PHP_POLL_FLAG_PERSISTENT 0x01
38+
#define PHP_POLL_FLAG_RAW_EVENTS 0x02
39+
40+
/* Poll backend types */
41+
typedef enum {
42+
PHP_POLL_BACKEND_AUTO = -1,
43+
PHP_POLL_BACKEND_POLL = 0,
44+
PHP_POLL_BACKEND_EPOLL,
45+
PHP_POLL_BACKEND_KQUEUE,
46+
PHP_POLL_BACKEND_EVENTPORT,
47+
PHP_POLL_BACKEND_WSAPOLL
48+
} php_poll_backend_type;
49+
50+
/* Error code constants for exception codes */
51+
#define PHP_POLL_ERROR_CODE_NONE 0
52+
#define PHP_POLL_ERROR_CODE_SYSTEM 1
53+
#define PHP_POLL_ERROR_CODE_NOMEM 2
54+
#define PHP_POLL_ERROR_CODE_INVALID 3
55+
#define PHP_POLL_ERROR_CODE_EXISTS 4
56+
#define PHP_POLL_ERROR_CODE_NOTFOUND 5
57+
#define PHP_POLL_ERROR_CODE_TIMEOUT 6
58+
#define PHP_POLL_ERROR_CODE_INTERRUPTED 7
59+
#define PHP_POLL_ERROR_CODE_PERMISSION 8
60+
#define PHP_POLL_ERROR_CODE_TOOBIG 9
61+
#define PHP_POLL_ERROR_CODE_AGAIN 10
62+
#define PHP_POLL_ERROR_CODE_NOSUPPORT 11
63+
64+
/* Error codes */
65+
typedef enum {
66+
PHP_POLL_ERR_NONE = PHP_POLL_ERROR_CODE_NONE, /* No error */
67+
PHP_POLL_ERR_SYSTEM = PHP_POLL_ERROR_CODE_SYSTEM, /* Generic system error */
68+
PHP_POLL_ERR_NOMEM = PHP_POLL_ERROR_CODE_NOMEM, /* Out of memory (ENOMEM) */
69+
PHP_POLL_ERR_INVALID = PHP_POLL_ERROR_CODE_INVALID, /* Invalid argument (EINVAL, EBADF) */
70+
PHP_POLL_ERR_EXISTS = PHP_POLL_ERROR_CODE_EXISTS, /* Already exists (EEXIST) */
71+
PHP_POLL_ERR_NOTFOUND = PHP_POLL_ERROR_CODE_NOTFOUND, /* Not found (ENOENT) */
72+
PHP_POLL_ERR_TIMEOUT = PHP_POLL_ERROR_CODE_TIMEOUT, /* Operation timed out (ETIME, ETIMEDOUT) */
73+
PHP_POLL_ERR_INTERRUPTED = PHP_POLL_ERROR_CODE_INTERRUPTED, /* Interrupted by signal (EINTR) */
74+
PHP_POLL_ERR_PERMISSION = PHP_POLL_ERROR_CODE_PERMISSION, /* Permission denied (EACCES, EPERM) */
75+
PHP_POLL_ERR_TOOBIG = PHP_POLL_ERROR_CODE_TOOBIG, /* Too many resources (EMFILE, ENFILE) */
76+
PHP_POLL_ERR_AGAIN = PHP_POLL_ERROR_CODE_AGAIN, /* Try again (EAGAIN, EWOULDBLOCK) */
77+
PHP_POLL_ERR_NOSUPPORT = PHP_POLL_ERROR_CODE_NOSUPPORT, /* Not supported (ENOSYS, EOPNOTSUPP) */
78+
} php_poll_error;
79+
80+
/* clang-format on */
81+
82+
/* Poll event structure */
83+
struct php_poll_event {
84+
int fd; /* File descriptor */
85+
uint32_t events; /* Requested events */
86+
uint32_t revents; /* Returned events */
87+
void *data; /* User data pointer */
88+
};
89+
90+
/* Forward declarations */
91+
typedef struct php_poll_ctx php_poll_ctx;
92+
typedef struct php_poll_backend_ops php_poll_backend_ops;
93+
typedef struct php_poll_event php_poll_event;
94+
95+
PHPAPI bool php_poll_is_backend_available(php_poll_backend_type backend);
96+
PHPAPI bool php_poll_backend_supports_edge_triggering(php_poll_backend_type backend);
97+
98+
PHPAPI php_poll_ctx *php_poll_create(php_poll_backend_type preferred_backend, uint32_t flags);
99+
PHPAPI php_poll_ctx *php_poll_create_by_name(const char *preferred_backend, uint32_t flags);
100+
101+
PHPAPI zend_result php_poll_set_max_events_hint(php_poll_ctx *ctx, int max_events);
102+
PHPAPI zend_result php_poll_init(php_poll_ctx *ctx);
103+
PHPAPI void php_poll_destroy(php_poll_ctx *ctx);
104+
105+
PHPAPI zend_result php_poll_add(php_poll_ctx *ctx, int fd, uint32_t events, void *data);
106+
PHPAPI zend_result php_poll_modify(php_poll_ctx *ctx, int fd, uint32_t events, void *data);
107+
PHPAPI zend_result php_poll_remove(php_poll_ctx *ctx, int fd);
108+
109+
PHPAPI int php_poll_wait(php_poll_ctx *ctx, php_poll_event *events, int max_events,
110+
const struct timespec *timeout);
111+
112+
PHPAPI const char *php_poll_backend_name(php_poll_ctx *ctx);
113+
PHPAPI php_poll_backend_type php_poll_get_backend_type(php_poll_ctx *ctx);
114+
PHPAPI bool php_poll_supports_et(php_poll_ctx *ctx);
115+
PHPAPI php_poll_error php_poll_get_error(php_poll_ctx *ctx);
116+
117+
/* Get suitable max_events for backend */
118+
PHPAPI int php_poll_get_suitable_max_events(php_poll_ctx *ctx);
119+
120+
/* Backend registration */
121+
PHPAPI void php_poll_register_backends(void);
122+
123+
/* Error string for the error */
124+
PHPAPI const char *php_poll_error_string(php_poll_error error);
125+
126+
/* ----- Public extension API ----- */
127+
128+
typedef struct php_poll_handle_ops php_poll_handle_ops;
129+
typedef struct php_poll_handle_object php_poll_handle_object;
130+
131+
/* Handle operations structure - extensions can provide their own */
132+
struct php_poll_handle_ops {
133+
/**
134+
* Get file descriptor for this handle
135+
* @param handle The handle object
136+
* @return File descriptor or SOCK_ERR if invalid/not applicable
137+
*/
138+
php_socket_t (*get_fd)(php_poll_handle_object *handle);
139+
140+
/**
141+
* Check if handle is still valid
142+
* @param handle The handle object
143+
* @return true if valid, false if invalid
144+
*/
145+
int (*is_valid)(php_poll_handle_object *handle);
146+
147+
/**
148+
* Cleanup handle-specific data
149+
* @param handle The handle object
150+
*/
151+
void (*cleanup)(php_poll_handle_object *handle);
152+
};
153+
154+
/* Base poll handle object structure */
155+
struct php_poll_handle_object {
156+
php_poll_handle_ops *ops;
157+
void *handle_data;
158+
zend_object std;
159+
};
160+
161+
#define PHP_POLL_HANDLE_OBJ_FROM_ZOBJ(obj) \
162+
((php_poll_handle_object *) ((char *) (obj) - XtOffsetOf(php_poll_handle_object, std)))
163+
164+
#define PHP_POLL_HANDLE_OBJ_FROM_ZV(zv) PHP_POLL_HANDLE_OBJ_FROM_ZOBJ(Z_OBJ_P(zv))
165+
166+
/* Default operations */
167+
extern php_poll_handle_ops php_poll_handle_default_ops;
168+
169+
/* Utility functions for extensions */
170+
PHPAPI php_poll_handle_object *php_poll_handle_object_create(
171+
size_t obj_size, zend_class_entry *ce, php_poll_handle_ops *ops);
172+
PHPAPI void php_poll_handle_object_free(zend_object *obj);
173+
174+
/* Get file descriptor from any poll handle */
175+
PHPAPI php_socket_t php_poll_handle_get_fd(php_poll_handle_object *handle);
176+
177+
#endif /* PHP_POLL_H */

0 commit comments

Comments
 (0)