1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/quota/QuotaObject.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,423 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "QuotaObject.h" 1.11 + 1.12 +#include "QuotaManager.h" 1.13 +#include "Utilities.h" 1.14 + 1.15 +USING_QUOTA_NAMESPACE 1.16 + 1.17 +void 1.18 +QuotaObject::AddRef() 1.19 +{ 1.20 + QuotaManager* quotaManager = QuotaManager::Get(); 1.21 + if (!quotaManager) { 1.22 + NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); 1.23 + 1.24 + ++mRefCnt; 1.25 + 1.26 + return; 1.27 + } 1.28 + 1.29 + MutexAutoLock lock(quotaManager->mQuotaMutex); 1.30 + 1.31 + ++mRefCnt; 1.32 +} 1.33 + 1.34 +void 1.35 +QuotaObject::Release() 1.36 +{ 1.37 + QuotaManager* quotaManager = QuotaManager::Get(); 1.38 + if (!quotaManager) { 1.39 + NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); 1.40 + 1.41 + nsrefcnt count = --mRefCnt; 1.42 + if (count == 0) { 1.43 + mRefCnt = 1; 1.44 + delete this; 1.45 + } 1.46 + 1.47 + return; 1.48 + } 1.49 + 1.50 + { 1.51 + MutexAutoLock lock(quotaManager->mQuotaMutex); 1.52 + 1.53 + --mRefCnt; 1.54 + 1.55 + if (mRefCnt > 0) { 1.56 + return; 1.57 + } 1.58 + 1.59 + if (mOriginInfo) { 1.60 + mOriginInfo->mQuotaObjects.Remove(mPath); 1.61 + } 1.62 + } 1.63 + 1.64 + delete this; 1.65 +} 1.66 + 1.67 +void 1.68 +QuotaObject::UpdateSize(int64_t aSize) 1.69 +{ 1.70 + QuotaManager* quotaManager = QuotaManager::Get(); 1.71 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.72 + 1.73 + MutexAutoLock lock(quotaManager->mQuotaMutex); 1.74 + 1.75 + if (!mOriginInfo) { 1.76 + return; 1.77 + } 1.78 + 1.79 + GroupInfo* groupInfo = mOriginInfo->mGroupInfo; 1.80 + 1.81 + if (groupInfo->IsForTemporaryStorage()) { 1.82 + quotaManager->mTemporaryStorageUsage -= mSize; 1.83 + } 1.84 + groupInfo->mUsage -= mSize; 1.85 + mOriginInfo->mUsage -= mSize; 1.86 + 1.87 + mSize = aSize; 1.88 + 1.89 + mOriginInfo->mUsage += mSize; 1.90 + groupInfo->mUsage += mSize; 1.91 + if (groupInfo->IsForTemporaryStorage()) { 1.92 + quotaManager->mTemporaryStorageUsage += mSize; 1.93 + } 1.94 +} 1.95 + 1.96 +bool 1.97 +QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount) 1.98 +{ 1.99 + int64_t end = aOffset + aCount; 1.100 + 1.101 + QuotaManager* quotaManager = QuotaManager::Get(); 1.102 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.103 + 1.104 + MutexAutoLock lock(quotaManager->mQuotaMutex); 1.105 + 1.106 + if (mSize >= end || !mOriginInfo) { 1.107 + return true; 1.108 + } 1.109 + 1.110 + GroupInfo* groupInfo = mOriginInfo->mGroupInfo; 1.111 + 1.112 + if (groupInfo->IsForPersistentStorage()) { 1.113 + uint64_t newUsage = mOriginInfo->mUsage - mSize + end; 1.114 + 1.115 + if (newUsage > mOriginInfo->mLimit) { 1.116 + // This will block the thread, but it will also drop the mutex while 1.117 + // waiting. The mutex will be reacquired again when the waiting is 1.118 + // finished. 1.119 + if (!quotaManager->LockedQuotaIsLifted()) { 1.120 + return false; 1.121 + } 1.122 + 1.123 + // Threads raced, the origin info removal has been done by some other 1.124 + // thread. 1.125 + if (!mOriginInfo) { 1.126 + // The other thread could allocate more space. 1.127 + if (end > mSize) { 1.128 + mSize = end; 1.129 + } 1.130 + 1.131 + return true; 1.132 + } 1.133 + 1.134 + nsCString group = mOriginInfo->mGroupInfo->mGroup; 1.135 + nsCString origin = mOriginInfo->mOrigin; 1.136 + 1.137 + mOriginInfo->LockedClearOriginInfos(); 1.138 + NS_ASSERTION(!mOriginInfo, 1.139 + "Should have cleared in LockedClearOriginInfos!"); 1.140 + 1.141 + quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_PERSISTENT, 1.142 + group, origin); 1.143 + 1.144 + // Some other thread could increase the size without blocking (increasing 1.145 + // the origin usage without hitting the limit), but no more than this one. 1.146 + NS_ASSERTION(mSize < end, "This shouldn't happen!"); 1.147 + 1.148 + mSize = end; 1.149 + 1.150 + return true; 1.151 + } 1.152 + 1.153 + mOriginInfo->mUsage = newUsage; 1.154 + 1.155 + groupInfo->mUsage = groupInfo->mUsage - mSize + end; 1.156 + 1.157 + mSize = end; 1.158 + 1.159 + return true; 1.160 + } 1.161 + 1.162 + NS_ASSERTION(groupInfo->mPersistenceType == PERSISTENCE_TYPE_TEMPORARY, 1.163 + "Huh?"); 1.164 + 1.165 + uint64_t delta = end - mSize; 1.166 + 1.167 + uint64_t newUsage = mOriginInfo->mUsage + delta; 1.168 + 1.169 + // Temporary storage has no limit for origin usage (there's a group and the 1.170 + // global limit though). 1.171 + 1.172 + uint64_t newGroupUsage = groupInfo->mUsage + delta; 1.173 + 1.174 + // Temporary storage has a hard limit for group usage (20 % of the global 1.175 + // limit). 1.176 + if (newGroupUsage > quotaManager->GetGroupLimit()) { 1.177 + return false; 1.178 + } 1.179 + 1.180 + uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + 1.181 + delta; 1.182 + 1.183 + if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) { 1.184 + // This will block the thread without holding the lock while waitting. 1.185 + 1.186 + nsAutoTArray<OriginInfo*, 10> originInfos; 1.187 + uint64_t sizeToBeFreed = 1.188 + quotaManager->LockedCollectOriginsForEviction(delta, originInfos); 1.189 + 1.190 + if (!sizeToBeFreed) { 1.191 + return false; 1.192 + } 1.193 + 1.194 + NS_ASSERTION(sizeToBeFreed >= delta, "Huh?"); 1.195 + 1.196 + { 1.197 + MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); 1.198 + 1.199 + for (uint32_t i = 0; i < originInfos.Length(); i++) { 1.200 + quotaManager->DeleteTemporaryFilesForOrigin(originInfos[i]->mOrigin); 1.201 + } 1.202 + } 1.203 + 1.204 + // Relocked. 1.205 + 1.206 + NS_ASSERTION(mOriginInfo, "How come?!"); 1.207 + 1.208 + nsTArray<nsCString> origins; 1.209 + for (uint32_t i = 0; i < originInfos.Length(); i++) { 1.210 + OriginInfo* originInfo = originInfos[i]; 1.211 + 1.212 + NS_ASSERTION(originInfo != mOriginInfo, "Deleted itself!"); 1.213 + 1.214 + nsCString group = originInfo->mGroupInfo->mGroup; 1.215 + nsCString origin = originInfo->mOrigin; 1.216 + quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY, 1.217 + group, origin); 1.218 + 1.219 +#ifdef DEBUG 1.220 + originInfos[i] = nullptr; 1.221 +#endif 1.222 + 1.223 + origins.AppendElement(origin); 1.224 + } 1.225 + 1.226 + // We unlocked and relocked several times so we need to recompute all the 1.227 + // essential variables and recheck the group limit. 1.228 + 1.229 + delta = end - mSize; 1.230 + 1.231 + newUsage = mOriginInfo->mUsage + delta; 1.232 + 1.233 + newGroupUsage = groupInfo->mUsage + delta; 1.234 + 1.235 + if (newGroupUsage > quotaManager->GetGroupLimit()) { 1.236 + // Unfortunately some other thread increased the group usage in the 1.237 + // meantime and we are not below the group limit anymore. 1.238 + 1.239 + // However, the origin eviction must be finalized in this case too. 1.240 + MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); 1.241 + 1.242 + quotaManager->FinalizeOriginEviction(origins); 1.243 + 1.244 + return false; 1.245 + } 1.246 + 1.247 + newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta; 1.248 + 1.249 + NS_ASSERTION(newTemporaryStorageUsage <= 1.250 + quotaManager->mTemporaryStorageLimit, "How come?!"); 1.251 + 1.252 + // Ok, we successfully freed enough space and the operation can continue 1.253 + // without throwing the quota error. 1.254 + 1.255 + mOriginInfo->mUsage = newUsage; 1.256 + groupInfo->mUsage = newGroupUsage; 1.257 + quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;; 1.258 + 1.259 + // Some other thread could increase the size in the meantime, but no more 1.260 + // than this one. 1.261 + NS_ASSERTION(mSize < end, "This shouldn't happen!"); 1.262 + mSize = end; 1.263 + 1.264 + // Finally, release IO thread only objects and allow next synchronized 1.265 + // ops for the evicted origins. 1.266 + MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); 1.267 + 1.268 + quotaManager->FinalizeOriginEviction(origins); 1.269 + 1.270 + return true; 1.271 + } 1.272 + 1.273 + mOriginInfo->mUsage = newUsage; 1.274 + groupInfo->mUsage = newGroupUsage; 1.275 + quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage; 1.276 + 1.277 + mSize = end; 1.278 + 1.279 + return true; 1.280 +} 1.281 + 1.282 +void 1.283 +OriginInfo::LockedDecreaseUsage(int64_t aSize) 1.284 +{ 1.285 + AssertCurrentThreadOwnsQuotaMutex(); 1.286 + 1.287 + mUsage -= aSize; 1.288 + 1.289 + mGroupInfo->mUsage -= aSize; 1.290 + 1.291 + if (mGroupInfo->IsForTemporaryStorage()) { 1.292 + QuotaManager* quotaManager = QuotaManager::Get(); 1.293 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.294 + 1.295 + quotaManager->mTemporaryStorageUsage -= aSize; 1.296 + } 1.297 +} 1.298 + 1.299 +// static 1.300 +PLDHashOperator 1.301 +OriginInfo::ClearOriginInfoCallback(const nsAString& aKey, 1.302 + QuotaObject* aValue, 1.303 + void* aUserArg) 1.304 +{ 1.305 + NS_ASSERTION(!aKey.IsEmpty(), "Empty key!"); 1.306 + NS_ASSERTION(aValue, "Null pointer!"); 1.307 + 1.308 + aValue->mOriginInfo = nullptr; 1.309 + 1.310 + return PL_DHASH_NEXT; 1.311 +} 1.312 + 1.313 +already_AddRefed<OriginInfo> 1.314 +GroupInfo::LockedGetOriginInfo(const nsACString& aOrigin) 1.315 +{ 1.316 + AssertCurrentThreadOwnsQuotaMutex(); 1.317 + 1.318 + for (uint32_t index = 0; index < mOriginInfos.Length(); index++) { 1.319 + nsRefPtr<OriginInfo>& originInfo = mOriginInfos[index]; 1.320 + 1.321 + if (originInfo->mOrigin == aOrigin) { 1.322 + nsRefPtr<OriginInfo> result = originInfo; 1.323 + return result.forget(); 1.324 + } 1.325 + } 1.326 + 1.327 + return nullptr; 1.328 +} 1.329 + 1.330 +void 1.331 +GroupInfo::LockedAddOriginInfo(OriginInfo* aOriginInfo) 1.332 +{ 1.333 + AssertCurrentThreadOwnsQuotaMutex(); 1.334 + 1.335 + NS_ASSERTION(!mOriginInfos.Contains(aOriginInfo), 1.336 + "Replacing an existing entry!"); 1.337 + mOriginInfos.AppendElement(aOriginInfo); 1.338 + 1.339 + mUsage += aOriginInfo->mUsage; 1.340 + 1.341 + if (IsForTemporaryStorage()) { 1.342 + QuotaManager* quotaManager = QuotaManager::Get(); 1.343 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.344 + 1.345 + quotaManager->mTemporaryStorageUsage += aOriginInfo->mUsage; 1.346 + } 1.347 +} 1.348 + 1.349 +void 1.350 +GroupInfo::LockedRemoveOriginInfo(const nsACString& aOrigin) 1.351 +{ 1.352 + AssertCurrentThreadOwnsQuotaMutex(); 1.353 + 1.354 + for (uint32_t index = 0; index < mOriginInfos.Length(); index++) { 1.355 + if (mOriginInfos[index]->mOrigin == aOrigin) { 1.356 + mUsage -= mOriginInfos[index]->mUsage; 1.357 + 1.358 + if (IsForTemporaryStorage()) { 1.359 + QuotaManager* quotaManager = QuotaManager::Get(); 1.360 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.361 + 1.362 + quotaManager->mTemporaryStorageUsage -= mOriginInfos[index]->mUsage; 1.363 + } 1.364 + 1.365 + mOriginInfos.RemoveElementAt(index); 1.366 + 1.367 + return; 1.368 + } 1.369 + } 1.370 +} 1.371 + 1.372 +void 1.373 +GroupInfo::LockedRemoveOriginInfos() 1.374 +{ 1.375 + AssertCurrentThreadOwnsQuotaMutex(); 1.376 + 1.377 + for (uint32_t index = mOriginInfos.Length(); index > 0; index--) { 1.378 + mUsage -= mOriginInfos[index - 1]->mUsage; 1.379 + 1.380 + if (IsForTemporaryStorage()) { 1.381 + QuotaManager* quotaManager = QuotaManager::Get(); 1.382 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.383 + 1.384 + quotaManager->mTemporaryStorageUsage -= mOriginInfos[index - 1]->mUsage; 1.385 + } 1.386 + 1.387 + mOriginInfos.RemoveElementAt(index - 1); 1.388 + } 1.389 +} 1.390 + 1.391 +void 1.392 +GroupInfo::LockedRemoveOriginInfosForPattern(const nsACString& aPattern) 1.393 +{ 1.394 + AssertCurrentThreadOwnsQuotaMutex(); 1.395 + 1.396 + for (uint32_t index = mOriginInfos.Length(); index > 0; index--) { 1.397 + if (PatternMatchesOrigin(aPattern, mOriginInfos[index - 1]->mOrigin)) { 1.398 + mUsage -= mOriginInfos[index - 1]->mUsage; 1.399 + 1.400 + if (IsForTemporaryStorage()) { 1.401 + QuotaManager* quotaManager = QuotaManager::Get(); 1.402 + NS_ASSERTION(quotaManager, "Shouldn't be null!"); 1.403 + 1.404 + quotaManager->mTemporaryStorageUsage -= mOriginInfos[index - 1]->mUsage; 1.405 + } 1.406 + 1.407 + mOriginInfos.RemoveElementAt(index - 1); 1.408 + } 1.409 + } 1.410 +} 1.411 + 1.412 +nsRefPtr<GroupInfo>& 1.413 +GroupInfoPair::GetGroupInfoForPersistenceType(PersistenceType aPersistenceType) 1.414 +{ 1.415 + switch (aPersistenceType) { 1.416 + case PERSISTENCE_TYPE_PERSISTENT: 1.417 + return mPersistentStorageGroupInfo; 1.418 + case PERSISTENCE_TYPE_TEMPORARY: 1.419 + return mTemporaryStorageGroupInfo; 1.420 + 1.421 + case PERSISTENCE_TYPE_INVALID: 1.422 + default: 1.423 + MOZ_CRASH("Bad persistence type value!"); 1.424 + return mPersistentStorageGroupInfo; 1.425 + } 1.426 +}