hotspot/src/share/vm/runtime/objectMonitor.cpp
// Consider:
// If the lock is cool (cxq == null && succ == null) and we're on an MP system
// then instead of transferring a thread from the WaitSet to the EntryList
// we might just dequeue a thread from the WaitSet and directly unpark() it.
void ObjectMonitor::notify(TRAPS) {
{- -------------------------------------------
(1) (ロックを持っているスレッドしか notify() を呼んではいけないので)
CHECK_OWNER() でロックを持っているかどうかをチェックしておく.
持っていなければ IllegalMonitorStateException.
(なお, もしカレントスレッドがロックを持っているにも関わらず
ObjectMonitor 内の _owner フィールドがカレントスレッドを指していない場合,
_owner をカレントスレッドに変更してもいる)
---------------------------------------- -}
CHECK_OWNER();
{- -------------------------------------------
(1) もし誰も待ちキューにいなければ, ここでリターン.
---------------------------------------- -}
if (_WaitSet == NULL) {
TEVENT (Empty-Notify) ;
return ;
}
{- -------------------------------------------
(1) (DTrace のフック点)
---------------------------------------- -}
DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
{- -------------------------------------------
(1) (変数宣言など)
(Knob_MoveNotifyee は起床処理の内容を決めるパラメータ. 後述)
---------------------------------------- -}
int Policy = Knob_MoveNotifyee ;
{- -------------------------------------------
(1) (ここから先は _WaitSetLock で守られた critical section)
---------------------------------------- -}
Thread::SpinAcquire (&_WaitSetLock, "WaitSet - notify") ;
{- -------------------------------------------
(1) ObjectMonitor::DequeueWaiter() で, 待ちキューから ObjectWaiter を取り出す.
(取り出された ObjectWaiter は待ちキュー内には存在しなくなる).
もし待ちキューが空でなければ, 以下の if ブロック内で起床処理を行う.
なお, ここで行われる起床処理は Knob_MoveNotifyee の値に応じて 5 通りが存在.
* 0 の場合:
EntryList の先頭に追加する.
* 1 の場合:
EntryList の末尾に追加する.
* 2 の場合: (<= このケースのみ, ObjectMonitor::notifyAll() と若干挙動が違うが...#TODO)
EntryList が空なら, EntryList に追加する.
そうでなければ, cxq の先頭に追加する.
* 3 の場合:
cxq の末尾に追加する.
* それ以外の場合: (実質的には 4 の場合??)
EntryList や cxq には追加せず, この場で os::PlatformEvent::unpark() を呼んで起床させる.
---------------------------------------- -}
ObjectWaiter * iterator = DequeueWaiter() ;
if (iterator != NULL) {
{- -------------------------------------------
(1.1) (トレース出力)
---------------------------------------- -}
TEVENT (Notify1 - Transfer) ;
{- -------------------------------------------
(1.1) (assert)
---------------------------------------- -}
guarantee (iterator->TState == ObjectWaiter::TS_WAIT, "invariant") ;
guarantee (iterator->_notified == 0, "invariant") ;
{- -------------------------------------------
(1.1) TState フィールドを変更しておく.
(ただし, ここでは ObjectWaiter::TS_ENTER (= EntryList 内にいる状態) に変更する.
Knob_MoveNotifyee の値によっては EntryList に追加しないケースもあるが,
その場合については後で改めて変更し直している.
(<= EntryList の場合についても後でやればいい気がするが...?? #TODO))
---------------------------------------- -}
if (Policy != 4) {
iterator->TState = ObjectWaiter::TS_ENTER ;
}
{- -------------------------------------------
(1.1) _notified フィールドを 1 にセットしておく.
(_notified フィールドは, wait() 待ちに入ったスレッドが起床していいかどうかの判断に使っているフィールド.
See: ObjectMonitor::wait())
---------------------------------------- -}
iterator->_notified = 1 ;
{- -------------------------------------------
(1.1) (変数宣言など)
---------------------------------------- -}
ObjectWaiter * List = _EntryList ;
{- -------------------------------------------
(1.1) (assert)
---------------------------------------- -}
if (List != NULL) {
assert (List->_prev == NULL, "invariant") ;
assert (List->TState == ObjectWaiter::TS_ENTER, "invariant") ;
assert (List != iterator, "invariant") ;
}
{- -------------------------------------------
(1.1) 待ちキューから取り出した要素を起床させる.
Knob_MoveNotifyee の値に応じて, 上述のように 5 通りの起床方法がある.
(EntryList の先頭に追加/末尾に追加, cxq の先頭に追加/末尾に追加, 追加せずこの場で起こす)
(なおコメントによると,
EntryList の末尾を簡単に取得できるように,
EntryList を circular doubly linked list (CDLL) にしてはどうか,
とのこと)
---------------------------------------- -}
if (Policy == 0) { // prepend to EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
List->_prev = iterator ;
iterator->_next = List ;
iterator->_prev = NULL ;
_EntryList = iterator ;
}
} else
if (Policy == 1) { // append to EntryList
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
// CONSIDER: finding the tail currently requires a linear-time walk of
// the EntryList. We can make tail access constant-time by converting to
// a CDLL instead of using our current DLL.
ObjectWaiter * Tail ;
for (Tail = List ; Tail->_next != NULL ; Tail = Tail->_next) ;
assert (Tail != NULL && Tail->_next == NULL, "invariant") ;
Tail->_next = iterator ;
iterator->_prev = Tail ;
iterator->_next = NULL ;
}
} else
if (Policy == 2) { // prepend to cxq
// prepend to cxq
if (List == NULL) {
iterator->_next = iterator->_prev = NULL ;
_EntryList = iterator ;
} else {
iterator->TState = ObjectWaiter::TS_CXQ ;
for (;;) {
ObjectWaiter * Front = _cxq ;
iterator->_next = Front ;
if (Atomic::cmpxchg_ptr (iterator, &_cxq, Front) == Front) {
break ;
}
}
}
} else
if (Policy == 3) { // append to cxq
iterator->TState = ObjectWaiter::TS_CXQ ;
for (;;) {
ObjectWaiter * Tail ;
Tail = _cxq ;
if (Tail == NULL) {
iterator->_next = NULL ;
if (Atomic::cmpxchg_ptr (iterator, &_cxq, NULL) == NULL) {
break ;
}
} else {
while (Tail->_next != NULL) Tail = Tail->_next ;
Tail->_next = iterator ;
iterator->_prev = Tail ;
iterator->_next = NULL ;
break ;
}
}
} else {
ParkEvent * ev = iterator->_event ;
iterator->TState = ObjectWaiter::TS_RUN ;
OrderAccess::fence() ;
ev->unpark() ;
}
{- -------------------------------------------
(1.1) (プロファイル情報の記録)
(See: ObjectWaiter::wait_reenter_begin()) (See: [here](no21146np.html) for details)
---------------------------------------- -}
if (Policy < 4) {
iterator->wait_reenter_begin(this);
}
{- -------------------------------------------
(1.1) (_WaitSetLock は, EntryList ではなく _WaitSet を保護するためのロックなので,
EntryList に要素を追加する処理については _WaitSet で保護された critical section の外で行うようにしてもいい.
ただし, _WaitSetLock はそもそもほとんど競合しない (wait() がタイムアウトするときか
interrupt されたときのみ競合する) ので, critical section を短くすることにさほどの意味は無い.)
---------------------------------------- -}
// _WaitSetLock protects the wait queue, not the EntryList. We could
// move the add-to-EntryList operation, above, outside the critical section
// protected by _WaitSetLock. In practice that's not useful. With the
// exception of wait() timeouts and interrupts the monitor owner
// is the only thread that grabs _WaitSetLock. There's almost no contention
// on _WaitSetLock so it's not profitable to reduce the length of the
// critical section.
}
{- -------------------------------------------
(1) (ここまでが _WaitSetLock による critical section)
---------------------------------------- -}
Thread::SpinRelease (&_WaitSetLock) ;
{- -------------------------------------------
(1) (プロファイル情報の記録) (See: UsePerfData) (See: sun.rt._sync_Notifications)
(なお, 実際にスレッドの起床処理を行った場合にのみカウンタ値を増加させている)
---------------------------------------- -}
if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
ObjectMonitor::_sync_Notifications->inc() ;
}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.