src/share/vm/runtime/objectMonitor.cpp
Print this page
*** 444,453 ****
--- 444,455 ----
// We can either return -1 or retry.
// Retry doesn't make as much sense because the lock was just acquired.
return -1;
}
+ #define MAX_RECHECK_INTERVAL 1000
+
void NOINLINE ObjectMonitor::EnterI(TRAPS) {
Thread * const Self = THREAD;
assert(Self->is_Java_thread(), "invariant");
assert(((JavaThread *) Self)->thread_state() == _thread_blocked, "invariant");
*** 553,563 ****
// to defer the state transitions until absolutely necessary,
// and in doing so avoid some transitions ...
TEVENT(Inflated enter - Contention);
int nWakeups = 0;
! int RecheckInterval = 1;
for (;;) {
if (TryLock(Self) > 0) break;
assert(_owner != Self, "invariant");
--- 555,565 ----
// to defer the state transitions until absolutely necessary,
// and in doing so avoid some transitions ...
TEVENT(Inflated enter - Contention);
int nWakeups = 0;
! int recheckInterval = 1;
for (;;) {
if (TryLock(Self) > 0) break;
assert(_owner != Self, "invariant");
*** 567,580 ****
}
// park self
if (_Responsible == Self || (SyncFlags & 1)) {
TEVENT(Inflated enter - park TIMED);
! Self->_ParkEvent->park((jlong) RecheckInterval);
! // Increase the RecheckInterval, but clamp the value.
! RecheckInterval *= 8;
! if (RecheckInterval > 1000) RecheckInterval = 1000;
} else {
TEVENT(Inflated enter - park UNTIMED);
Self->_ParkEvent->park();
}
--- 569,584 ----
}
// park self
if (_Responsible == Self || (SyncFlags & 1)) {
TEVENT(Inflated enter - park TIMED);
! Self->_ParkEvent->park((jlong) recheckInterval);
! // Increase the recheckInterval, but clamp the value.
! recheckInterval *= 8;
! if (recheckInterval > MAX_RECHECK_INTERVAL) {
! recheckInterval = MAX_RECHECK_INTERVAL;
! }
} else {
TEVENT(Inflated enter - park UNTIMED);
Self->_ParkEvent->park();
}
*** 723,733 ****
// cleared by handle_special_suspend_equivalent_condition()
// or java_suspend_self()
jt->set_suspend_equivalent();
if (SyncFlags & 1) {
! Self->_ParkEvent->park((jlong)1000);
} else {
Self->_ParkEvent->park();
}
// were we externally suspended while we were waiting?
--- 727,737 ----
// cleared by handle_special_suspend_equivalent_condition()
// or java_suspend_self()
jt->set_suspend_equivalent();
if (SyncFlags & 1) {
! Self->_ParkEvent->park((jlong)MAX_RECHECK_INTERVAL);
} else {
Self->_ParkEvent->park();
}
// were we externally suspended while we were waiting?
*** 1650,1743 ****
// 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) {
! CHECK_OWNER();
! if (_WaitSet == NULL) {
! TEVENT(Empty-Notify);
! return;
! }
! DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
- int Policy = Knob_MoveNotifyee;
-
Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notify");
ObjectWaiter * iterator = DequeueWaiter();
if (iterator != NULL) {
TEVENT(Notify1 - Transfer);
guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant");
guarantee(iterator->_notified == 0, "invariant");
! if (Policy != 4) {
iterator->TState = ObjectWaiter::TS_ENTER;
}
iterator->_notified = 1;
- Thread * Self = THREAD;
iterator->_notifier_tid = Self->osthread()->thread_id();
! ObjectWaiter * List = _EntryList;
! if (List != NULL) {
! assert(List->_prev == NULL, "invariant");
! assert(List->TState == ObjectWaiter::TS_ENTER, "invariant");
! assert(List != iterator, "invariant");
}
! 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) /* empty */;
! 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 {
--- 1654,1741 ----
// 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::INotify(Thread * Self) {
! const int policy = Knob_MoveNotifyee;
Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notify");
ObjectWaiter * iterator = DequeueWaiter();
if (iterator != NULL) {
TEVENT(Notify1 - Transfer);
guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant");
guarantee(iterator->_notified == 0, "invariant");
! // Disposition - what might we do with iterator ?
! // a. add it directly to the EntryList - either tail or head.
! // b. push it onto the front of the _cxq.
! // For now we use (a).
! if (policy != 4) {
iterator->TState = ObjectWaiter::TS_ENTER;
}
iterator->_notified = 1;
iterator->_notifier_tid = Self->osthread()->thread_id();
! ObjectWaiter * list = _EntryList;
! if (list != NULL) {
! assert(list->_prev == NULL, "invariant");
! assert(list->TState == ObjectWaiter::TS_ENTER, "invariant");
! assert(list != iterator, "invariant");
}
! 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) /* empty */;
! assert(tail != NULL && tail->_next == NULL, "invariant");
! tail->_next = iterator;
! iterator->_prev = tail;
iterator->_next = NULL;
}
! } else if (policy == 2) { // 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 = _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 {
*** 1745,1892 ****
iterator->TState = ObjectWaiter::TS_RUN;
OrderAccess::fence();
ev->unpark();
}
- if (Policy < 4) {
- iterator->wait_reenter_begin(this);
- }
-
// _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.
- }
! Thread::SpinRelease(&_WaitSetLock);
!
! if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
! ObjectMonitor::_sync_Notifications->inc();
}
}
! void ObjectMonitor::notifyAll(TRAPS) {
CHECK_OWNER();
- ObjectWaiter* iterator;
if (_WaitSet == NULL) {
! TEVENT(Empty-NotifyAll);
return;
}
! DTRACE_MONITOR_PROBE(notifyAll, this, object(), THREAD);
!
! int Policy = Knob_MoveNotifyee;
! int Tally = 0;
! Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notifyall");
!
! for (;;) {
! iterator = DequeueWaiter();
! if (iterator == NULL) break;
! TEVENT(NotifyAll - Transfer1);
! ++Tally;
!
! // Disposition - what might we do with iterator ?
! // a. add it directly to the EntryList - either tail or head.
! // b. push it onto the front of the _cxq.
! // For now we use (a).
!
! guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant");
! guarantee(iterator->_notified == 0, "invariant");
! iterator->_notified = 1;
! Thread * Self = THREAD;
! iterator->_notifier_tid = Self->osthread()->thread_id();
! if (Policy != 4) {
! iterator->TState = ObjectWaiter::TS_ENTER;
}
- ObjectWaiter * List = _EntryList;
- if (List != NULL) {
- assert(List->_prev == NULL, "invariant");
- assert(List->TState == ObjectWaiter::TS_ENTER, "invariant");
- assert(List != iterator, "invariant");
- }
! 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) /* empty */;
! 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
! 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();
! }
! if (Policy < 4) {
! iterator->wait_reenter_begin(this);
}
! // _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.
}
! Thread::SpinRelease(&_WaitSetLock);
!
! if (Tally != 0 && ObjectMonitor::_sync_Notifications != NULL) {
! ObjectMonitor::_sync_Notifications->inc(Tally);
}
}
// -----------------------------------------------------------------------------
// Adaptive Spinning Support
--- 1743,1816 ----
iterator->TState = ObjectWaiter::TS_RUN;
OrderAccess::fence();
ev->unpark();
}
// _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.
! if (policy < 4) {
! iterator->wait_reenter_begin(this);
}
+ }
+ Thread::SpinRelease(&_WaitSetLock);
}
+ // Consider: a not-uncommon synchronization bug is to use notify() when
+ // notifyAll() is more appropriate, potentially resulting in stranded
+ // threads; this is one example of a lost wakeup. A useful diagnostic
+ // option is to force all notify() operations to behave as notifyAll().
+ //
+ // Note: We can also detect many such problems with a "minimum wait".
+ // When the "minimum wait" is set to a small non-zero timeout value
+ // and the program does not hang whereas it did absent "minimum wait",
+ // that suggests a lost wakeup bug. The '-XX:SyncFlags=1' option uses
+ // a "minimum wait" for all park() operations; see the recheckInterval
+ // variable and MAX_RECHECK_INTERVAL.
! void ObjectMonitor::notify(TRAPS) {
CHECK_OWNER();
if (_WaitSet == NULL) {
! TEVENT(Empty-Notify);
return;
}
! DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
! INotify(THREAD);
! if (ObjectMonitor::_sync_Notifications != NULL) {
! ObjectMonitor::_sync_Notifications->inc(1);
}
+ }
! // The current implementation of notifyAll() transfers the waiters one-at-a-time
! // from the waitset to the EntryList. This could be done more efficiently with a
! // single bulk transfer but in practice it's not time-critical. Beware too,
! // that in prepend-mode we invert the order of the waiters. Let's say that the
! // waitset is "ABCD" and the EntryList is "XYZ". After a notifyAll() in prepend
! // mode the waitset will be empty and the EntryList will be "DCBAXYZ".
! void ObjectMonitor::notifyAll(TRAPS) {
! CHECK_OWNER();
! if (_WaitSet == NULL) {
! TEVENT(Empty-NotifyAll);
! return;
}
! DTRACE_MONITOR_PROBE(notifyAll, this, object(), THREAD);
! int tally = 0;
! while (_WaitSet != NULL) {
! tally++;
! INotify(THREAD);
}
! if (tally != 0 && ObjectMonitor::_sync_Notifications != NULL) {
! ObjectMonitor::_sync_Notifications->inc(tally);
}
}
// -----------------------------------------------------------------------------
// Adaptive Spinning Support