< prev index next >

src/hotspot/share/runtime/objectMonitor.cpp

Print this page
rev 51780 : imported patch syncknobs-00-base
rev 51781 : imported patch syncknobs-01-Knob_ReportSettings
rev 51782 : imported patch syncknobs-02-Knob_SpinBackOff
rev 51783 : imported patch syncknobs-03-BackOffMask
rev 51784 : imported patch syncknobs-04-Knob_ExitRelease
rev 51785 : imported patch syncknobs-05-Knob_InlineNotify
rev 51786 : imported patch syncknobs-06-Knob_Verbose
rev 51787 : imported patch syncknobs-07-Knob_VerifyInUse
rev 51788 : imported patch syncknobs-08-Knob_VerifyMatch
rev 51789 : imported patch syncknobs-09-Knob_SpinBase
rev 51790 : imported patch syncknobs-10-Knob_CASPenalty
rev 51791 : imported patch syncknobs-11-Knob_OXPenalty
rev 51792 : imported patch syncknobs-12-Knob_SpinSetSucc
rev 51793 : imported patch syncknobs-13-Knob_SpinEarly
rev 51794 : imported patch syncknobs-14-Knob_SuccEnabled
rev 51795 : imported patch syncknobs-15-Knob_SuccRestrict
rev 51796 : imported patch syncknobs-16-Knob_MaxSpinners
rev 51797 : imported patch syncknobs-17-Knob_SpinAfterFutile
rev 51798 : imported patch syncknobs-18-Knob_OState
rev 51799 : imported patch syncknobs-19-Knob_UsePause
rev 51800 : imported patch syncknobs-20-Knob_ExitPolicy
rev 51801 : imported patch syncknobs-21-Knob_ResetEvent
rev 51802 : imported patch syncknobs-22-Knob_FastHSSEC
rev 51803 : imported patch syncknobs-23-Knob_MoveNotifyee
rev 51804 : imported patch syncknobs-24-Knob_QMode

*** 99,141 **** // Tunables ... // The knob* variables are effectively final. Once set they should // never be modified hence. Consider using __read_mostly with GCC. - int ObjectMonitor::Knob_ExitRelease = 0; - int ObjectMonitor::Knob_InlineNotify = 1; - int ObjectMonitor::Knob_Verbose = 0; - int ObjectMonitor::Knob_VerifyInUse = 0; - int ObjectMonitor::Knob_VerifyMatch = 0; int ObjectMonitor::Knob_SpinLimit = 5000; // derived by an external tool - - static int Knob_ReportSettings = 0; - static int Knob_SpinBase = 0; // Floor AKA SpinMin - static int Knob_SpinBackOff = 0; // spin-loop backoff - static int Knob_CASPenalty = -1; // Penalty for failed CAS - static int Knob_OXPenalty = -1; // Penalty for observed _owner change - static int Knob_SpinSetSucc = 1; // spinners set the _succ field - static int Knob_SpinEarly = 1; - static int Knob_SuccEnabled = 1; // futile wake throttling - static int Knob_SuccRestrict = 0; // Limit successors + spinners to at-most-one - static int Knob_MaxSpinners = -1; // Should be a function of # CPUs static int Knob_Bonus = 100; // spin success bonus static int Knob_BonusB = 100; // spin success bonus static int Knob_Penalty = 200; // spin failure penalty static int Knob_Poverty = 1000; - static int Knob_SpinAfterFutile = 1; // Spin after returning from park() static int Knob_FixedSpin = 0; - static int Knob_OState = 3; // Spinner checks thread state of _owner - static int Knob_UsePause = 1; - static int Knob_ExitPolicy = 0; static int Knob_PreSpin = 10; // 20-100 likely better - static int Knob_ResetEvent = 0; - static int BackOffMask = 0; - static int Knob_FastHSSEC = 0; - static int Knob_MoveNotifyee = 2; // notify() - disposition of notifyee - static int Knob_QMode = 0; // EntryList-cxq policy - queue discipline static volatile int InitDone = 0; // ----------------------------------------------------------------------------- // Theory of operations -- Monitors lists, thread residency, etc: // --- 99,117 ----
*** 297,307 **** // Try one round of spinning *before* enqueueing Self // and before going through the awkward and expensive state // transitions. The following spin is strictly optional ... // Note that if we acquire the monitor from an initial spin // we forgo posting JVMTI events and firing DTRACE probes. ! if (Knob_SpinEarly && TrySpin (Self) > 0) { assert(_owner == Self, "invariant"); assert(_recursions == 0, "invariant"); assert(((oop)(object()))->mark() == markOopDesc::encode(this), "invariant"); Self->_Stalled = 0; return; --- 273,283 ---- // Try one round of spinning *before* enqueueing Self // and before going through the awkward and expensive state // transitions. The following spin is strictly optional ... // Note that if we acquire the monitor from an initial spin // we forgo posting JVMTI events and firing DTRACE probes. ! if (TrySpin(Self) > 0) { assert(_owner == Self, "invariant"); assert(_recursions == 0, "invariant"); assert(((oop)(object()))->mark() == markOopDesc::encode(this), "invariant"); Self->_Stalled = 0; return;
*** 459,469 **** // If the _owner is ready but OFFPROC we could use a YieldTo() // operation to donate the remainder of this thread's quantum // to the owner. This has subtle but beneficial affinity // effects. ! if (TrySpin (Self) > 0) { assert(_owner == Self, "invariant"); assert(_succ != Self, "invariant"); assert(_Responsible != Self, "invariant"); return; } --- 435,445 ---- // If the _owner is ready but OFFPROC we could use a YieldTo() // operation to donate the remainder of this thread's quantum // to the owner. This has subtle but beneficial affinity // effects. ! if (TrySpin(Self) > 0) { assert(_owner == Self, "invariant"); assert(_succ != Self, "invariant"); assert(_Responsible != Self, "invariant"); return; }
*** 581,604 **** // Assuming this is not a spurious wakeup we'll normally find _succ == Self. // We can defer clearing _succ until after the spin completes // TrySpin() must tolerate being called with _succ == Self. // Try yet another round of adaptive spinning. ! if ((Knob_SpinAfterFutile & 1) && TrySpin(Self) > 0) break; // We can find that we were unpark()ed and redesignated _succ while // we were spinning. That's harmless. If we iterate and call park(), // park() will consume the event and return immediately and we'll // just spin again. This pattern can repeat, leaving _succ to simply ! // spin on a CPU. Enable Knob_ResetEvent to clear pending unparks(). ! // Alternately, we can sample fired() here, and if set, forgo spinning ! // in the next iteration. - if ((Knob_ResetEvent & 1) && Self->_ParkEvent->fired()) { - Self->_ParkEvent->reset(); - OrderAccess::fence(); - } if (_succ == Self) _succ = NULL; // Invariant: after clearing _succ a thread *must* retry _owner before parking. OrderAccess::fence(); } --- 557,574 ---- // Assuming this is not a spurious wakeup we'll normally find _succ == Self. // We can defer clearing _succ until after the spin completes // TrySpin() must tolerate being called with _succ == Self. // Try yet another round of adaptive spinning. ! if (TrySpin(Self) > 0) break; // We can find that we were unpark()ed and redesignated _succ while // we were spinning. That's harmless. If we iterate and call park(), // park() will consume the event and return immediately and we'll // just spin again. This pattern can repeat, leaving _succ to simply ! // spin on a CPU. if (_succ == Self) _succ = NULL; // Invariant: after clearing _succ a thread *must* retry _owner before parking. OrderAccess::fence(); }
*** 673,685 **** // ReenterI() is a specialized inline form of the latter half of the // contended slow-path from EnterI(). We use ReenterI() only for // monitor reentry in wait(). // ! // In the future we should reconcile EnterI() and ReenterI(), adding ! // Knob_Reset and Knob_SpinAfterFutile support and restructuring the ! // loop accordingly. void ObjectMonitor::ReenterI(Thread * Self, ObjectWaiter * SelfNode) { assert(Self != NULL, "invariant"); assert(SelfNode != NULL, "invariant"); assert(SelfNode->_thread == Self, "invariant"); --- 643,653 ---- // ReenterI() is a specialized inline form of the latter half of the // contended slow-path from EnterI(). We use ReenterI() only for // monitor reentry in wait(). // ! // In the future we should reconcile EnterI() and ReenterI(). void ObjectMonitor::ReenterI(Thread * Self, ObjectWaiter * SelfNode) { assert(Self != NULL, "invariant"); assert(SelfNode != NULL, "invariant"); assert(SelfNode->_thread == Self, "invariant");
*** 927,937 **** #endif for (;;) { assert(THREAD == _owner, "invariant"); - if (Knob_ExitPolicy == 0) { // release semantics: prior loads and stores from within the critical section // must not float (reorder) past the following store that drops the lock. // On SPARC that requires MEMBAR #loadstore|#storestore. // But of course in TSO #loadstore|#storestore is not required. // I'd like to write one of the following: --- 895,904 ----
*** 986,1111 **** // falls to the new owner. // if (!Atomic::replace_if_null(THREAD, &_owner)) { return; } - } else { - if ((intptr_t(_EntryList)|intptr_t(_cxq)) == 0 || _succ != NULL) { - OrderAccess::release_store(&_owner, (void*)NULL); // drop the lock - OrderAccess::storeload(); - // Ratify the previously observed values. - if (_cxq == NULL || _succ != NULL) { - return; - } - - // inopportune interleaving -- the exiting thread (this thread) - // in the fast-exit path raced an entering thread in the slow-enter - // path. - // We have two choices: - // A. Try to reacquire the lock. - // If the CAS() fails return immediately, otherwise - // we either restart/rerun the exit operation, or simply - // fall-through into the code below which wakes a successor. - // B. If the elements forming the EntryList|cxq are TSM - // we could simply unpark() the lead thread and return - // without having set _succ. - if (!Atomic::replace_if_null(THREAD, &_owner)) { - return; - } - } - } guarantee(_owner == THREAD, "invariant"); ObjectWaiter * w = NULL; - int QMode = Knob_QMode; - - if (QMode == 2 && _cxq != NULL) { - // QMode == 2 : cxq has precedence over EntryList. - // Try to directly wake a successor from the cxq. - // If successful, the successor will need to unlink itself from cxq. - w = _cxq; - assert(w != NULL, "invariant"); - assert(w->TState == ObjectWaiter::TS_CXQ, "Invariant"); - ExitEpilog(Self, w); - return; - } - - if (QMode == 3 && _cxq != NULL) { - // Aggressively drain cxq into EntryList at the first opportunity. - // This policy ensure that recently-run threads live at the head of EntryList. - // Drain _cxq into EntryList - bulk transfer. - // First, detach _cxq. - // The following loop is tantamount to: w = swap(&cxq, NULL) - w = _cxq; - for (;;) { - assert(w != NULL, "Invariant"); - ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w); - if (u == w) break; - w = u; - } - assert(w != NULL, "invariant"); - - ObjectWaiter * q = NULL; - ObjectWaiter * p; - for (p = w; p != NULL; p = p->_next) { - guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant"); - p->TState = ObjectWaiter::TS_ENTER; - p->_prev = q; - q = p; - } - - // Append the RATs to the EntryList - // TODO: organize EntryList as a CDLL so we can locate the tail in constant-time. - ObjectWaiter * Tail; - for (Tail = _EntryList; Tail != NULL && Tail->_next != NULL; - Tail = Tail->_next) - /* empty */; - if (Tail == NULL) { - _EntryList = w; - } else { - Tail->_next = w; - w->_prev = Tail; - } - - // Fall thru into code that tries to wake a successor from EntryList - } - - if (QMode == 4 && _cxq != NULL) { - // Aggressively drain cxq into EntryList at the first opportunity. - // This policy ensure that recently-run threads live at the head of EntryList. - - // Drain _cxq into EntryList - bulk transfer. - // First, detach _cxq. - // The following loop is tantamount to: w = swap(&cxq, NULL) - w = _cxq; - for (;;) { - assert(w != NULL, "Invariant"); - ObjectWaiter * u = Atomic::cmpxchg((ObjectWaiter*)NULL, &_cxq, w); - if (u == w) break; - w = u; - } - assert(w != NULL, "invariant"); - - ObjectWaiter * q = NULL; - ObjectWaiter * p; - for (p = w; p != NULL; p = p->_next) { - guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant"); - p->TState = ObjectWaiter::TS_ENTER; - p->_prev = q; - q = p; - } - - // Prepend the RATs to the EntryList - if (_EntryList != NULL) { - q->_next = _EntryList; - _EntryList->_prev = q; - } - _EntryList = w; - - // Fall thru into code that tries to wake a successor from EntryList - } w = _EntryList; if (w != NULL) { // I'd like to write: guarantee (w->_thread != Self). // But in practice an exiting thread may find itself on the EntryList. --- 953,966 ----
*** 1148,1186 **** // and effectively lengthening the critical section. // Invariant: s chases t chases u. // TODO-FIXME: consider changing EntryList from a DLL to a CDLL so // we have faster access to the tail. - if (QMode == 1) { - // QMode == 1 : drain cxq to EntryList, reversing order - // We also reverse the order of the list. - ObjectWaiter * s = NULL; - ObjectWaiter * t = w; - ObjectWaiter * u = NULL; - while (t != NULL) { - guarantee(t->TState == ObjectWaiter::TS_CXQ, "invariant"); - t->TState = ObjectWaiter::TS_ENTER; - u = t->_next; - t->_prev = u; - t->_next = s; - s = t; - t = u; - } - _EntryList = s; - assert(s != NULL, "invariant"); - } else { - // QMode == 0 or QMode == 2 _EntryList = w; ObjectWaiter * q = NULL; ObjectWaiter * p; for (p = w; p != NULL; p = p->_next) { guarantee(p->TState == ObjectWaiter::TS_CXQ, "Invariant"); p->TState = ObjectWaiter::TS_ENTER; p->_prev = q; q = p; } - } // In 1-0 mode we need: ST EntryList; MEMBAR #storestore; ST _owner = NULL // The MEMBAR is satisfied by the release_store() operation in ExitEpilog(). // See if we can abdicate to a spinner instead of waking a thread. --- 1003,1021 ----
*** 1224,1249 **** // Alternately, a Dekker-like mechanism with multiple variables // would suffice: // ST Self->_suspend_equivalent = false // MEMBAR // LD Self_>_suspend_flags - // - // UPDATE 2007-10-6: since I've replaced the native Mutex/Monitor subsystem - // with a more efficient implementation, the need to use "FastHSSEC" has - // decreased. - Dave - bool ObjectMonitor::ExitSuspendEquivalent(JavaThread * jSelf) { - const int Mode = Knob_FastHSSEC; - if (Mode && !jSelf->is_external_suspend()) { - assert(jSelf->is_suspend_equivalent(), "invariant"); - jSelf->clear_suspend_equivalent(); - if (2 == Mode) OrderAccess::storeload(); - if (!jSelf->is_external_suspend()) return false; - // We raced a suspension -- fall thru into the slow path - jSelf->set_suspend_equivalent(); - } return jSelf->handle_special_suspend_equivalent_condition(); } void ObjectMonitor::ExitEpilog(Thread * Self, ObjectWaiter * Wakee) { --- 1059,1070 ----
*** 1253,1263 **** // 1. ST _succ = wakee // 2. membar #loadstore|#storestore; // 2. ST _owner = NULL // 3. unpark(wakee) ! _succ = Knob_SuccEnabled ? Wakee->_thread : NULL; ParkEvent * Trigger = Wakee->_event; // Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again. // The thread associated with Wakee may have grabbed the lock and "Wakee" may be // out-of-scope (non-extant). --- 1074,1084 ---- // 1. ST _succ = wakee // 2. membar #loadstore|#storestore; // 2. ST _owner = NULL // 3. unpark(wakee) ! _succ = Wakee->_thread; ParkEvent * Trigger = Wakee->_event; // Hygiene -- once we've set _owner = NULL we can't safely dereference Wakee again. // The thread associated with Wakee may have grabbed the lock and "Wakee" may be // out-of-scope (non-extant).
*** 1346,1361 **** void ObjectMonitor::check_slow(TRAPS) { assert(THREAD != _owner && !THREAD->is_lock_owned((address) _owner), "must not be owner"); THROW_MSG(vmSymbols::java_lang_IllegalMonitorStateException(), "current thread not owner"); } - static int Adjust(volatile int * adr, int dx) { - int v; - for (v = *adr; Atomic::cmpxchg(v + dx, adr, v) != v; v = *adr) /* empty */; - return v; - } - static void post_monitor_wait_event(EventJavaMonitorWait* event, ObjectMonitor* monitor, jlong notifier_tid, jlong timeout, bool timedout) { --- 1167,1176 ----
*** 1597,1657 **** // 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) { 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 (policy == 1) // or head (policy == 0). // b. push it onto the front of the _cxq (policy == 2). // For now we use (b). ! if (policy != 4) { iterator->TState = ObjectWaiter::TS_ENTER; ! } iterator->_notified = 1; iterator->_notifier_tid = JFR_THREAD_ID(Self); 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) {} ! 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; --- 1412,1445 ---- // 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) { Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notify"); ObjectWaiter * iterator = DequeueWaiter(); if (iterator != NULL) { 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 (policy == 1) // or head (policy == 0). // b. push it onto the front of the _cxq (policy == 2). // For now we use (b). ! iterator->TState = ObjectWaiter::TS_ENTER; ! iterator->_notified = 1; iterator->_notifier_tid = JFR_THREAD_ID(Self); ObjectWaiter * list = _EntryList; if (list != NULL) { assert(list->_prev == NULL, "invariant"); assert(list->TState == ObjectWaiter::TS_ENTER, "invariant"); assert(list != iterator, "invariant"); } ! // prepend to cxq if (list == NULL) { iterator->_next = iterator->_prev = NULL; _EntryList = iterator; } else { iterator->TState = ObjectWaiter::TS_CXQ;
*** 1661,1706 **** if (Atomic::cmpxchg(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::replace_if_null(iterator, &_cxq)) { - 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(); - } // _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 --- 1449,1469 ----
*** 1852,1888 **** // the # of failed spin attempts (or iterations) reaches some threshold. // This takes us into the realm of 1-out-of-N spinning, where we // hold the duration constant but vary the frequency. ctr = _SpinDuration; - if (ctr < Knob_SpinBase) ctr = Knob_SpinBase; if (ctr <= 0) return 0; ! if (Knob_SuccRestrict && _succ != NULL) return 0; ! if (Knob_OState && NotRunnable (Self, (Thread *) _owner)) { ! return 0; ! } ! ! int MaxSpin = Knob_MaxSpinners; ! if (MaxSpin >= 0) { ! if (_Spinner > MaxSpin) { return 0; } - // Slightly racy, but benign ... - Adjust(&_Spinner, 1); - } // We're good to spin ... spin ingress. // CONSIDER: use Prefetch::write() to avoid RTS->RTO upgrades // when preparing to LD...CAS _owner, etc and the CAS is likely // to succeed. ! int hits = 0; ! int msk = 0; ! int caspty = Knob_CASPenalty; ! int oxpty = Knob_OXPenalty; ! int sss = Knob_SpinSetSucc; ! if (sss && _succ == NULL) _succ = Self; Thread * prv = NULL; // There are three ways to exit the following loop: // 1. A successful spin where this thread has acquired the lock. // 2. Spin failure with prejudice --- 1615,1637 ---- // the # of failed spin attempts (or iterations) reaches some threshold. // This takes us into the realm of 1-out-of-N spinning, where we // hold the duration constant but vary the frequency. ctr = _SpinDuration; if (ctr <= 0) return 0; ! if (NotRunnable(Self, (Thread *) _owner)) { return 0; } // We're good to spin ... spin ingress. // CONSIDER: use Prefetch::write() to avoid RTS->RTO upgrades // when preparing to LD...CAS _owner, etc and the CAS is likely // to succeed. ! if (_succ == NULL) { ! _succ = Self; ! } Thread * prv = NULL; // There are three ways to exit the following loop: // 1. A successful spin where this thread has acquired the lock. // 2. Spin failure with prejudice
*** 1901,1936 **** // We periodically check to see if there's a safepoint pending. if ((ctr & 0xFF) == 0) { if (SafepointMechanism::poll(Self)) { goto Abort; // abrupt spin egress } ! if (Knob_UsePause & 1) SpinPause(); ! } ! ! if (Knob_UsePause & 2) SpinPause(); ! ! // Exponential back-off ... Stay off the bus to reduce coherency traffic. ! // This is useful on classic SMP systems, but is of less utility on ! // N1-style CMT platforms. ! // ! // Trade-off: lock acquisition latency vs coherency bandwidth. ! // Lock hold times are typically short. A histogram ! // of successful spin attempts shows that we usually acquire ! // the lock early in the spin. That suggests we want to ! // sample _owner frequently in the early phase of the spin, ! // but then back-off and sample less frequently as the spin ! // progresses. The back-off makes a good citizen on SMP big ! // SMP systems. Oversampling _owner can consume excessive ! // coherency bandwidth. Relatedly, if we _oversample _owner we ! // can inadvertently interfere with the the ST m->owner=null. ! // executed by the lock owner. ! if (ctr & msk) continue; ! ++hits; ! if ((hits & 0xF) == 0) { ! // The 0xF, above, corresponds to the exponent. ! // Consider: (msk+1)|msk ! msk = ((msk << 2)|3) & BackOffMask; } // Probe _owner with TATAS // If this thread observes the monitor transition or flicker // from locked to unlocked to locked, then the odds that this --- 1650,1660 ---- // We periodically check to see if there's a safepoint pending. if ((ctr & 0xFF) == 0) { if (SafepointMechanism::poll(Self)) { goto Abort; // abrupt spin egress } ! SpinPause(); } // Probe _owner with TATAS // If this thread observes the monitor transition or flicker // from locked to unlocked to locked, then the odds that this
*** 1945,1958 **** if (ox == NULL) { ox = (Thread*)Atomic::cmpxchg(Self, &_owner, (void*)NULL); if (ox == NULL) { // The CAS succeeded -- this thread acquired ownership // Take care of some bookkeeping to exit spin state. ! if (sss && _succ == Self) { _succ = NULL; } - if (MaxSpin > 0) Adjust(&_Spinner, -1); // Increase _SpinDuration : // The spin was successful (profitable) so we tend toward // longer spin attempts in the future. // CONSIDER: factor "ctr" into the _SpinDuration adjustment. --- 1669,1681 ---- if (ox == NULL) { ox = (Thread*)Atomic::cmpxchg(Self, &_owner, (void*)NULL); if (ox == NULL) { // The CAS succeeded -- this thread acquired ownership // Take care of some bookkeeping to exit spin state. ! if (_succ == Self) { _succ = NULL; } // Increase _SpinDuration : // The spin was successful (profitable) so we tend toward // longer spin attempts in the future. // CONSIDER: factor "ctr" into the _SpinDuration adjustment.
*** 1966,2002 **** } return 1; } // The CAS failed ... we can take any of the following actions: ! // * penalize: ctr -= Knob_CASPenalty // * exit spin with prejudice -- goto Abort; // * exit spin without prejudice. // * Since CAS is high-latency, retry again immediately. prv = ox; ! if (caspty == -2) break; ! if (caspty == -1) goto Abort; ! ctr -= caspty; ! continue; } // Did lock ownership change hands ? if (ox != prv && prv != NULL) { ! if (oxpty == -2) break; ! if (oxpty == -1) goto Abort; ! ctr -= oxpty; } prv = ox; // Abort the spin if the owner is not executing. // The owner must be executing in order to drop the lock. // Spinning while the owner is OFFPROC is idiocy. // Consider: ctr -= RunnablePenalty ; ! if (Knob_OState && NotRunnable (Self, ox)) { goto Abort; } ! if (sss && _succ == NULL) _succ = Self; } // Spin failed with prejudice -- reduce _SpinDuration. // TODO: Use an AIMD-like policy to adjust _SpinDuration. // AIMD is globally stable. --- 1689,1722 ---- } return 1; } // The CAS failed ... we can take any of the following actions: ! // * penalize: ctr -= CASPenalty // * exit spin with prejudice -- goto Abort; // * exit spin without prejudice. // * Since CAS is high-latency, retry again immediately. prv = ox; ! goto Abort; } // Did lock ownership change hands ? if (ox != prv && prv != NULL) { ! goto Abort; } prv = ox; // Abort the spin if the owner is not executing. // The owner must be executing in order to drop the lock. // Spinning while the owner is OFFPROC is idiocy. // Consider: ctr -= RunnablePenalty ; ! if (NotRunnable(Self, ox)) { goto Abort; } ! if (_succ == NULL) { ! _succ = Self; ! } } // Spin failed with prejudice -- reduce _SpinDuration. // TODO: Use an AIMD-like policy to adjust _SpinDuration. // AIMD is globally stable.
*** 2010,2021 **** _SpinDuration = x; } } Abort: ! if (MaxSpin >= 0) Adjust(&_Spinner, -1); ! if (sss && _succ == Self) { _succ = NULL; // Invariant: after setting succ=null a contending thread // must recheck-retry _owner before parking. This usually happens // in the normal usage of TrySpin(), but it's safest // to make TrySpin() as foolproof as possible. --- 1730,1740 ---- _SpinDuration = x; } } Abort: ! if (_succ == Self) { _succ = NULL; // Invariant: after setting succ=null a contending thread // must recheck-retry _owner before parking. This usually happens // in the normal usage of TrySpin(), but it's safest // to make TrySpin() as foolproof as possible.
*** 2202,2307 **** #undef NEWPERFCOUNTER #undef NEWPERFVARIABLE } } - static char * kvGet(char * kvList, const char * Key) { - if (kvList == NULL) return NULL; - size_t n = strlen(Key); - char * Search; - for (Search = kvList; *Search; Search += strlen(Search) + 1) { - if (strncmp (Search, Key, n) == 0) { - if (Search[n] == '=') return Search + n + 1; - if (Search[n] == 0) return(char *) "1"; - } - } - return NULL; - } - - static int kvGetInt(char * kvList, const char * Key, int Default) { - char * v = kvGet(kvList, Key); - int rslt = v ? ::strtol(v, NULL, 0) : Default; - if (Knob_ReportSettings && v != NULL) { - tty->print_cr("INFO: SyncKnob: %s %d(%d)", Key, rslt, Default) ; - tty->flush(); - } - return rslt; - } - void ObjectMonitor::DeferredInitialize() { if (InitDone > 0) return; if (Atomic::cmpxchg (-1, &InitDone, 0) != 0) { while (InitDone != 1) /* empty */; return; } // One-shot global initialization ... // The initialization is idempotent, so we don't need locks. // In the future consider doing this via os::init_2(). - // SyncKnobs consist of <Key>=<Value> pairs in the style - // of environment variables. Start by converting ':' to NUL. - - if (SyncKnobs == NULL) SyncKnobs = ""; ! size_t sz = strlen(SyncKnobs); ! char * knobs = (char *) os::malloc(sz + 2, mtInternal); ! if (knobs == NULL) { ! vm_exit_out_of_memory(sz + 2, OOM_MALLOC_ERROR, "Parse SyncKnobs"); ! guarantee(0, "invariant"); ! } ! strcpy(knobs, SyncKnobs); ! knobs[sz+1] = 0; ! for (char * p = knobs; *p; p++) { ! if (*p == ':') *p = 0; ! } ! ! #define SETKNOB(x) { Knob_##x = kvGetInt(knobs, #x, Knob_##x); } ! SETKNOB(ReportSettings); ! SETKNOB(ExitRelease); ! SETKNOB(InlineNotify); ! SETKNOB(Verbose); ! SETKNOB(VerifyInUse); ! SETKNOB(VerifyMatch); ! SETKNOB(FixedSpin); ! SETKNOB(SpinLimit); ! SETKNOB(SpinBase); ! SETKNOB(SpinBackOff); ! SETKNOB(CASPenalty); ! SETKNOB(OXPenalty); ! SETKNOB(SpinSetSucc); ! SETKNOB(SuccEnabled); ! SETKNOB(SuccRestrict); ! SETKNOB(Penalty); ! SETKNOB(Bonus); ! SETKNOB(BonusB); ! SETKNOB(Poverty); ! SETKNOB(SpinAfterFutile); ! SETKNOB(UsePause); ! SETKNOB(SpinEarly); ! SETKNOB(OState); ! SETKNOB(MaxSpinners); ! SETKNOB(PreSpin); ! SETKNOB(ExitPolicy); ! SETKNOB(QMode); ! SETKNOB(ResetEvent); ! SETKNOB(MoveNotifyee); ! SETKNOB(FastHSSEC); ! #undef SETKNOB ! ! if (os::is_MP()) { ! BackOffMask = (1 << Knob_SpinBackOff) - 1; ! if (Knob_ReportSettings) { ! tty->print_cr("INFO: BackOffMask=0x%X", BackOffMask); ! } ! // CONSIDER: BackOffMask = ROUNDUP_NEXT_POWER2 (ncpus-1) ! } else { Knob_SpinLimit = 0; - Knob_SpinBase = 0; Knob_PreSpin = 0; Knob_FixedSpin = -1; } - os::free(knobs); OrderAccess::fence(); InitDone = 1; } --- 1921,1946 ---- #undef NEWPERFCOUNTER #undef NEWPERFVARIABLE } } void ObjectMonitor::DeferredInitialize() { if (InitDone > 0) return; if (Atomic::cmpxchg (-1, &InitDone, 0) != 0) { while (InitDone != 1) /* empty */; return; } // One-shot global initialization ... // The initialization is idempotent, so we don't need locks. // In the future consider doing this via os::init_2(). ! if (!os::is_MP()) { Knob_SpinLimit = 0; Knob_PreSpin = 0; Knob_FixedSpin = -1; } OrderAccess::fence(); InitDone = 1; }
< prev index next >