OLD | NEW |
| (Empty) |
1 /* Licensed to the Apache Software Foundation (ASF) under one or more | |
2 * contributor license agreements. See the NOTICE file distributed with | |
3 * this work for additional information regarding copyright ownership. | |
4 * The ASF licenses this file to You under the Apache License, Version 2.0 | |
5 * (the "License"); you may not use this file except in compliance with | |
6 * the License. You may obtain a copy of the License at | |
7 * | |
8 * http://www.apache.org/licenses/LICENSE-2.0 | |
9 * | |
10 * Unless required by applicable law or agreed to in writing, software | |
11 * distributed under the License is distributed on an "AS IS" BASIS, | |
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 * See the License for the specific language governing permissions and | |
14 * limitations under the License. | |
15 */ | |
16 | |
17 /* | |
18 * | |
19 * @author Mladen Turk | |
20 * @version $Id: poll.c 1667243 2015-03-17 10:28:05Z markt $ | |
21 */ | |
22 | |
23 #include "tcn.h" | |
24 | |
25 #ifdef TCN_DO_STATISTICS | |
26 static int sp_created = 0; | |
27 static int sp_destroyed = 0; | |
28 static int sp_cleared = 0; | |
29 #endif | |
30 | |
31 /* Internal poll structure for queryset | |
32 */ | |
33 typedef struct tcn_pollset { | |
34 apr_pool_t *pool; | |
35 apr_int32_t nelts; | |
36 apr_int32_t nalloc; | |
37 apr_pollset_t *pollset; | |
38 jlong *set; | |
39 apr_interval_time_t default_timeout; | |
40 /* A ring containing all of the pollfd_t that are active | |
41 */ | |
42 APR_RING_HEAD(pfd_poll_ring_t, tcn_pfde_t) poll_ring; | |
43 /* A ring of pollfd_t that have been used, and then _remove()'d | |
44 */ | |
45 APR_RING_HEAD(pfd_free_ring_t, tcn_pfde_t) free_ring; | |
46 /* A ring of pollfd_t where rings that have been _remove()`ed but | |
47 * might still be inside a _poll() | |
48 */ | |
49 APR_RING_HEAD(pfd_dead_ring_t, tcn_pfde_t) dead_ring; | |
50 #ifdef TCN_DO_STATISTICS | |
51 int sp_added; | |
52 int sp_max_count; | |
53 int sp_poll; | |
54 int sp_polled; | |
55 int sp_max_polled; | |
56 int sp_remove; | |
57 int sp_removed; | |
58 int sp_maintained; | |
59 int sp_max_maintained; | |
60 int sp_err_poll; | |
61 int sp_poll_timeout; | |
62 int sp_overflow; | |
63 int sp_equals; | |
64 int sp_eintr; | |
65 #endif | |
66 } tcn_pollset_t; | |
67 | |
68 #ifdef TCN_DO_STATISTICS | |
69 static void sp_poll_statistics(tcn_pollset_t *p) | |
70 { | |
71 fprintf(stderr, "Pollset Statistics ......\n"); | |
72 fprintf(stderr, "Number of added sockets : %d\n", p->sp_added); | |
73 fprintf(stderr, "Max. number of sockets : %d\n", p->sp_max_count); | |
74 fprintf(stderr, "Poll calls : %d\n", p->sp_poll); | |
75 fprintf(stderr, "Poll timeouts : %d\n", p->sp_poll_timeout); | |
76 fprintf(stderr, "Poll errors : %d\n", p->sp_err_poll); | |
77 fprintf(stderr, "Poll overflows : %d\n", p->sp_overflow); | |
78 fprintf(stderr, "Polled sockets : %d\n", p->sp_polled); | |
79 fprintf(stderr, "Max. Polled sockets : %d\n", p->sp_max_polled); | |
80 fprintf(stderr, "Poll remove : %d\n", p->sp_remove); | |
81 fprintf(stderr, "Total removed : %d\n", p->sp_removed); | |
82 fprintf(stderr, "Maintained : %d\n", p->sp_maintained); | |
83 fprintf(stderr, "Max. maintained : %d\n", p->sp_max_maintained); | |
84 fprintf(stderr, "Number of duplicates : %d\n", p->sp_equals); | |
85 fprintf(stderr, "Number of interrupts : %d\n", p->sp_eintr); | |
86 | |
87 } | |
88 | |
89 static apr_status_t sp_poll_cleanup(void *data) | |
90 { | |
91 sp_cleared++; | |
92 sp_poll_statistics(data); | |
93 return APR_SUCCESS; | |
94 } | |
95 | |
96 void sp_poll_dump_statistics() | |
97 { | |
98 fprintf(stderr, "Poll Statistics .........\n"); | |
99 fprintf(stderr, "Polls created : %d\n", sp_created); | |
100 fprintf(stderr, "Polls destroyed : %d\n", sp_destroyed); | |
101 fprintf(stderr, "Polls cleared : %d\n", sp_cleared); | |
102 } | |
103 #endif | |
104 | |
105 TCN_IMPLEMENT_CALL(jlong, Poll, create)(TCN_STDARGS, jint size, | |
106 jlong pool, jint flags, | |
107 jlong default_timeout) | |
108 { | |
109 apr_pool_t *p = J2P(pool, apr_pool_t *); | |
110 apr_pollset_t *pollset = NULL; | |
111 tcn_pollset_t *tps = NULL; | |
112 apr_uint32_t f = (apr_uint32_t)flags | APR_POLLSET_NOCOPY; | |
113 UNREFERENCED(o); | |
114 TCN_ASSERT(pool != 0); | |
115 | |
116 if (f & APR_POLLSET_THREADSAFE) { | |
117 apr_status_t rv = apr_pollset_create(&pollset, (apr_uint32_t)size, p, f)
; | |
118 if (rv == APR_ENOTIMPL) | |
119 f &= ~APR_POLLSET_THREADSAFE; | |
120 else if (rv != APR_SUCCESS) { | |
121 tcn_ThrowAPRException(e, rv); | |
122 goto cleanup; | |
123 } | |
124 } | |
125 if (pollset == NULL) { | |
126 TCN_THROW_IF_ERR(apr_pollset_create(&pollset, | |
127 (apr_uint32_t)size, p, f), pollset); | |
128 } | |
129 tps = apr_pcalloc(p, sizeof(tcn_pollset_t)); | |
130 TCN_CHECK_ALLOCATED(tps); | |
131 tps->pollset = pollset; | |
132 tps->set = apr_pcalloc(p, size * sizeof(jlong) * 2); | |
133 TCN_CHECK_ALLOCATED(tps->set); | |
134 APR_RING_INIT(&tps->poll_ring, tcn_pfde_t, link); | |
135 APR_RING_INIT(&tps->free_ring, tcn_pfde_t, link); | |
136 APR_RING_INIT(&tps->dead_ring, tcn_pfde_t, link); | |
137 | |
138 tps->nelts = 0; | |
139 tps->nalloc = size; | |
140 tps->pool = p; | |
141 tps->default_timeout = J2T(default_timeout); | |
142 #ifdef TCN_DO_STATISTICS | |
143 sp_created++; | |
144 apr_pool_cleanup_register(p, (const void *)tps, | |
145 sp_poll_cleanup, | |
146 apr_pool_cleanup_null); | |
147 #endif | |
148 cleanup: | |
149 return P2J(tps); | |
150 } | |
151 | |
152 TCN_IMPLEMENT_CALL(jint, Poll, destroy)(TCN_STDARGS, jlong pollset) | |
153 { | |
154 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
155 | |
156 UNREFERENCED_STDARGS; | |
157 TCN_ASSERT(pollset != 0); | |
158 #ifdef TCN_DO_STATISTICS | |
159 sp_destroyed++; | |
160 apr_pool_cleanup_kill(p->pool, p, sp_poll_cleanup); | |
161 sp_poll_statistics(p); | |
162 #endif | |
163 return (jint)apr_pollset_destroy(p->pollset); | |
164 } | |
165 | |
166 static apr_status_t do_add(tcn_pollset_t *p, tcn_socket_t *s, | |
167 apr_int16_t reqevents, | |
168 apr_interval_time_t socket_timeout) | |
169 { | |
170 | |
171 apr_status_t rv; | |
172 apr_interval_time_t timeout = socket_timeout; | |
173 tcn_pfde_t *elem = NULL; | |
174 | |
175 if (p->nelts == p->nalloc) { | |
176 #ifdef TCN_DO_STATISTICS | |
177 p->sp_overflow++; | |
178 #endif | |
179 return APR_ENOMEM; | |
180 } | |
181 if (s->pe != NULL) { | |
182 /* Socket is already added to the pollset. | |
183 */ | |
184 #ifdef TCN_DO_STATISTICS | |
185 p->sp_equals++; | |
186 #endif | |
187 return APR_EEXIST; | |
188 } | |
189 if (timeout == TCN_NO_SOCKET_TIMEOUT) { | |
190 timeout = p->default_timeout; | |
191 } | |
192 if (timeout > 0) | |
193 s->last_active = apr_time_now(); | |
194 else | |
195 s->last_active = 0; | |
196 s->timeout = socket_timeout; | |
197 if (!APR_RING_EMPTY(&p->free_ring, tcn_pfde_t, link)) { | |
198 elem = APR_RING_FIRST(&p->free_ring); | |
199 APR_RING_REMOVE(elem, link); | |
200 } | |
201 else { | |
202 elem = (tcn_pfde_t *)apr_palloc(p->pool, sizeof(tcn_pfde_t)); | |
203 APR_RING_ELEM_INIT(elem, link); | |
204 } | |
205 elem->fd.reqevents = reqevents; | |
206 elem->fd.desc_type = APR_POLL_SOCKET; | |
207 elem->fd.desc.s = s->sock; | |
208 elem->fd.client_data = s; | |
209 #ifdef TCN_DO_STATISTICS | |
210 p->sp_added++; | |
211 p->sp_max_count = TCN_MAX(p->sp_max_count, p->sp_added); | |
212 #endif | |
213 rv = apr_pollset_add(p->pollset, &elem->fd); | |
214 if (rv != APR_SUCCESS) { | |
215 APR_RING_INSERT_TAIL(&p->free_ring, elem, tcn_pfde_t, link); | |
216 } | |
217 else { | |
218 APR_RING_INSERT_TAIL(&p->poll_ring, elem, tcn_pfde_t, link); | |
219 s->pe = elem; | |
220 } | |
221 return rv; | |
222 } | |
223 | |
224 TCN_IMPLEMENT_CALL(jint, Poll, add)(TCN_STDARGS, jlong pollset, | |
225 jlong socket, jint reqevents) | |
226 { | |
227 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
228 tcn_socket_t *s = J2P(socket, tcn_socket_t *); | |
229 | |
230 UNREFERENCED_STDARGS; | |
231 TCN_ASSERT(socket != 0); | |
232 | |
233 return (jint) do_add(p, s, (apr_int16_t)reqevents, TCN_NO_SOCKET_TIMEOUT); | |
234 } | |
235 | |
236 TCN_IMPLEMENT_CALL(jint, Poll, addWithTimeout)(TCN_STDARGS, jlong pollset, | |
237 jlong socket, jint reqevents, | |
238 jlong socket_timeout) | |
239 { | |
240 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
241 tcn_socket_t *s = J2P(socket, tcn_socket_t *); | |
242 | |
243 UNREFERENCED_STDARGS; | |
244 TCN_ASSERT(socket != 0); | |
245 | |
246 return (jint) do_add(p, s, (apr_int16_t)reqevents, J2T(socket_timeout)); | |
247 } | |
248 | |
249 TCN_IMPLEMENT_CALL(jint, Poll, remove)(TCN_STDARGS, jlong pollset, | |
250 jlong socket) | |
251 { | |
252 apr_pollfd_t fd; | |
253 apr_status_t rv; | |
254 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
255 tcn_socket_t *s = J2P(socket, tcn_socket_t *); | |
256 | |
257 UNREFERENCED_STDARGS; | |
258 TCN_ASSERT(socket != 0); | |
259 | |
260 if (s->pe == NULL) { | |
261 /* Already removed */ | |
262 return APR_NOTFOUND; | |
263 } | |
264 fd.desc_type = APR_POLL_SOCKET; | |
265 fd.desc.s = s->sock; | |
266 fd.client_data = s; | |
267 fd.reqevents = APR_POLLIN | APR_POLLOUT; | |
268 #ifdef TCN_DO_STATISTICS | |
269 p->sp_remove++; | |
270 #endif | |
271 | |
272 rv = apr_pollset_remove(p->pollset, &fd); | |
273 APR_RING_REMOVE(s->pe, link); | |
274 APR_RING_INSERT_TAIL(&p->dead_ring, s->pe, tcn_pfde_t, link); | |
275 s->pe = NULL; | |
276 p->nelts--; | |
277 #ifdef TCN_DO_STATISTICS | |
278 p->sp_removed++; | |
279 #endif | |
280 return rv; | |
281 } | |
282 | |
283 | |
284 TCN_IMPLEMENT_CALL(jint, Poll, poll)(TCN_STDARGS, jlong pollset, | |
285 jlong timeout, jlongArray set, | |
286 jboolean remove) | |
287 { | |
288 const apr_pollfd_t *fd = NULL; | |
289 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
290 apr_int32_t i, num = 0; | |
291 apr_status_t rv = APR_SUCCESS; | |
292 apr_time_t now = 0; | |
293 apr_interval_time_t ptime = J2T(timeout); | |
294 UNREFERENCED(o); | |
295 TCN_ASSERT(pollset != 0); | |
296 | |
297 #ifdef TCN_DO_STATISTICS | |
298 p->sp_poll++; | |
299 #endif | |
300 | |
301 if (ptime > 0) { | |
302 tcn_pfde_t *ep; | |
303 | |
304 now = apr_time_now(); | |
305 /* Find the minimum timeout */ | |
306 APR_RING_FOREACH(ep, &p->poll_ring, tcn_pfde_t, link) | |
307 { | |
308 apr_interval_time_t socket_timeout = 0; | |
309 tcn_socket_t *s = (tcn_socket_t *)ep->fd.client_data; | |
310 if (s->timeout == TCN_NO_SOCKET_TIMEOUT) { | |
311 socket_timeout = p->default_timeout; | |
312 } | |
313 else { | |
314 socket_timeout = s->timeout; | |
315 } | |
316 if (socket_timeout >= 0) { | |
317 apr_interval_time_t t = now - s->last_active; | |
318 if (t >= socket_timeout) { | |
319 ptime = 0; | |
320 break; | |
321 } | |
322 else { | |
323 ptime = TCN_MIN(socket_timeout - t, ptime); | |
324 } | |
325 } | |
326 } | |
327 } | |
328 else if (ptime < 0) | |
329 ptime = 0; | |
330 for (;;) { | |
331 rv = apr_pollset_poll(p->pollset, ptime, &num, &fd); | |
332 if (rv != APR_SUCCESS) { | |
333 if (APR_STATUS_IS_EINTR(rv)) { | |
334 #ifdef TCN_DO_STATISTICS | |
335 p->sp_eintr++; | |
336 #endif | |
337 continue; | |
338 } | |
339 TCN_ERROR_WRAP(rv); | |
340 #ifdef TCN_DO_STATISTICS | |
341 if (rv == TCN_TIMEUP) | |
342 p->sp_poll_timeout++; | |
343 else | |
344 p->sp_err_poll++; | |
345 #endif | |
346 num = (apr_int32_t)(-rv); | |
347 } | |
348 break; | |
349 } | |
350 /* Shift all PFDs in the Dead Ring to the Free Ring */ | |
351 APR_RING_CONCAT(&p->free_ring, &p->dead_ring, tcn_pfde_t, link); | |
352 if (num > 0) { | |
353 #ifdef TCN_DO_STATISTICS | |
354 p->sp_polled += num; | |
355 p->sp_max_polled = TCN_MAX(p->sp_max_polled, num); | |
356 #endif | |
357 if (!remove) | |
358 now = apr_time_now(); | |
359 for (i = 0; i < num; i++) { | |
360 tcn_socket_t *s = (tcn_socket_t *)fd->client_data; | |
361 p->set[i*2+0] = (jlong)(fd->rtnevents); | |
362 p->set[i*2+1] = P2J(s); | |
363 /* If a socket is registered for multiple events and the poller has | |
364 multiple events to return it may do as a single pair in this | |
365 array or as multiple pairs depending on implementation. On OSX at | |
366 least, multiple pairs have been observed. In this case do not try | |
367 and remove socket from the pollset for a second time else a crash | |
368 will result. */ | |
369 if (remove) { | |
370 if (s->pe) { | |
371 apr_pollset_remove(p->pollset, fd); | |
372 APR_RING_REMOVE(s->pe, link); | |
373 APR_RING_INSERT_TAIL(&p->dead_ring, s->pe, tcn_pfde_t, link)
; | |
374 s->pe = NULL; | |
375 p->nelts--; | |
376 #ifdef TCN_DO_STATISTICS | |
377 p->sp_removed++; | |
378 #endif | |
379 } | |
380 } | |
381 else { | |
382 /* Update last active with the current time | |
383 * after the poll call. | |
384 */ | |
385 s->last_active = now; | |
386 } | |
387 fd ++; | |
388 } | |
389 (*e)->SetLongArrayRegion(e, set, 0, num * 2, p->set); | |
390 } | |
391 | |
392 return (jint)num; | |
393 } | |
394 | |
395 TCN_IMPLEMENT_CALL(jint, Poll, maintain)(TCN_STDARGS, jlong pollset, | |
396 jlongArray set, jboolean remove) | |
397 { | |
398 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
399 apr_int32_t i = 0, num = 0; | |
400 apr_time_t now = apr_time_now(); | |
401 tcn_pfde_t *ep, *ip; | |
402 | |
403 UNREFERENCED(o); | |
404 TCN_ASSERT(pollset != 0); | |
405 | |
406 /* Check for timeout sockets */ | |
407 APR_RING_FOREACH_SAFE(ep, ip, &p->poll_ring, tcn_pfde_t, link) | |
408 { | |
409 apr_interval_time_t timeout = 0; | |
410 tcn_socket_t *s = (tcn_socket_t *)ep->fd.client_data; | |
411 if (s->timeout == TCN_NO_SOCKET_TIMEOUT) { | |
412 timeout = p->default_timeout; | |
413 } | |
414 else { | |
415 timeout = s->timeout; | |
416 } | |
417 if (timeout == -1) { | |
418 continue; | |
419 } | |
420 if ((now - s->last_active) >= timeout) { | |
421 p->set[num++] = P2J(s); | |
422 if (remove) { | |
423 APR_RING_REMOVE(ep, link); | |
424 APR_RING_INSERT_TAIL(&p->dead_ring, ep, tcn_pfde_t, link); | |
425 s->pe = NULL; | |
426 p->nelts--; | |
427 #ifdef TCN_DO_STATISTICS | |
428 p->sp_removed++; | |
429 #endif | |
430 } | |
431 } | |
432 } | |
433 if (num) { | |
434 #ifdef TCN_DO_STATISTICS | |
435 p->sp_maintained += num; | |
436 p->sp_max_maintained = TCN_MAX(p->sp_max_maintained, num); | |
437 #endif | |
438 if (remove) { | |
439 for (i = 0; i < num; i++) { | |
440 apr_pollfd_t fd; | |
441 tcn_socket_t *s = J2P(p->set[i], tcn_socket_t *); | |
442 fd.desc_type = APR_POLL_SOCKET; | |
443 fd.desc.s = s->sock; | |
444 fd.client_data = s; | |
445 fd.reqevents = APR_POLLIN | APR_POLLOUT; | |
446 apr_pollset_remove(p->pollset, &fd); | |
447 } | |
448 } | |
449 (*e)->SetLongArrayRegion(e, set, 0, num, p->set); | |
450 } | |
451 return (jint)num; | |
452 } | |
453 | |
454 TCN_IMPLEMENT_CALL(void, Poll, setTtl)(TCN_STDARGS, jlong pollset, | |
455 jlong default_timeout) | |
456 { | |
457 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
458 UNREFERENCED_STDARGS; | |
459 p->default_timeout = J2T(default_timeout); | |
460 } | |
461 | |
462 TCN_IMPLEMENT_CALL(jlong, Poll, getTtl)(TCN_STDARGS, jlong pollset) | |
463 { | |
464 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
465 UNREFERENCED_STDARGS; | |
466 return (jlong)p->default_timeout; | |
467 } | |
468 | |
469 TCN_IMPLEMENT_CALL(jint, Poll, pollset)(TCN_STDARGS, jlong pollset, | |
470 jlongArray set) | |
471 { | |
472 tcn_pollset_t *p = J2P(pollset, tcn_pollset_t *); | |
473 apr_int32_t n = 0; | |
474 tcn_pfde_t *ep; | |
475 | |
476 UNREFERENCED(o); | |
477 TCN_ASSERT(pollset != 0); | |
478 | |
479 APR_RING_FOREACH(ep, &p->poll_ring, tcn_pfde_t, link) | |
480 { | |
481 apr_pollfd_t *fd = &ep->fd; | |
482 fd->rtnevents = APR_POLLHUP | APR_POLLIN; | |
483 p->set[n++] = (jlong)(fd->rtnevents); | |
484 p->set[n++] = P2J(fd->client_data); | |
485 } | |
486 if (n > 0) | |
487 (*e)->SetLongArrayRegion(e, set, 0, n, p->set); | |
488 return n / 2; | |
489 } | |
OLD | NEW |