(14.2) イベントチェック処理
監視対象ソケットの接続受付、データ受信、書込み許可などのイベントをチェックする。
以下の処理がイベントチェック処理apr_pollset_poll()だ。
1460 rc = apr_pollset_poll(event_pollset, timeout_interval, &num, &out_pfd);
第一引数のevent_pollsetは、apr_pollset_t型の変数で、event.cのファイル内にファイルスコープで定義されている。
256 static apr_pollset_t *event_pollset;
この構造体は、start_threads()関数で、生成される。
1883 int good_methods[] = {APR_POLLSET_KQUEUE, APR_POLLSET_PORT, APR_POLLSET_EPOLL};
:
1921 /* Create the main pollset */
1922 for (i = 0; i < sizeof(good_methods) / sizeof(void*); i++) {
1923 rv = apr_pollset_create_ex(&event_pollset,
1924 threads_per_child*2, /* XXX don't we need more, to handle
1925 * connections in K-A or lingering
1926 * close?
1927 */
1928 pchild, APR_POLLSET_THREADSAFE | APR_POLLSET_NOCOPY | APR_POLLSET_NODEFAULT,
1929 good_methods[i]);
1930 if (rv == APR_SUCCESS) {
1931 break;
1932 }
1933 }
現時点ではこの詳細は見ないが、good_methodsを順に試して、使えるものが見つかったら 1931行目でbreakしている。
CentOS 6で実行してみると、APR_POLLSET_EPOLLが使用されている。
event_pollset.providerには、このepoll用の必要な関数が指定される。
(apr-1.5.0/poll/unix/epoll.c)
311 static apr_pollset_provider_t impl = {
312 impl_pollset_create,
313 impl_pollset_add,
314 impl_pollset_remove,
315 impl_pollset_poll,
316 impl_pollset_cleanup,
317 "epoll"
318 };
319
320 apr_pollset_provider_t *apr_pollset_provider_epoll = &impl;
apr_pollset_poll()に戻ると、これは、このapr_pollset_provider_epollに定義されている poll関数、impl_pollset_poll()を実行する。
(apr-1.5.0/poll/unix/pollset.c)
346 APR_DECLARE(apr_status_t) apr_pollset_poll(apr_pollset_t *pollset,
347 apr_interval_time_t timeout,
348 apr_int32_t *num,
349 const apr_pollfd_t **descriptors)
350 {
351 return (*pollset->provider->poll)(pollset, timeout, num, descriptors);
352 }
impl_pollset_poll関数のソースは次の通り。
(apr-1.5.0/poll/unix/epoll.c)
243 static apr_status_t impl_pollset_poll(apr_pollset_t *pollset,
244 apr_interval_time_t timeout,
245 apr_int32_t *num,
246 const apr_pollfd_t **descriptors)
247 {
248 int ret, i, j;
249 apr_status_t rv = APR_SUCCESS;
250 apr_pollfd_t *fdptr;
251
252 if (timeout > 0) {
253 timeout /= 1000;
254 }
255
256 ret = epoll_wait(pollset->p->epoll_fd, pollset->p->pollset, pollset->nalloc,
257 timeout);
258 (*num) = ret;
259
260 if (ret < 0) {
261 rv = apr_get_netos_error();
262 }
263 else if (ret == 0) {
264 rv = APR_TIMEUP;
265 }
266 else {
267 for (i = 0, j = 0; i < ret; i++) {
268 if (pollset->flags & APR_POLLSET_NOCOPY) {
269 fdptr = (apr_pollfd_t *)(pollset->p->pollset[i].data.ptr);
270 }
271 else {
272 fdptr = &(((pfd_elem_t *) (pollset->p->pollset[i].data.ptr))->pfd);
273 }
274 /* Check if the polled descriptor is our
275 * wakeup pipe. In that case do not put it result set.
276 */
277 if ((pollset->flags & APR_POLLSET_WAKEABLE) &&
278 fdptr->desc_type == APR_POLL_FILE &&
279 fdptr->desc.f == pollset->wakeup_pipe[0]) {
280 apr_pollset_drain_wakeup_pipe(pollset);
281 rv = APR_EINTR;
282 }
283 else {
284 pollset->p->result_set[j] = *fdptr;
285 pollset->p->result_set[j].rtnevents =
286 get_epoll_revent(pollset->p->pollset[i].events);
287 j++;
288 }
289 }
290 if (((*num) = j)) { /* any event besides wakeup pipe? */
291 rv = APR_SUCCESS;
292
293 if (descriptors) {
294 *descriptors = pollset->p->result_set;
295 }
296 }
297 }
298
299 if (!(pollset->flags & APR_POLLSET_NOCOPY)) {
300 pollset_lock_rings();
301
302 /* Shift all PFDs in the Dead Ring to the Free Ring */
303 APR_RING_CONCAT(&(pollset->p->free_ring), &(pollset->p->dead_ring), pfd_elem_t, link);
304
305 pollset_unlock_rings();
306 }
307
308 return rv;
309 }
event_pollset.p->epoll_fdがepollのディスクリプタで、
event_pollset.p->pollsetに利用可能なイベントが格納されて返される。
epollの監視対象ファイルディスクリプタは、apr_pollset_add()関数で追加され、apr_pollset_remove()関数で除去される。
上記のimpl_pollset_poll()ではこのpollsetをすべて返すのではなく、一部のイベントを除去している。
それが、コメント中に書かれている「wakeup pipe」だ。
これが処理可能なイベントに入っていた場合には、rvにAPR_EINTRがセットされ、apr_pollset_drain_wakeup_pipe() が実行される。
この「wakeup pipe」は apr_pollset_create_ex()関数で、APR_POLLSET_WAKEABLEが指定されている場合に生成されているが、event MPMでは指定されていないようだ。
従って、このpollsetの内容が、descriptorsにセットされる。
呼び出し元(1460行)の変数名で書けば、out_pfdとなる。
そして、numには、out_pfdにセットされた利用可能なイベント数が返される。
timeout_intervalはepoll_wait()で指定されるタイムアウトが渡される。
これは、リクエスト処理の流れ: listener_thread(1) メインループの開始 で見たスキップリストに登録されているタイムアウトまでの時間に基づいて設定された。
1461 if (rc != APR_SUCCESS) {
ここは成功しなかった場合の経路だ。
1462 if (APR_STATUS_IS_EINTR(rc)) {
シグナルによる割り込みがあった場合には、continueにより処理を続ける。
1414行目からのforループの先頭に帰っている。
1463 continue;
1464 }
1465 if (!APR_STATUS_IS_TIMEUP(rc)) {
タイムアウト以外のエラーだった場合は
異常発生とみなして、子プロセスのgracefulな終了を開始している。
1469行目のsignal_threads()では処理中でlistener_may_exit変数に1がセットされる。
1466 ap_log_error(APLOG_MARK, APLOG_CRIT, rc, ap_server_conf, 1467 "apr_pollset_poll failed. Attempting to " 1468 "shutdown process gracefully"); 1469 signal_threads(ST_GRACEFUL); 1470 } 1471 } 1472 1473 if (listener_may_exit) {
ここで再び、終了チェックが行われる。
以下は、1417行目からの処理と同一だ。
1474 close_listeners(process_slot, &closed); 1475 if (terminate_mode == ST_UNGRACEFUL 1476 || apr_atomic_read32(&connection_count) == 0) 1477 break; 1478 } 1479
次はタイムアウト処理に進む。
0 件のコメント:
コメントを投稿