Example #1
0
/**
 * 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);
		}
	}
}
Example #3
0
/*
 * 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);
}
Example #4
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;
}