xref: /php-src/sapi/fpm/fpm/events/poll.c (revision 819df032)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Jerome Loyet <jerome@loyet.net>                             |
14    +----------------------------------------------------------------------+
15 */
16 
17 #include "../fpm_config.h"
18 #include "../fpm_events.h"
19 #include "../fpm.h"
20 #include "../zlog.h"
21 
22 #ifdef HAVE_POLL
23 
24 #include <poll.h>
25 #include <errno.h>
26 #include <string.h>
27 
28 static int fpm_event_poll_init(int max);
29 static int fpm_event_poll_clean(void);
30 static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout);
31 static int fpm_event_poll_add(struct fpm_event_s *ev);
32 static int fpm_event_poll_remove(struct fpm_event_s *ev);
33 
34 static struct fpm_event_module_s poll_module = {
35 	.name = "poll",
36 	.support_edge_trigger = 0,
37 	.init = fpm_event_poll_init,
38 	.clean = fpm_event_poll_clean,
39 	.wait = fpm_event_poll_wait,
40 	.add = fpm_event_poll_add,
41 	.remove = fpm_event_poll_remove,
42 };
43 
44 static struct pollfd *pollfds = NULL;
45 static struct pollfd *active_pollfds = NULL;
46 static int npollfds = 0;
47 static int next_free_slot = 0;
48 #endif /* HAVE_POLL */
49 
50 /*
51  * return the module configuration
52  */
fpm_event_poll_module(void)53 struct fpm_event_module_s *fpm_event_poll_module(void)
54 {
55 #ifdef HAVE_POLL
56 	return &poll_module;
57 #else
58 	return NULL;
59 #endif /* HAVE_POLL */
60 }
61 
62 #ifdef HAVE_POLL
63 
64 /*
65  * Init the module
66  */
fpm_event_poll_init(int max)67 static int fpm_event_poll_init(int max)
68 {
69 	int i;
70 
71 	if (max < 1) {
72 		return 0;
73 	}
74 
75 	/* alloc and clear pollfds */
76 	pollfds = malloc(sizeof(struct pollfd) * max);
77 	if (!pollfds) {
78 		zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
79 		return -1;
80 	}
81 	memset(pollfds, 0, sizeof(struct pollfd) * max);
82 
83 	/* set all fd to -1 in order to ensure it's not set */
84 	for (i = 0; i < max; i++) {
85 			pollfds[i].fd = -1;
86 	}
87 
88 	/* alloc and clear active_pollfds */
89 	active_pollfds = malloc(sizeof(struct pollfd) * max);
90 	if (!active_pollfds) {
91 		free(pollfds);
92 		zlog(ZLOG_ERROR, "poll: unable to allocate %d events", max);
93 		return -1;
94 	}
95 	memset(active_pollfds, 0, sizeof(struct pollfd) * max);
96 
97 	/* save max */
98 	npollfds = max;
99 	return 0;
100 }
101 
102 /*
103  * Clean the module
104  */
fpm_event_poll_clean(void)105 static int fpm_event_poll_clean(void)
106 {
107 	/* free pollfds */
108 	if (pollfds) {
109 		free(pollfds);
110 		pollfds = NULL;
111 	}
112 
113 	/* free active_pollfds */
114 	if (active_pollfds) {
115 		free(active_pollfds);
116 		active_pollfds = NULL;
117 	}
118 
119 	npollfds = 0;
120 	return 0;
121 }
122 
123 /*
124  * wait for events or timeout
125  */
fpm_event_poll_wait(struct fpm_event_queue_s * queue,unsigned long int timeout)126 static int fpm_event_poll_wait(struct fpm_event_queue_s *queue, unsigned long int timeout) /* {{{ */
127 {
128 	int ret;
129 	struct fpm_event_queue_s *q;
130 
131 	if (npollfds > 0) {
132 		/* copy pollfds because poll() alters it */
133 		memcpy(active_pollfds, pollfds, sizeof(struct pollfd) * npollfds);
134 	}
135 
136 	/* wait for incoming event or timeout */
137 	ret = poll(active_pollfds, npollfds, timeout);
138 	if (ret == -1) {
139 
140 		/* trigger error unless signal interrupt */
141 		if (errno != EINTR) {
142 			zlog(ZLOG_WARNING, "poll() returns %d", errno);
143 			return -1;
144 		}
145 	}
146 
147 	/* events have been triggered */
148 	if (ret > 0) {
149 
150 		/* trigger POLLIN events */
151 		q = queue;
152 		while (q) {
153 			/* ensure ev->index is valid */
154 			if (q->ev && q->ev->index >= 0 && q->ev->index < npollfds && q->ev->fd == active_pollfds[q->ev->index].fd) {
155 
156 				/* has the event has been triggered ? */
157 				if (active_pollfds[q->ev->index].revents & POLLIN) {
158 
159 					/* fire the event */
160 					fpm_event_fire(q->ev);
161 
162 					/* sanity check */
163 					if (fpm_globals.parent_pid != getpid()) {
164 						return -2;
165 					}
166 				}
167 			}
168 			q = q->next; /* iterate */
169 		}
170 	}
171 
172 	return ret;
173 }
174 /* }}} */
175 
176 /*
177  * Add a FD to the fd set
178  */
fpm_event_poll_add(struct fpm_event_s * ev)179 static int fpm_event_poll_add(struct fpm_event_s *ev) /* {{{ */
180 {
181 	int i;
182 
183 	/* do we have a direct free slot */
184 	if (pollfds[next_free_slot].fd == -1) {
185 		/* register the event */
186 		pollfds[next_free_slot].fd = ev->fd;
187 		pollfds[next_free_slot].events = POLLIN;
188 
189 		/* remember the event place in the fd list and suppose next slot is free */
190 		ev->index = next_free_slot++;
191 		if (next_free_slot >= npollfds) {
192 			next_free_slot = 0;
193 		}
194 		return 0;
195 	}
196 
197 	/* let's search */
198 	for (i = 0; i < npollfds; i++) {
199 		if (pollfds[i].fd != -1) {
200 			/* not free */
201 			continue;
202 		}
203 
204 		/* register the event */
205 		pollfds[i].fd = ev->fd;
206 		pollfds[i].events = POLLIN;
207 
208 		/* remember the event place in the fd list and suppose next slot is free */
209 		ev->index = next_free_slot++;
210 		if (next_free_slot >= npollfds) {
211 			next_free_slot = 0;
212 		}
213 		return 0;
214 	}
215 
216 	zlog(ZLOG_ERROR, "poll: not enough space to add event (fd=%d)", ev->fd);
217 	return -1;
218 }
219 /* }}} */
220 
221 /*
222  * Remove a FD from the fd set
223  */
fpm_event_poll_remove(struct fpm_event_s * ev)224 static int fpm_event_poll_remove(struct fpm_event_s *ev) /* {{{ */
225 {
226 	int i;
227 
228 	/* do we have a direct access */
229 	if (ev->index >= 0 && ev->index < npollfds && pollfds[ev->index].fd == ev->fd) {
230 		/* remember this slot as free */
231 		next_free_slot = ev->index;
232 
233 		/* clear event in pollfds */
234 		pollfds[ev->index].fd = -1;
235 		pollfds[ev->index].events = 0;
236 
237 		/* mark the event as not registered */
238 		ev->index = -1;
239 
240 		return 0;
241 	}
242 
243 	/* let's search */
244 	for (i = 0; i < npollfds; i++) {
245 
246 		if (pollfds[i].fd != ev->fd) {
247 			/* not found */
248 			continue;
249 		}
250 
251 		/* remember this slot as free */
252 		next_free_slot = i;
253 
254 		/* clear event in pollfds */
255 		pollfds[i].fd = -1;
256 		pollfds[i].events = 0;
257 
258 		/* mark the event as not registered */
259 		ev->index = -1;
260 
261 		return 0;
262 	}
263 
264 	zlog(ZLOG_ERROR, "poll: unable to remove event: not found (fd=%d, index=%d)", ev->fd, ev->index);
265 	return -1;
266 }
267 /* }}} */
268 
269 #endif /* HAVE_POLL */
270