hotspot/src/share/vm/runtime/park.cpp
ParkEvent * ParkEvent::Allocate (Thread * t) {
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
// In rare cases -- JVM_RawMonitor* operations -- we can find t == null.
ParkEvent * ev ;
{- -------------------------------------------
(1) まずは ParkEvent::FreeList からの確保を試みる.
以下の for ループ内が ParkEvent::FreeList からの確保処理. 処理は次のような流れになる.
(0) FreeList が空だったら, 確保のしようがないので, 何もしないでループを抜ける
(1) Atomic::cmpxchg_ptr() で ParkEvent::FreeList 全体を取得する.
(代わりに, ParkEvent::FreeList の値は一時的に NULL に変更しておく)
(2) 取得したリストの先頭要素だけを取り出す.
(もしリストの要素が1個だけなら, もうすることはないので, ここでループを抜ける)
(3) リストの残りを Atomic::cmpxchg_ptr() で ParkEvent::FreeList に戻す.
成功したら, ここでループを抜ける.
(4) ParkEvent::FreeList に戻す作業が失敗するのは
新しい要素が追加されていた場合なので,
もう一度 ParkEvent::FreeList 全体を取得し, 初めに取得したリストとマージする
(取得し損ねたら (3) に戻ってやり直し).
この後, (3) に戻って, このマージ結果を ParkEvent::FreeList に戻す作業を行う.
以下, 成功するまで (3) と (4) を繰り返す.
---------------------------------------- -}
// Start by trying to recycle an existing but unassociated
// ParkEvent from the global free list.
for (;;) {
ev = FreeList ;
if (ev == NULL) break ;
// 1: Detach - sequester or privatize the list
// Tantamount to ev = Swap (&FreeList, NULL)
if (Atomic::cmpxchg_ptr (NULL, &FreeList, ev) != ev) {
continue ;
}
// We've detached the list. The list in-hand is now
// local to this thread. This thread can operate on the
// list without risk of interference from other threads.
// 2: Extract -- pop the 1st element from the list.
ParkEvent * List = ev->FreeNext ;
if (List == NULL) break ;
for (;;) {
// 3: Try to reattach the residual list
guarantee (List != NULL, "invariant") ;
ParkEvent * Arv = (ParkEvent *) Atomic::cmpxchg_ptr (List, &FreeList, NULL) ;
if (Arv == NULL) break ;
// New nodes arrived. Try to detach the recent arrivals.
if (Atomic::cmpxchg_ptr (NULL, &FreeList, Arv) != Arv) {
continue ;
}
guarantee (Arv != NULL, "invariant") ;
// 4: Merge Arv into List
ParkEvent * Tail = List ;
while (Tail->FreeNext != NULL) Tail = Tail->FreeNext ;
Tail->FreeNext = Arv ;
}
break ;
}
{- -------------------------------------------
(1) もし ParkEvent オブジェクトが確保できていなければ,
新しい ParkEvent オブジェクトを new で生成する.
(コメントによると,
「上の処理で FreeList 全体を取得してから残りを元に戻すまでの間に
別のスレッドが確保にくると, (FreeList が NULL に見えるので)
不必要な new ParkEvent が行われてしまうかもしれない.
とはいえ, そういうケースはほとんど起こらないし, 起こったとしても
最大でもスレッド数と同数までしか発生しないので大した問題ではない.
あと, TSM/immortal という制約を外していいなら, 不要な ParkEvent を捨てて減らすことも可能」
とのこと.)
---------------------------------------- -}
if (ev != NULL) {
guarantee (ev->AssociatedWith == NULL, "invariant") ;
} else {
// Do this the hard way -- materialize a new ParkEvent.
// In rare cases an allocating thread might detach a long list --
// installing null into FreeList -- and then stall or be obstructed.
// A 2nd thread calling Allocate() would see FreeList == null.
// The list held privately by the 1st thread is unavailable to the 2nd thread.
// In that case the 2nd thread would have to materialize a new ParkEvent,
// even though free ParkEvents existed in the system. In this case we end up
// with more ParkEvents in circulation than we need, but the race is
// rare and the outcome is benign. Ideally, the # of extant ParkEvents
// is equal to the maximum # of threads that existed at any one time.
// Because of the race mentioned above, segments of the freelist
// can be transiently inaccessible. At worst we may end up with the
// # of ParkEvents in circulation slightly above the ideal.
// Note that if we didn't have the TSM/immortal constraint, then
// when reattaching, above, we could trim the list.
ev = new ParkEvent () ;
guarantee ((intptr_t(ev) & 0xFF) == 0, "invariant") ;
}
{- -------------------------------------------
(1) 取得した ParkEvent オブジェクトを初期化しておく.
---------------------------------------- -}
ev->reset() ; // courtesy to caller
ev->AssociatedWith = t ; // Associate ev with t
ev->FreeNext = NULL ;
{- -------------------------------------------
(1) 結果をリターン
---------------------------------------- -}
return ev ;
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.