Пример #1
0
    bool LockManager::unlock(LockRequest* request) {
        invariant(request->lock);

        // Fast path for decrementing multiple references of the same lock. It is safe to do this
        // without locking, because 1) all calls for the same lock request must be done on the same
        // thread and 2) if there are lock requests hanging of a given LockHead, then this lock
        // will never disappear.
        request->recursiveCount--;
        if ((request->status == LockRequest::STATUS_GRANTED) && (request->recursiveCount > 0)) {
            return false;
        }

        LockHead* lock = request->lock;

        LockBucket* bucket = _getBucket(lock->resourceId);
        scoped_spinlock scopedLock(bucket->mutex);

        invariant(lock->grantedQueue != NULL);
        invariant(lock->grantedModes != 0);

        if (request->status == LockRequest::STATUS_WAITING) {
            // This cancels a pending lock request
            invariant(request->recursiveCount == 0);

            lock->removeFromConflictQueue(request);
            lock->changeConflictModeCount(request->mode, LockHead::Decrement);
        }
        else if (request->status == LockRequest::STATUS_CONVERTING) {
            // This cancels a pending convert request
            invariant(request->recursiveCount > 0);

            // Lock only goes from GRANTED to CONVERTING, so cancelling the conversion request
            // brings it back to the previous granted mode.
            request->status = LockRequest::STATUS_GRANTED;

            lock->conversionsCount--;
            lock->changeGrantedModeCount(request->convertMode, LockHead::Decrement);

            request->convertMode = MODE_NONE;

            _onLockModeChanged(lock, lock->grantedCounts[request->convertMode] == 0);
        }
        else if (request->status == LockRequest::STATUS_GRANTED) {
            // This releases a currently held lock and is the most common path, so it should be
            // as efficient as possible.
            invariant(request->recursiveCount == 0);

            // Remove from the granted list
            lock->removeFromGrantedQueue(request);
            lock->changeGrantedModeCount(request->mode, LockHead::Decrement);

            _onLockModeChanged(lock, lock->grantedCounts[request->mode] == 0);
        }
        else {
            // Invalid request status
            invariant(false);
        }

        return (request->recursiveCount == 0);
    }
Пример #2
0
    LockResult LockManager::lock(const ResourceId& resId, LockRequest* request, LockMode mode) {
        dassert(mode > MODE_NONE);

        // Fast path for acquiring the same lock multiple times in modes, which are already covered
        // by the current mode. It is safe to do this without locking, because 1) all calls for the
        // same lock request must be done on the same thread and 2) if there are lock requests
        // hanging off a given LockHead, then this lock will never disappear.
        if ((LockConflictsTable[request->mode] | LockConflictsTable[mode]) == 
                LockConflictsTable[request->mode]) {
            request->recursiveCount++;
            return LOCK_OK;
        }

        // TODO: For the time being we do not need conversions between unrelated lock modes (i.e.,
        // modes which both add and remove to the conflicts set), so these are not implemented yet
        // (e.g., S -> IX).
        invariant((LockConflictsTable[request->mode] | LockConflictsTable[mode]) == 
                LockConflictsTable[mode]);

        LockBucket* bucket = _getBucket(resId);
        scoped_spinlock scopedLock(bucket->mutex);

        LockHead* lock;

        LockHeadMap::iterator it = bucket->data.find(resId);
        if (it == bucket->data.end()) {
            // Lock is free (not on the map)
            invariant(request->status == LockRequest::STATUS_NEW);

            lock = new LockHead(resId);
            bucket->data.insert(LockHeadPair(resId, lock));
        }
        else {
            // Lock is not free
            lock = it->second;
        }

        // Sanity check if requests are being reused
        invariant(request->lock == NULL || request->lock == lock);

        request->lock = lock;
        request->recursiveCount++;

        if (request->status == LockRequest::STATUS_NEW) {
            invariant(request->recursiveCount == 1);

            // New lock request
            if (conflicts(mode, lock->grantedModes)) {
                request->status = LockRequest::STATUS_WAITING;
                request->mode = mode;
                request->convertMode = MODE_NONE;

                // Put it on the conflict queue. This is the place where various policies could be
                // applied for where in the wait queue does a request go.
                lock->addToConflictQueue(request);
                lock->changeConflictModeCount(mode, LockHead::Increment);

                return LOCK_WAITING;
            }
            else {  // No conflict, new request
                request->status = LockRequest::STATUS_GRANTED;
                request->mode = mode;
                request->convertMode = MODE_NONE;

                lock->addToGrantedQueue(request);
                lock->changeGrantedModeCount(mode, LockHead::Increment);

                return LOCK_OK;
            }
        }
        else {
            // If we are here, we already hold the lock in some mode. In order to keep it simple,
            // we do not allow requesting a conversion while a lock is already waiting or pending
            // conversion, hence the assertion below.
            invariant(request->status == LockRequest::STATUS_GRANTED);
            invariant(request->recursiveCount > 1);
            invariant(request->mode != mode);

            // Construct granted mask without our current mode, so that it is not counted as
            // conflicting
            uint32_t grantedModesWithoutCurrentRequest = 0;

            // We start the counting at 1 below, because LockModesCount also includes MODE_NONE
            // at position 0, which can never be acquired/granted.
            for (uint32_t i = 1; i < LockModesCount; i++) {
                const uint32_t currentRequestHolds =
                                    (request->mode == static_cast<LockMode>(i) ? 1 : 0);

                if (lock->grantedCounts[i] > currentRequestHolds) {
                    grantedModesWithoutCurrentRequest |= modeMask(static_cast<LockMode>(i));
                }
            }

            // This check favours conversion requests over pending requests. For example:
            //
            // T1 requests lock L in IS
            // T2 requests lock L in X
            // T1 then upgrades L from IS -> S
            //
            // Because the check does not look into the conflict modes bitmap, it will grant L to
            // T1 in S mode, instead of block, which would otherwise cause deadlock.
            if (conflicts(mode, grantedModesWithoutCurrentRequest)) {
                request->status = LockRequest::STATUS_CONVERTING;
                request->convertMode = mode;

                lock->conversionsCount++;
                lock->changeGrantedModeCount(request->convertMode, LockHead::Increment);

                return LOCK_WAITING;
            }
            else {  // No conflict, existing request
                lock->changeGrantedModeCount(mode, LockHead::Increment);
                lock->changeGrantedModeCount(request->mode, LockHead::Decrement);
                request->mode = mode;

                return LOCK_OK;
            }
        }
    }