今日もApacheのコードリーディング。
本日分はまだ終わっていないがメモっとく。
apr_pool_create_ex関数
apr_pool_create_ex()は読み終わった。コードは以下。
810 APR_DECLARE(apr_status_t) apr_pool_create_ex(apr_pool_t **newpool,
811 apr_pool_t *parent,
812 apr_abortfunc_t abort_fn,
813 apr_allocator_t *allocator)
814 {
815 apr_pool_t *pool;
816 apr_memnode_t *node;
817
818 *newpool = NULL;
819
820 if (!parent)
821 parent = global_pool;
822
823 if (!abort_fn && parent)
824 abort_fn = parent->abort_fn;
825
826 if (allocator == NULL)
827 allocator = parent->allocator;
828
829 if ((node = allocator_alloc(allocator,
830 MIN_ALLOC - APR_MEMNODE_T_SIZE)) == NULL) {
831 if (abort_fn)
832 abort_fn(APR_ENOMEM);
833
834 return APR_ENOMEM;
835 }
836
837 node->next = node;
838 node->ref = &node->next;
839
840 pool = (apr_pool_t *)node->first_avail;
841 node->first_avail = pool->self_first_avail = (char *)pool + SIZEOF_POOL_T;
842
843 pool->allocator = allocator;
844 pool->active = pool->self = node;
845 pool->abort_fn = abort_fn;
846 pool->child = NULL;
847 pool->cleanups = NULL;
848 pool->free_cleanups = NULL;
849 pool->subprocesses = NULL;
850 pool->user_data = NULL;
851 pool->tag = NULL;
852
856
857 if ((pool->parent = parent) != NULL) {
859 apr_thread_mutex_t *mutex;
860
861 if ((mutex = apr_allocator_mutex_get(parent->allocator)) != NULL)
862 apr_thread_mutex_lock(mutex);
864
865 if ((pool->sibling = parent->child) != NULL)
866 pool->sibling->ref = &pool->sibling;
867
868 parent->child = pool;
869 pool->ref = &parent->child;
870
872 if (mutex)
873 apr_thread_mutex_unlock(mutex);
875 }
876 else {
877 pool->sibling = NULL;
878 pool->ref = NULL;
879 }
880
881 *newpool = pool;
882
883 return APR_SUCCESS;
884 }
読みにくいのでNETWARE部分は削除し、APR_HAS_THREADSの#ifdefも削除してある。
ざっと説明すると、13日分で読んだallocator_alloc関数を使ってノード領域を確保する。
確保したノード領域のnextとrefをセットし、ノードの利用可能開始アドレスにapr_pool_tコントロール領域を配置。
ノードの利用可能開始アドレスを更新後、apr_pool_tコントロールの各メンバに値をセットし、
できあがったプールを呼び出し元へ返す。
ここでまず疑問が生じた。
837行目、838行目のnextとrefをセットしている部分。
837 node->next = node;
838 node->ref = &node->next;
一見意味が無いように見える。なんじゃこりゃ。
でも先に読み進むとすぐに解決。list_insertマクロとlist_removeマクロというものが出てくるが、
これらを読めば一目瞭然。なるほど、といった感じ。
list_insertマクロとlist_removeマクロ
一度パターンを知っておくと、次見たときもすぐわかる系。
コードは以下。
606 #define list_insert(node, point) do { \
607 node->ref = point->ref; \
608 *node->ref = node; \
609 node->next = point; \
610 point->ref = &node->next; \
611 } while (0)
となっている。
なるほどねー。
一方向リストは前の要素を指すポインタを保持しない。
次の要素を指すポインタだけを保持する。ゆえに一方向リストと呼ぶ。
だが、一方向リストだと途中の要素をリストから削除するのが大変面倒になる。
なぜかというと、削除の場合では「前の要素」の「次」を、削除対象の「次」に更新したいが
削除対象だけでは「前の要素」がわからないので、リストの先頭から1つづつ「前の要素」を
探さないといけない。
そこで、「ref」が出てくる。
「ref」は何かというと、「前の要素」の「次」を指すポインタになっている。
refを持つことで「前の要素」の「次」がわかるので削除時にはrefの指している先を更新すればよい。
下記は実際の削除処理。
613 /* list_remove() removes 'node' from its list. */
614 #define list_remove(node) do { \
615 *node->ref = node->next; \
616 node->next->ref = node->ref; \
617 } while (0)
これだけでOK.
さらに素敵なのがnodeのリストは結果的に循環リストになる。
最初の要素を
837 node->next = node;
838 node->ref = &node->next;
と初期化することで、
「先頭の要素」の「前の要素」は「最後の要素」になる。・・・何言っているかよくわからなくなりそうだが。
そして、
「最後の要素」の「次の要素」は「先頭の要素」になる。
こうしておくことでいろいろな処理が楽に書けるようになる。
面白いねー。
さて、次はapr_atomic_init()を攻略する。
知らない関数のapr_palloc()あたりから片付ける。
.
0 コメント:
コメントを投稿