| 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 |