/** * Try to get the lock * This method will return immediately */ bool Mutex::lock_try(double sec) { #if defined(_SYS_MSVC_) || defined(_SYS_MINGW_) || defined(_SYS_CYGWIN_) || defined(_SYS_MACOSX_) assert(sec >= 0.0); if (lock_try()) return true; double end = time() + sec; Thread::yield(); uint32_t wcnt = 0; while(!lock_try()) { if (time() > end) return false; if (wcnt > LOCKBUSYLOOP) { Thread::chill(); } else { Thread::yield(); wcnt++; } } return true; #else assert(sec >= 0.0); pthread_mutex_t* pmutex = (pthread_mutex_t*)opq_; struct ::timeval tv; struct ::timespec ts; if (gettimeofday(&tv, NULL) == 0) { double integ; double fract = std::modf(sec, &integ); ts.tv_sec = tv.tv_sec + (time_t)integ; ts.tv_nsec = (long)(tv.tv_usec * 1000.0 + fract * 999999000); if (ts.tv_nsec >= 1000000000) { ts.tv_nsec -= 1000000000; ts.tv_sec++; } } else { ts.tv_sec = std::time(NULL) + 1; ts.tv_nsec = 0; } int32_t ecode = pthread_mutex_timedlock(pmutex, &ts); if (ecode == 0) return true; if (ecode != ETIMEDOUT) throw std::runtime_error("pthread_mutex_timedlock"); return false; #endif }
/* * When we apply priority inheritance, we must grab the owner's thread lock * while already holding the waiter's thread lock. If both thread locks are * turnstile locks, this can lead to deadlock: while we hold L1 and try to * grab L2, some unrelated thread may be applying priority inheritance to * some other blocking chain, holding L2 and trying to grab L1. The most * obvious solution -- do a lock_try() for the owner lock -- isn't quite * sufficient because it can cause livelock: each thread may hold one lock, * try to grab the other, fail, bail out, and try again, looping forever. * To prevent livelock we must define a winner, i.e. define an arbitrary * lock ordering on the turnstile locks. For simplicity we declare that * virtual address order defines lock order, i.e. if L1 < L2, then the * correct lock ordering is L1, L2. Thus the thread that holds L1 and * wants L2 should spin until L2 is available, but the thread that holds * L2 and can't get L1 on the first try must drop L2 and return failure. * Moreover, the losing thread must not reacquire L2 until the winning * thread has had a chance to grab it; to ensure this, the losing thread * must grab L1 after dropping L2, thus spinning until the winner is done. * Complicating matters further, note that the owner's thread lock pointer * can change (i.e. be pointed at a different lock) while we're trying to * grab it. If that happens, we must unwind our state and try again. * * On success, returns 1 with both locks held. * On failure, returns 0 with neither lock held. */ static int turnstile_interlock(lock_t *wlp, lock_t *volatile *olpp) { ASSERT(LOCK_HELD(wlp)); for (;;) { volatile lock_t *olp = *olpp; /* * If the locks are identical, there's nothing to do. */ if (olp == wlp) return (1); if (lock_try((lock_t *)olp)) { /* * If 'olp' is still the right lock, return success. * Otherwise, drop 'olp' and try the dance again. */ if (olp == *olpp) return (1); lock_clear((lock_t *)olp); } else { hrtime_t spin_time = 0; /* * If we're grabbing the locks out of order, we lose. * Drop the waiter's lock, and then grab and release * the owner's lock to ensure that we won't retry * until the winner is done (as described above). */ if (olp >= (lock_t *)turnstile_table && olp < wlp) { lock_clear(wlp); lock_set((lock_t *)olp); lock_clear((lock_t *)olp); return (0); } /* * We're grabbing the locks in the right order, * so spin until the owner's lock either becomes * available or spontaneously changes. */ spin_time = LOCKSTAT_START_TIME(LS_TURNSTILE_INTERLOCK_SPIN); while (olp == *olpp && LOCK_HELD(olp)) { if (panicstr) return (1); SMT_PAUSE(); } LOCKSTAT_RECORD_TIME(LS_TURNSTILE_INTERLOCK_SPIN, olp, spin_time); } } }
/* * mutex_vector_tryenter() is called from the assembly mutex_tryenter() * routine if the lock is held or is not of type MUTEX_ADAPTIVE. */ int mutex_vector_tryenter(mutex_impl_t *lp) { int s; if (MUTEX_TYPE_ADAPTIVE(lp)) return (0); /* we already tried in assembly */ if (!MUTEX_TYPE_SPIN(lp)) { mutex_panic("mutex_tryenter: bad mutex", lp); return (0); } s = splr(lp->m_spin.m_minspl); if (lock_try(&lp->m_spin.m_spinlock)) { lp->m_spin.m_oldspl = (ushort_t)s; return (1); } splx(s); return (0); }
/* * handler for APIC Error interrupt. Just print a warning and continue */ int apic_error_intr() { uint_t error0, error1, error; uint_t i; /* * We need to write before read as per 7.4.17 of system prog manual. * We do both and or the results to be safe */ error0 = apic_reg_ops->apic_read(APIC_ERROR_STATUS); apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0); error1 = apic_reg_ops->apic_read(APIC_ERROR_STATUS); error = error0 | error1; /* * Clear the APIC error status (do this on all cpus that enter here) * (two writes are required due to the semantics of accessing the * error status register.) */ apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0); apic_reg_ops->apic_write(APIC_ERROR_STATUS, 0); /* * Prevent more than 1 CPU from handling error interrupt causing * double printing (interleave of characters from multiple * CPU's when using prom_printf) */ if (lock_try(&apic_error_lock) == 0) return (error ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); if (error) { #if DEBUG if (apic_debug) debug_enter("pcplusmp: APIC Error interrupt received"); #endif /* DEBUG */ if (apic_panic_on_apic_error) cmn_err(CE_PANIC, "APIC Error interrupt on CPU %d. Status = %x", psm_get_cpu_id(), error); else { if ((error & ~APIC_CS_ERRORS) == 0) { /* cksum error only */ apic_error |= APIC_ERR_APIC_ERROR; apic_apic_error |= error; apic_num_apic_errors++; apic_num_cksum_errors++; } else { /* * prom_printf is the best shot we have of * something which is problem free from * high level/NMI type of interrupts */ prom_printf("APIC Error interrupt on CPU %d. " "Status 0 = %x, Status 1 = %x\n", psm_get_cpu_id(), error0, error1); apic_error |= APIC_ERR_APIC_ERROR; apic_apic_error |= error; apic_num_apic_errors++; for (i = 0; i < apic_error_display_delay; i++) { tenmicrosec(); } /* * provide more delay next time limited to * roughly 1 clock tick time */ if (apic_error_display_delay < 500) apic_error_display_delay *= 2; } } lock_clear(&apic_error_lock); return (DDI_INTR_CLAIMED); } else { lock_clear(&apic_error_lock); return (DDI_INTR_UNCLAIMED); } }
StatusWith<DistLockManager::ScopedDistLock> LegacyDistLockManager::lock( OperationContext* txn, StringData name, StringData whyMessage, milliseconds waitFor, milliseconds lockTryInterval) { auto distLock = stdx::make_unique<DistributedLock>(_configServer, name.toString()); { stdx::lock_guard<stdx::mutex> sl(_mutex); if (_isStopped) { return Status(ErrorCodes::LockBusy, "legacy distlock manager is stopped"); } if (_pingerEnabled) { auto pingStatus = _pinger->startPing(*(distLock.get()), kDefaultPingInterval); if (!pingStatus.isOK()) { return pingStatus; } } } auto lastStatus = Status(ErrorCodes::LockBusy, str::stream() << "timed out waiting for " << name); Timer timer; Timer msgTimer; while (waitFor <= milliseconds::zero() || milliseconds(timer.millis()) < waitFor) { bool acquired = false; BSONObj lockDoc; try { acquired = distLock->lock_try( whyMessage.toString(), &lockDoc, durationCount<Seconds>(kDefaultSocketTimeout)); if (!acquired) { lastStatus = Status(ErrorCodes::LockBusy, str::stream() << "Lock for " << whyMessage << " is taken."); } } catch (const LockException& lockExcep) { OID needUnlockID(lockExcep.getMustUnlockID()); if (needUnlockID.isSet()) { _pinger->addUnlockOID(needUnlockID); } lastStatus = lockExcep.toStatus(); } catch (...) { lastStatus = exceptionToStatus(); } if (acquired) { verify(!lockDoc.isEmpty()); auto locksTypeResult = LocksType::fromBSON(lockDoc); if (!locksTypeResult.isOK()) { return StatusWith<ScopedDistLock>( ErrorCodes::UnsupportedFormat, str::stream() << "error while parsing lock document: " << lockDoc << " : " << locksTypeResult.getStatus().toString()); } const LocksType& lock = locksTypeResult.getValue(); dassert(lock.isLockIDSet()); { stdx::lock_guard<stdx::mutex> sl(_mutex); _lockMap.insert(std::make_pair(lock.getLockID(), std::move(distLock))); } return ScopedDistLock(txn, lock.getLockID(), this); } if (waitFor == milliseconds::zero()) break; if (lastStatus != ErrorCodes::LockBusy) { return lastStatus; } // Periodically message for debugging reasons if (msgTimer.seconds() > 10) { log() << "waited " << timer.seconds() << "s for distributed lock " << name << " for " << whyMessage << ": " << lastStatus.toString(); msgTimer.reset(); } milliseconds timeRemaining = std::max(milliseconds::zero(), waitFor - milliseconds(timer.millis())); sleepFor(std::min(lockTryInterval, timeRemaining)); } return lastStatus; }