hotspot/src/share/vm/runtime/safepoint.cpp
void SafepointSynchronize::block(JavaThread *thread) {
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(thread != NULL, "thread must be set");
assert(thread->is_Java_thread(), "not a Java thread");
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
// Threads shouldn't block if they are in the middle of printing, but...
ttyLocker::break_tty_lock_for_safepoint(os::current_thread_id());
{- -------------------------------------------
(1) このスレッドが終了していないかどうかを確認しておく.
終了している場合は, 以下のどちらかを行う.
* HotSpot の終了処理が始まったことでスレッドが終了している場合:
ここで永久にブロックする.
* 上記以外の理由でスレッドが終了した場合:
ここでリターンする.
(なお, ブロックするかどうかの判定は JavaThread::block_if_vm_exited() を呼んで行っている.
HotSpot の終了処理が始まっていた場合は, この中でブロックする)
---------------------------------------- -}
// Only bail from the block() call if the thread is gone from the
// thread list; starting to exit should still block.
if (thread->is_terminated()) {
// block current thread if we come here from native code when VM is gone
thread->block_if_vm_exited();
// otherwise do nothing
return;
}
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
JavaThreadState state = thread->thread_state();
{- -------------------------------------------
(1) JavaFrameAnchor::make_walkable() を呼んで,
スタックフレームを辿れるようにしておく.
---------------------------------------- -}
thread->frame_anchor()->make_walkable(thread);
{- -------------------------------------------
(1) (以下の switch の中で, JavaThreadState(以下の state)に応じた停止処理を行う.
どの場合も, 実際の停止処理は
Threads_lock に対して Monitor::lock_without_safepoint_check() を呼ぶことで行う.
* _thread_in_vm_trans または _thread_in_Java の場合:
JavaThreadState を _thread_blocked に変更する.
また, 既に VMThread による Safepoint 処理が開始されている場合は,
_waiting_to_block をデクリメントしてから
Safepoint_lock に対して Monitor::notify_all() を呼ぶことで VM Thread を起こしておく.
その後, Threads_lock に対して Monitor::lock_without_safepoint_check() を呼ぶことでブロックされる.
* _thread_in_native_trans, _thread_blocked_trans, または _thread_new_trans の場合
JavaThreadState を _thread_blocked に変更した後,
Threads_lock に対して Monitor::lock_without_safepoint_check() を呼ぶことでブロックされる.
* それ以外の場合
あり得ない (fatal() で強制終了) )
---------------------------------------- -}
// Check that we have a valid thread_state at this point
switch(state) {
{- -------------------------------------------
(1.1) (以下は _thread_in_vm_trans または _thread_in_Java の場合の処理)
---------------------------------------- -}
case _thread_in_vm_trans:
case _thread_in_Java: // From compiled code
{- -------------------------------------------
(1.1.1) JavaThreadState を _thread_in_vm に変更しておく.
(この後の処理で Safepoint_lock のロックを取った際にブロックしないようにするため,
まだ VM 内の処理を行っているようなふりをする.)
---------------------------------------- -}
// We are highly likely to block on the Safepoint_lock. In order to avoid blocking in this case,
// we pretend we are still in the VM.
thread->set_thread_state(_thread_in_vm);
{- -------------------------------------------
(1.1.1) ?? (TryingToBlock は何に使われる?? 現在の実装ではどこからも使われていないような...?? #TODO)
---------------------------------------- -}
if (is_synchronizing()) {
Atomic::inc (&TryingToBlock) ;
}
{- -------------------------------------------
(1.1.1) (以下の処理は Safepoint_lock で排他した状態で行う)
JavaThreadState を _thread_blocked に変更する.
(なお, 普通ならこの後の lock_without_safepoint_check() 呼び出しの後で
external suspension をチェックして suspend されていれば self-suspend するところだが, ここではできない.
理由は, この関数は他のロックを握った状態で呼び出されることが多く, その状態で self-suspend するとデッドロックの恐れがあるため.)
また, 既に VMThread による Safepoint 処理が開始されている場合は
(= SafepointSynchronize::is_synchronizing() が true を返す場合は),
_waiting_to_block をデクリメントした後,
Safepoint_lock に対して Monitor::notify_all() を呼ぶことで VM Thread を起こしておく.
(これは, SafepointSynchronize::begin() の処理と同期を取るための処理.
See: SafepointSynchronize::begin())
(なお, 以下で呼び出している ThreadSafepointState::set_has_called_back() は, デバッグ用の処理.)
---------------------------------------- -}
// We will always be holding the Safepoint_lock when we are examine the state
// of a thread. Hence, the instructions between the Safepoint_lock->lock() and
// Safepoint_lock->unlock() are happening atomic with regards to the safepoint code
Safepoint_lock->lock_without_safepoint_check();
if (is_synchronizing()) {
// Decrement the number of threads to wait for and signal vm thread
assert(_waiting_to_block > 0, "sanity check");
_waiting_to_block--;
thread->safepoint_state()->set_has_called_back(true);
// Consider (_waiting_to_block < 2) to pipeline the wakeup of the VM thread
if (_waiting_to_block == 0) {
Safepoint_lock->notify_all();
}
}
// We transition the thread to state _thread_blocked here, but
// we can't do our usual check for external suspension and then
// self-suspend after the lock_without_safepoint_check() call
// below because we are often called during transitions while
// we hold different locks. That would leave us suspended while
// holding a resource which results in deadlocks.
thread->set_thread_state(_thread_blocked);
Safepoint_lock->unlock();
{- -------------------------------------------
(1.1.1) Threads_lock に対して Monitor::lock_without_safepoint_check() を呼ぶことでブロックされる.
目が覚めたら, JavaThreadState をブロック前の状態 (state) に戻した後,
Monitor::unlock() で Threads_lock のロックを解除する.
---------------------------------------- -}
// We now try to acquire the threads lock. Since this lock is hold by the VM thread during
// the entire safepoint, the threads will all line up here during the safepoint.
Threads_lock->lock_without_safepoint_check();
// restore original state. This is important if the thread comes from compiled code, so it
// will continue to execute with the _thread_in_Java state.
thread->set_thread_state(state);
Threads_lock->unlock();
break;
{- -------------------------------------------
(1.1) (以下は, _thread_in_native_trans, _thread_blocked_trans, または _thread_new_trans の場合の処理)
---------------------------------------- -}
case _thread_in_native_trans:
case _thread_blocked_trans:
case _thread_new_trans:
{- -------------------------------------------
(1.1.1) (verify)
(このケースで suspend_type が _call_back ということはあり得ない)
---------------------------------------- -}
if (thread->safepoint_state()->type() == ThreadSafepointState::_call_back) {
thread->print_thread_state();
fatal("Deadlock in safepoint code. "
"Should have called back to the VM before blocking.");
}
{- -------------------------------------------
(1.1.1) JavaThreadState を _thread_blocked に変更しておく.
(なお, 普通ならこの後の lock_without_safepoint_check() 呼び出しの後で
external suspension をチェックして suspend されていれば self-suspend するところだが, ここではできない.
理由は, この関数は他のロックを握った状態で呼び出されることが多く, その状態で self-suspend するとデッドロックの恐れがあるため.)
(_thread_in_native_trans 状態のスレッドを強制的に停止させるのは危険なので,
safepoint 処理でスレッドを停止させようとしているコード側では
_thread_in_native_trans のスレッドが自発的に停止するのを待っているかもしれない.
そのため, ここで状態を変更して safepoint に到達したことを伝える必要がある)
---------------------------------------- -}
// We transition the thread to state _thread_blocked here, but
// we can't do our usual check for external suspension and then
// self-suspend after the lock_without_safepoint_check() call
// below because we are often called during transitions while
// we hold different locks. That would leave us suspended while
// holding a resource which results in deadlocks.
thread->set_thread_state(_thread_blocked);
// It is not safe to suspend a thread if we discover it is in _thread_in_native_trans. Hence,
// the safepoint code might still be waiting for it to block. We need to change the state here,
// so it can see that it is at a safepoint.
{- -------------------------------------------
(1.1.1) Threads_lock に対して Monitor::lock_without_safepoint_check() を呼ぶことでブロックされる.
目が覚めたら, JavaThreadState をブロック前の状態 (state) に戻した後,
Threads_lock のロックを Monitor::unlock() で解除する.
---------------------------------------- -}
// Block until the safepoint operation is completed.
Threads_lock->lock_without_safepoint_check();
// Restore state
thread->set_thread_state(state);
Threads_lock->unlock();
break;
{- -------------------------------------------
(1.1) (以下は, 上記以外のケースの処理.
通常はあり得ないので, fatal() で終了させるだけ.)
---------------------------------------- -}
default:
fatal(err_msg("Illegal threadstate encountered: %d", state));
}
{- -------------------------------------------
(1)
---------------------------------------- -}
// Check for pending. async. exceptions or suspends - except if the
// thread was blocked inside the VM. has_special_runtime_exit_condition()
// is called last since it grabs a lock and we only want to do that when
// we must.
//
// Note: we never deliver an async exception at a polling point as the
// compiler may not have an exception handler for it. The polling
// code will notice the async and deoptimize and the exception will
// be delivered. (Polling at a return point is ok though). Sure is
// a lot of bother for a deprecated feature...
//
// We don't deliver an async exception if the thread state is
// _thread_in_native_trans so JNI functions won't be called with
// a surprising pending exception. If the thread state is going back to java,
// async exception is checked in check_special_condition_for_native_trans().
if (state != _thread_blocked_trans &&
state != _thread_in_vm_trans &&
thread->has_special_runtime_exit_condition()) {
thread->handle_special_runtime_exit_condition(
!thread->is_at_poll_safepoint() && (state != _thread_in_native_trans));
}
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.