bool QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount) { int64_t end = aOffset + aCount; QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); MutexAutoLock lock(quotaManager->mQuotaMutex); if (mSize >= end || !mOriginInfo) { return true; } int64_t newUsage = mOriginInfo->mUsage - mSize + end; if (newUsage > mOriginInfo->mLimit) { // This will block the thread, but it will also drop the mutex while // waiting. The mutex will be reacquired again when the waiting is finished. if (!quotaManager->LockedQuotaIsLifted()) { return false; } // Threads raced, the origin info removal has been done by some other // thread. if (!mOriginInfo) { // The other thread could allocate more space. if (end > mSize) { mSize = end; } return true; } nsCString origin = mOriginInfo->mOrigin; mOriginInfo->LockedClearOriginInfos(); NS_ASSERTION(!mOriginInfo, "Should have cleared in LockedClearOriginInfos!"); quotaManager->mOriginInfos.Remove(origin); // Some other thread could increase the size without blocking (increasing // the origin usage without hitting the limit), but no more than this one. NS_ASSERTION(mSize < end, "This shouldn't happen!"); mSize = end; return true; } mOriginInfo->mUsage = newUsage; mSize = end; return true; }
bool QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount) { int64_t end = aOffset + aCount; QuotaManager* quotaManager = QuotaManager::Get(); NS_ASSERTION(quotaManager, "Shouldn't be null!"); MutexAutoLock lock(quotaManager->mQuotaMutex); if (mSize >= end || !mOriginInfo) { return true; } GroupInfo* groupInfo = mOriginInfo->mGroupInfo; if (groupInfo->IsForPersistentStorage()) { uint64_t newUsage = mOriginInfo->mUsage - mSize + end; if (newUsage > mOriginInfo->mLimit) { // This will block the thread, but it will also drop the mutex while // waiting. The mutex will be reacquired again when the waiting is // finished. if (!quotaManager->LockedQuotaIsLifted()) { return false; } // Threads raced, the origin info removal has been done by some other // thread. if (!mOriginInfo) { // The other thread could allocate more space. if (end > mSize) { mSize = end; } return true; } nsCString group = mOriginInfo->mGroupInfo->mGroup; nsCString origin = mOriginInfo->mOrigin; mOriginInfo->LockedClearOriginInfos(); NS_ASSERTION(!mOriginInfo, "Should have cleared in LockedClearOriginInfos!"); quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_PERSISTENT, group, origin); // Some other thread could increase the size without blocking (increasing // the origin usage without hitting the limit), but no more than this one. NS_ASSERTION(mSize < end, "This shouldn't happen!"); mSize = end; return true; } mOriginInfo->mUsage = newUsage; groupInfo->mUsage = groupInfo->mUsage - mSize + end; mSize = end; return true; } NS_ASSERTION(groupInfo->mPersistenceType == PERSISTENCE_TYPE_TEMPORARY, "Huh?"); uint64_t delta = end - mSize; uint64_t newUsage = mOriginInfo->mUsage + delta; // Temporary storage has no limit for origin usage (there's a group and the // global limit though). uint64_t newGroupUsage = groupInfo->mUsage + delta; // Temporary storage has a hard limit for group usage (20 % of the global // limit). if (newGroupUsage > quotaManager->GetGroupLimit()) { return false; } uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta; if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) { // This will block the thread without holding the lock while waitting. nsAutoTArray<OriginInfo*, 10> originInfos; uint64_t sizeToBeFreed = quotaManager->LockedCollectOriginsForEviction(delta, originInfos); if (!sizeToBeFreed) { return false; } NS_ASSERTION(sizeToBeFreed >= delta, "Huh?"); { MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); for (uint32_t i = 0; i < originInfos.Length(); i++) { quotaManager->DeleteTemporaryFilesForOrigin(originInfos[i]->mOrigin); } } // Relocked. NS_ASSERTION(mOriginInfo, "How come?!"); nsTArray<nsCString> origins; for (uint32_t i = 0; i < originInfos.Length(); i++) { OriginInfo* originInfo = originInfos[i]; NS_ASSERTION(originInfo != mOriginInfo, "Deleted itself!"); nsCString group = originInfo->mGroupInfo->mGroup; nsCString origin = originInfo->mOrigin; quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY, group, origin); #ifdef DEBUG originInfos[i] = nullptr; #endif origins.AppendElement(origin); } // We unlocked and relocked several times so we need to recompute all the // essential variables and recheck the group limit. delta = end - mSize; newUsage = mOriginInfo->mUsage + delta; newGroupUsage = groupInfo->mUsage + delta; if (newGroupUsage > quotaManager->GetGroupLimit()) { // Unfortunately some other thread increased the group usage in the // meantime and we are not below the group limit anymore. // However, the origin eviction must be finalized in this case too. MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); quotaManager->FinalizeOriginEviction(origins); return false; } newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta; NS_ASSERTION(newTemporaryStorageUsage <= quotaManager->mTemporaryStorageLimit, "How come?!"); // Ok, we successfully freed enough space and the operation can continue // without throwing the quota error. mOriginInfo->mUsage = newUsage; groupInfo->mUsage = newGroupUsage; quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;; // Some other thread could increase the size in the meantime, but no more // than this one. NS_ASSERTION(mSize < end, "This shouldn't happen!"); mSize = end; // Finally, release IO thread only objects and allow next synchronized // ops for the evicted origins. MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); quotaManager->FinalizeOriginEviction(origins); return true; } mOriginInfo->mUsage = newUsage; groupInfo->mUsage = newGroupUsage; quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage; mSize = end; return true; }