RFR: 8166197: assert(RelaxAssert || w != Thread::current()->_MutexEvent) failed: invariant

David Holmes david.holmes at oracle.com
Wed Oct 12 05:08:38 UTC 2016


Bug: https://bugs.openjdk.java.net/browse/JDK-8166197
webrev: http://cr.openjdk.java.net/~dholmes/8166197/webrev/

In IUnlock we have the following succession code to wakeup the "onDeck" 
thread:

  ParkEvent * List = _EntryList;
   if (List != NULL) {
     // Transfer the head of the EntryList to the OnDeck position.
     // Once OnDeck, a thread stays OnDeck until it acquires the lock.
     // For a given lock there is at most OnDeck thread at any one instant.
    WakeOne:
     assert(List == _EntryList, "invariant");
     ParkEvent * const w = List;
     assert(RelaxAssert || w != Thread::current()->_MutexEvent, 
"invariant");
     _EntryList = w->ListNext;
     // as a diagnostic measure consider setting w->_ListNext = BAD
     assert(UNS(_OnDeck) == _LBIT, "invariant");
     _OnDeck = w;  // pass OnDeck to w.

It is critical that the update to _EntryList happens before we set 
_OnDeck, as as soon as _OnDeck is set the selected thread (which need 
not yet have parked) can acquire the mutex, complete its critical 
section and proceed to unlock the mutex, and so execute IUnlock in 
parallel with the original thread. If the write to _EntryList has not 
yet happened that second thread finds itself still at the head of 
_EntryList and so the assert fires. If the write to _EntryList happens 
after the load "List = _EntryList", then the first assert can also fire.

Preferred fix today is to use OrderAccess::release_store(&_OnDeck, w) 
with a matching load_acquire(&_OnDeck) in the ILock code:

   while (_OnDeck != ESelf) {
     ParkCommon(ESelf, 0);
   }

and corresponding "raw" lock code. Also fixed a couple of typos.

Thanks,
David


More information about the hotspot-runtime-dev mailing list