hotspot/src/share/vm/runtime/thread.cpp
// Release() must extract a successor from the list and then wake that thread. // It can "pop" the front of the list or use a detach-modify-reattach (DMR) scheme // similar to that used by ParkEvent::Allocate() and ::Release(). DMR-based // Release() would : // (A) CAS() or swap() null to *Lock, releasing the lock and detaching the list. // (B) Extract a successor from the private list "in-hand" // (C) attempt to CAS() the residual back into *Lock over null. // If there were any newly arrived threads and the CAS() would fail. // In that case Release() would detach the RATs, re-merge the list in-hand // with the RATs and repeat as needed. Alternately, Release() might // detach and extract a successor, but then pass the residual list to the wakee. // The wakee would be responsible for reattaching and remerging before it // competed for the lock. // // Both "pop" and DMR are immune from ABA corruption -- there can be // multiple concurrent pushers, but only one popper or detacher. // This implementation pops from the head of the list. This is unfair, // but tends to provide excellent throughput as hot threads remain hot. // (We wake recently run threads first).
void Thread::muxRelease (volatile intptr_t * Lock) {
{- -------------------------------------------
(1) (この関数では, 以下の for ループ内でロックを解放する.
さらに, ロック待ちをしているスレッドがいればそのスレッドを起床させる.
なお, ロック待ちしているスレッドが複数いる場合, 先頭のものだけを起床させる.)
---------------------------------------- -}
for (;;) {
{- -------------------------------------------
(1) まず, 引数で渡されたロック(Lock)に対し,
CAS で 1(LOCKBIT) から 0 への書き換えを試みる (= ロックの解放を試みる).
ロック待ちをしているスレッドがいなければ, この CAS は成功する.
CAS が成功したらここでリターン.
---------------------------------------- -}
const intptr_t w = Atomic::cmpxchg_ptr (0, Lock, LOCKBIT) ;
assert (w & LOCKBIT, "invariant") ;
if (w == LOCKBIT) return ;
{- -------------------------------------------
(1) (変数宣言など)
(List は, ロック待ちしているスレッドのうち, 先頭のスレッド.
nxt は, ロック待ちしているスレッドのうち, 2 番目以降のスレッドのリスト)
---------------------------------------- -}
ParkEvent * List = (ParkEvent *) (w & ~LOCKBIT) ;
assert (List != NULL, "invariant") ;
assert (List->OnList == intptr_t(Lock), "invariant") ;
ParkEvent * nxt = List->ListNext ;
{- -------------------------------------------
(1) CAS で, ロック待ちしているスレッドのうち, 先頭のスレッドを取り出す.
(より具体的には, 引数で渡されたロック(Lock)に, CAS で 2番目以降のスレッドのリスト(nxt)を書き込む)
CAS が成功したら, 取り出した先頭のスレッドを os::PlatformEvent::unpark() で起床させた後, リターンする.
(なお, unpark() する前に, 取り出した先頭のスレッドの OnList フィールドをクリアし,
さらに OrderAccess::fence() で見えるようにしている.
OnList フィールドは, ロック待ちに入ったスレッドが起床していいかどうかの判断に使っているフィールド.
See: Thread::muxAcquire())
CAS が失敗したら, この for ループの先頭に戻ってやり直す.
---------------------------------------- -}
// The following CAS() releases the lock and pops the head element.
if (Atomic::cmpxchg_ptr (intptr_t(nxt), Lock, w) != w) {
continue ;
}
List->OnList = 0 ;
OrderAccess::fence() ;
List->unpark () ;
return ;
}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.