DeadlockDetector::DeadlockDetector(const LockManager& lockMgr, const Locker* initialLocker) : _lockMgr(lockMgr), _initialLockerId(initialLocker->getId()), _foundCycle(false) { const ResourceId resId = initialLocker->getWaitingResource(); // If there is no resource waiting there is nothing to do if (resId.isValid()) { _queue.push_front(UnprocessedNode(_initialLockerId, resId)); } }
bool ResourceTable::markPublicImpl(const ResourceNameRef& name, const ResourceId& resId, const SourceLine& source, const char16_t* validChars) { if (!name.package.empty() && name.package != mPackage) { Logger::error(source) << "resource '" << name << "' has incompatible package. Must be '" << mPackage << "'." << std::endl; return false; } auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); if (badCharIter != name.entry.end()) { Logger::error(source) << "resource '" << name << "' has invalid entry name '" << name.entry << "'. Invalid character '" << StringPiece16(badCharIter, 1) << "'." << std::endl; return false; } std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type); if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && type->typeId != resId.typeId()) { Logger::error(source) << "trying to make resource '" << name << "' public with ID " << resId << " but type '" << type->type << "' already has ID " << std::hex << type->typeId << std::dec << "." << std::endl; return false; } std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry); if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && entry->entryId != resId.entryId()) { Logger::error(source) << "trying to make resource '" << name << "' public with ID " << resId << " but resource already has ID " << ResourceId(mPackageId, type->typeId, entry->entryId) << "." << std::endl; return false; } type->publicStatus.isPublic = true; entry->publicStatus.isPublic = true; entry->publicStatus.source = source; if (resId.isValid()) { type->typeId = resId.typeId(); entry->entryId = resId.entryId(); } return true; }
bool ResourceTable::addResourceImpl(const ResourceNameRef& name, const ResourceId& resId, const ConfigDescription& config, const SourceLine& source, std::unique_ptr<Value> value, const char16_t* validChars) { if (!name.package.empty() && name.package != mPackage) { Logger::error(source) << "resource '" << name << "' has incompatible package. Must be '" << mPackage << "'." << std::endl; return false; } auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, validChars); if (badCharIter != name.entry.end()) { Logger::error(source) << "resource '" << name << "' has invalid entry name '" << name.entry << "'. Invalid character '" << StringPiece16(badCharIter, 1) << "'." << std::endl; return false; } std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type); if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && type->typeId != resId.typeId()) { Logger::error(source) << "trying to add resource '" << name << "' with ID " << resId << " but type '" << type->type << "' already has ID " << std::hex << type->typeId << std::dec << "." << std::endl; return false; } std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry); if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && entry->entryId != resId.entryId()) { Logger::error(source) << "trying to add resource '" << name << "' with ID " << resId << " but resource already has ID " << ResourceId(mPackageId, type->typeId, entry->entryId) << "." << std::endl; return false; } const auto endIter = std::end(entry->values); auto iter = std::lower_bound(std::begin(entry->values), endIter, config, compareConfigs); if (iter == endIter || iter->config != config) { // This resource did not exist before, add it. entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) }); } else { int collisionResult = defaultCollisionHandler(*iter->value, *value); if (collisionResult > 0) { // Take the incoming value. *iter = ResourceConfigValue{ config, source, {}, std::move(value) }; } else if (collisionResult == 0) { Logger::error(source) << "duplicate value for resource '" << name << "' " << "with config '" << iter->config << "'." << std::endl; Logger::error(iter->source) << "resource previously defined here." << std::endl; return false; } } if (resId.isValid()) { type->typeId = resId.typeId(); entry->entryId = resId.entryId(); } return true; }
void DeadlockDetector::_processNextNode(const UnprocessedNode& node) { // Locate the request LockManager::LockBucket* bucket = _lockMgr._getBucket(node.resId); SimpleMutex::scoped_lock scopedLock(bucket->mutex); LockManager::LockHeadMap::const_iterator iter = bucket->data.find(node.resId); if (iter == bucket->data.end()) { return; } const LockHead* lock = iter->second; LockRequest* request = lock->findRequest(node.lockerId); // It is possible that a request which was thought to be waiting suddenly became // granted, so check that before proceeding if (!request || (request->status == LockRequest::STATUS_GRANTED)) { return; } std::pair<WaitForGraph::iterator, bool> val = _graph.insert(WaitForGraphPair(node.lockerId, Edges(node.resId))); if (!val.second) { // We already saw this locker id, which means we have a cycle. if (!_foundCycle) { _foundCycle = (node.lockerId == _initialLockerId); } return; } Edges& edges = val.first->second; bool seen = false; for (LockRequest* it = lock->grantedQueueEnd; it != NULL; it = it->prev) { // We can't conflict with ourselves if (it == request) { seen = true; continue; } // If we are a regular conflicting request, both granted and conversion modes need to // be checked for conflict, since conversions will be granted first. if (request->status == LockRequest::STATUS_WAITING) { if (conflicts(request->mode, modeMask(it->mode)) || conflicts(request->mode, modeMask(it->convertMode))) { const LockerId lockerId = it->locker->getId(); const ResourceId waitResId = it->locker->getWaitingResource(); if (waitResId.isValid()) { _queue.push_front(UnprocessedNode(lockerId, waitResId)); edges.owners.push_back(lockerId); } } continue; } // If we are a conversion request, only requests, which are before us need to be // accounted for. invariant(request->status == LockRequest::STATUS_CONVERTING); if (conflicts(request->convertMode, modeMask(it->mode)) || (seen && conflicts(request->convertMode, modeMask(it->convertMode)))) { const LockerId lockerId = it->locker->getId(); const ResourceId waitResId = it->locker->getWaitingResource(); if (waitResId.isValid()) { _queue.push_front(UnprocessedNode(lockerId, waitResId)); edges.owners.push_back(lockerId); } } } // All conflicting waits, which would be granted before us for (LockRequest* it = request->prev; (request->status == LockRequest::STATUS_WAITING) && (it != NULL); it = it->prev) { // We started from the previous element, so we should never see ourselves invariant(it != request); if (conflicts(request->mode, modeMask(it->mode))) { const LockerId lockerId = it->locker->getId(); const ResourceId waitResId = it->locker->getWaitingResource(); if (waitResId.isValid()) { _queue.push_front(UnprocessedNode(lockerId, waitResId)); edges.owners.push_back(lockerId); } } } }