hotspot/src/share/vm/runtime/thread.cpp
void Thread::muxAcquire (volatile intptr_t * Lock, const char * LockName) {
{- -------------------------------------------
(1) 引数で指定されたロック(Lock)に対して, CAS で 0 から 1(LOCKBIT) への書き換えを試みる (= ロックの確保を試みる).
CAS が成功したら, ここでリターン.
---------------------------------------- -}
intptr_t w = Atomic::cmpxchg_ptr (LOCKBIT, Lock, 0) ;
if (w == 0) return ;
{- -------------------------------------------
(1) Lock が 0 ではないがロックされてもいない (単にスレッドのポインタが入っているだけ) のであれば,
ロックビットを立てるために, 再度 CAS を行ってみる.
CAS が成功したら, ここでリターン.
---------------------------------------- -}
if ((w & LOCKBIT) == 0 && Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) {
return ;
}
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
TEVENT (muxAcquire - Contention) ;
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
ParkEvent * const Self = Thread::current()->_MuxEvent ;
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert ((intptr_t(Self) & LOCKBIT) == 0, "invariant") ;
{- -------------------------------------------
(1) (ロックが取れるまで, 以下の for ループを繰り返す.
この for ループ中では, スピンロックと park による待機が繰り返される.)
---------------------------------------- -}
for (;;) {
{- -------------------------------------------
(1.1) まず, 適当な回数だけスピンロックを行う.
(マルチプロセッサの場合は 101 回, そうでなければ 1 回).
なお, スピンロック処理は以下のようなもの.
引数で指定されたロック(Lock)がロックされておらず(= LOCKBIT が立っておらず), かつ
CAS でロックビットを立てることに成功すれば, リターンする.
---------------------------------------- -}
int its = (os::is_MP() ? 100 : 0) + 1 ;
// Optional spin phase: spin-then-park strategy
while (--its >= 0) {
w = *Lock ;
if ((w & LOCKBIT) == 0 && Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) {
return ;
}
}
{- -------------------------------------------
(1.1) カレントスレッドの _MuxEvent フィールド(以下の Self) をリセットし, OnList フィールドに値を設定しておく.
---------------------------------------- -}
Self->reset() ;
Self->OnList = intptr_t(Lock) ;
// The following fence() isn't _strictly necessary as the subsequent
// CAS() both serializes execution and ratifies the fetched *Lock value.
OrderAccess::fence();
{- -------------------------------------------
(1.1) 引数で指定されたロック(Lock)に対して,
カレントスレッドの _MuxEvent フィールド(以下の Self)を登録し,
ロック待ちだと分かる状態にする.
登録処理は, Lock に格納されているリストの先頭に Self を追加するだけ.
この追加処理は CAS で行う.
CAS は失敗することもあるため, 成功するまで以下の for ループを繰り返す.
なお, リストに追加する前に Lock のロックが解放されるケースもありうる (以下の, "(w & LOCKBIT) == 0" のパス).
その場合は CAS でロックの確保を試み,
成功すれば (リストへの追加処理は止めて) この時点でリターンすることにしている.
---------------------------------------- -}
for (;;) {
w = *Lock ;
if ((w & LOCKBIT) == 0) {
if (Atomic::cmpxchg_ptr (w|LOCKBIT, Lock, w) == w) {
Self->OnList = 0 ; // hygiene - allows stronger asserts
return ;
}
continue ; // Interference -- *Lock changed -- Just retry
}
assert (w & LOCKBIT, "invariant") ;
Self->ListNext = (ParkEvent *) (w & ~LOCKBIT );
if (Atomic::cmpxchg_ptr (intptr_t(Self)|LOCKBIT, Lock, w) == w) break ;
}
{- -------------------------------------------
(1.1) カレントスレッドの _MuxEvent フィールド(以下の Self) の OnList がクリアされるまで,
os::PlatformEvent::park() で待機し続ける.
(OnList は, Thread::muxRelease() 内でクリアされる.
See: Thread::muxRelease())
---------------------------------------- -}
while (Self->OnList != 0) {
Self->park() ;
}
}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.