Top


定義場所(file name)

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

名前(function name)

bool Monitor::notify() {

本体部(body)

  {- -------------------------------------------
  (1) (assert)
      ---------------------------------------- -}

      assert (_owner == Thread::current(), "invariant") ;
      assert (ILocked(), "invariant") ;

  {- -------------------------------------------
  (1) もし起こすべきスレッドがいなければ (= _WaitSet が空であれば), ここでリターン.
      ---------------------------------------- -}

      if (_WaitSet == NULL) return true ;

  {- -------------------------------------------
  (1) (プロファイル情報の記録)
      (現状では, NotifyCount はどこからも使われていないような気がするが...)
      ---------------------------------------- -}

      NotifyCount ++ ;

  {- -------------------------------------------
  (1) _WaitSet が空でなければ, _WaitSet の先頭要素を cxq(_LockWord) へと移動し, 
      移動させた要素の Notified フィールドを 1 にセットする.
      (Notified フィールドは, wait() 待ちに入ったスレッドが起床していいかどうかの判断に使っているフィールド.
       See: Monitor::IWait())

      (なお, _WaitSet をいじるので, この処理は _WaitLock を取得して排他した状態で行う (See: Monitor::IWait()).
       また, DCL(Double Checked Locking) のイディオムにのっとって, 
       _WaitLock を取得した後に, 再度 _WaitSet が空でないことを確認している.)
      (また, cxq への追加処理は, CAS による変更が成功するまでループすることで行う.)
      (また, 順序が逆になっても現状では問題ないのだが, 
       Notified フィールドの変更が一番最後になった方がどちらかというと安全性が高いので
       _WaitSet や cxq の変更の後に OrderAccess::fence() を張っている.)

      (なお, コメントによると別に先頭の要素でなくてもいいし, 追加先も cxq ではなく EntryList でもいいとのこと.
       さらに言うと, cxq や EntryList に追加するのではなく単に unpark() するだけでもいいが, 
       その場合は reenter 処理ですぐに stuck してしまうだろう, とのこと.)
      (<= といいつつ, 下の実験的なコードでは unpark() させているような気がするが?)
      ---------------------------------------- -}

      // Transfer one thread from the WaitSet to the EntryList or cxq.
      // Currently we just unlink the head of the WaitSet and prepend to the cxq.
      // And of course we could just unlink it and unpark it, too, but
      // in that case it'd likely impale itself on the reentry.
      Thread::muxAcquire (_WaitLock, "notify:WaitLock") ;
      ParkEvent * nfy = _WaitSet ;
      if (nfy != NULL) {                  // DCL idiom
        _WaitSet = nfy->ListNext ;
        assert (nfy->Notified == 0, "invariant") ;
        // push nfy onto the cxq
        for (;;) {
          const intptr_t v = _LockWord.FullWord ;
          assert ((v & 0xFF) == _LBIT, "invariant") ;
          nfy->ListNext = (ParkEvent *)(v & ~_LBIT);
          if (CASPTR (&_LockWord, v, UNS(nfy)|_LBIT) == v) break;
          // interference - _LockWord changed -- just retry
        }
        // Note that setting Notified before pushing nfy onto the cxq is
        // also legal and safe, but the safety properties are much more
        // subtle, so for the sake of code stewardship ...
        OrderAccess::fence() ;
        nfy->Notified = 1;
      }
      Thread::muxRelease (_WaitLock) ;

  {- -------------------------------------------
  (1) (以下の if ブロックは実験的なコード.
       起こすスレッドがいる場合には, この場で os::PlatformEvent::unpark() を呼んで起こしてしまう.
       起こされたスレッドが実行可能状態になるよりも先に, 
       この notify 処理を実行しているスレッドがロックを解放するだろうと期待しての処理)

      (なお, この最適化は NativeMonitorFlags の 5bit目(16)が立っている場合にのみ行われる)
      ---------------------------------------- -}

      if (nfy != NULL && (NativeMonitorFlags & 16)) {
        // Experimental code ... light up the wakee in the hope that this thread (the owner)
        // will drop the lock just about the time the wakee comes ONPROC.
        nfy->unpark() ;
      }

  {- -------------------------------------------
  (1) (assert)
      ---------------------------------------- -}

      assert (ILocked(), "invariant") ;

  {- -------------------------------------------
  (1) リターン
      ---------------------------------------- -}

      return true ;
    }

This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.