メモ代わり。てきとーに。 いや、ですからてきとーですって。 2年前ぐらいにPythonあたりでメールくれた方、ごめんなさい。メール紛失してしまい無視した形になってしまいました。。。

2008年2月22日金曜日

[Apache][CodeReading] Apache2.2.8コードリーディング22日目

今日もApache2.2.8コードリーディング。

今日やったところは、

  • apr_pollset_add()(kqueue)/Apache2.2.8
  • apr_pollset_add()(epoll)/Apache2.2.8
  • APR_EBADFマクロ/Apache2.2.8
  • apr_pollset_remove()/Apache2.2.8
  • apr_os_sock_t/Apache2.2.8
  • NETWAREマクロ/Apache2.2.8
  • __BEOS__マクロ/Apache2.2.8
  • OS2マクロ/Apache2.2.8
  • WIN32マクロ/Apache2.2.8
  • pollset_unlock_rings()/Apache2.2.8
  • APR_RING_INSERT_TAILマクロ/Apache2.2.8
  • APR_RING_SPLICE_TAILマクロ/Apache2.2.8
  • APR_RING_SPLICE_BEFOREマクロ/Apache2.2.8
あたり。

で、まだ初期化部分。
DEBUGログを出力後にIOWAITするところ。
IOWAITするためにpollするんだけど、それを実行しようとしている部分。

リングの構造はさらっと19日目ぐらいにやった。
apr_pollset_tは環境により5種類の実装のうちひとつがconfigureなりで選択される。
今日読んだapr_pollset_add()もapr_pollset_remove()も5種類。

apr_pollset_remove()
apr_pollset_removeは、指定されたポールセット(apr_pollset_t)から指定されたディスクリプタ(apr_pollfd_t)を削除する。
ringをapr_pollset_t構造体に持つパターン(epoll、kqueue、portが使える環境)の場合は、
query_ringというリングから対象ディスクリプタを削除し、dead_ringというリングに対象ディスクリプタを登録する、
ということをやっている。
もちろん、epoll、kqueue、portからも削除する。


epoll、kqueue、portのいずれも使えず、poll関数が使える場合はringを保持しない。
代わりにquery_setというapr_pollfd_t構造体へのポインタの配列を保持する。
その配列から、対象ディスクリプタを削除する。

epoll、kqueue、portのいずれも使えず、さらにpoll関数も使用不可の場合も、ringを保持しないapr_pollset_tが使用される。
またpoll関数が使える場合と同様に、query_setほ保持し、apr_pollfd_t構造体へのポインタの配列から対象ディスクリプタを削除する。



apr_pollset_add()
まだ、epoll版とkqueue版しか読んでいない。

まず最初にfree_ringから空き領域を取得する。
空きが無ければapr_palloc()で領域を確保後、その領域を初期化(APR_RING_ELEM_INIT)する。
その領域にパラメータで指定されたディスクリプタをセットし、epoll_ctlを使用してepollにイベントを追加する。
うまく追加できればquery_ringにfree_ringから取得した領域を追加し、
うまく追加できなければfree_ringに取得した領域を返す。

で、そのソースが以下。

138 APR_DECLARE(apr_status_t) apr_pollset_add(apr_pollset_t *pollset,
139 const apr_pollfd_t *descriptor)
140 {
141 struct epoll_event ev;
142 int ret = -1;
143 pfd_elem_t *elem;
144 apr_status_t rv = APR_SUCCESS;
145
146 pollset_lock_rings();
147
148 if (!APR_RING_EMPTY(&(pollset->free_ring), pfd_elem_t, link)) {
149 elem = APR_RING_FIRST(&(pollset->free_ring));
150 APR_RING_REMOVE(elem, link);
151 }
152 else {
153 elem = (pfd_elem_t *) apr_palloc(pollset->pool, sizeof(pfd_elem_t));
154 APR_RING_ELEM_INIT(elem, link);
155 }
156 elem->pfd = *descriptor;
157
158 ev.events = get_epoll_event(descriptor->reqevents);
159 ev.data.ptr = elem;
160 if (descriptor->desc_type == APR_POLL_SOCKET) {
161 ret = epoll_ctl(pollset->epoll_fd, EPOLL_CTL_ADD,
162 descriptor->desc.s->socketdes, &ev);
163 }
164 else {
165 ret = epoll_ctl(pollset->epoll_fd, EPOLL_CTL_ADD,
166 descriptor->desc.f->filedes, &ev);
167 }
168
169 if (0 != ret) {
170 rv = APR_EBADF;
171 APR_RING_INSERT_TAIL(&(pollset->free_ring), elem, pfd_elem_t, link);
172 }
173 else {
174 pollset->nelts++;
175 APR_RING_INSERT_TAIL(&(pollset->query_ring), elem, pfd_elem_t, link);
176 }
177
178 pollset_unlock_rings();
179
180 return rv;
181 }
 

APR_RING_EMPTYで空かどうかチェックし、空でなければ先頭の領域を取得する。
空だった場合は、領域確保後、APR_RING_ELEM_INITマクロで初期化するのだが、
この、APR_RING_ELEM_INITが良くわからない。
やっていることは、elem->link->nextとelem->link->prevにelemを代入しているだけ。
その初期化自体は良いのだが、epoll_ctl後、free_ringか、query_ringにすぐに追加してしまうので
APR_RING_ELEM_INIT自体無意味に思える。
最初はAPR_RING_INSERT_TAILマクロでnextとprevに何かしらのポインタが必要なのかと思ったが、
そうでもない。

APR_RING_INSERT_TAILは、

#define APR_RING_SPLICE_BEFORE(lep, ep1, epN, link) do { \
APR_RING_NEXT((epN), link) = (lep); \
APR_RING_PREV((ep1), link) = APR_RING_PREV((lep), link); \
APR_RING_NEXT(APR_RING_PREV((lep), link), link) = (ep1); \
APR_RING_PREV((lep), link) = (epN); \
} while (0)
 

こんな感じに置き換わる。
で、

APR_RING_NEXT((epN), link) = (lep)
 

は、

elem->link = APR_SENTINEL(hp)
 

に、そして、

APR_RING_PREV((ep1), link = APR_RING_PREV((lep), link);
 

は、

elem->link->prev = APR_SENTINEL(hp)->link->prev;
 

に置き換わる。
つまり、せっかく初期化してもすぐに上書きしてしまうので、
無意味な気がする・・・・。

理由があるとすれば、RINGを構成する構造体に何かしらの変更が加わった場合に対処しやすいように
といった感じか。
といってもそれも考えにくい・・・。

うーむ。

わからん。
とりあえず、今度APR_RING_ELEM_INITをコメントアウトして動かしてみようかと思う。

いつかやるタスク。
  • APR_RING_ELEM_INITをapr_pollset_add関数から削除して動かしてみる。
あと、ついでに、
apr_palloc()関数も。
apr_palloc()は空きノードが無かった場合allocator_alloc()をつかってmallocするんだが、
このときNULLが返ってきた場合apr_palloc()はNULLを返す。
なのにもかかわらず、apr_palloc()を使用しているところのほとんどがNULLチェックをしていない。
うーむ。
Segmentation Faltですか??
ということで、
  • malloc失敗するパターンで、apr_palloc()を読んでみる。
というのもやってみようと思う。





おしまい。
.

0 コメント: