Top


定義場所(file name)

hotspot/src/share/vm/runtime/safepoint.cpp

名前(function name)

void SafepointSynchronize::block(JavaThread *thread) {

本体部(body)

  {- -------------------------------------------
  (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.