2014年10月27日月曜日

リクエスト処理の流れ: listener_thread(2) イベントチェック

(14.1) 主ループからの続きだ。

(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 件のコメント:

コメントを投稿