CanonicalQuotaObject.cpp (10013B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "CanonicalQuotaObject.h" 8 9 #include "GroupInfo.h" 10 #include "GroupInfoPair.h" 11 #include "OriginInfo.h" 12 #include "mozilla/dom/StorageActivityService.h" 13 #include "mozilla/dom/quota/AssertionsImpl.h" 14 #include "mozilla/dom/quota/NotifyUtils.h" 15 #include "mozilla/dom/quota/OriginDirectoryLock.h" 16 #include "mozilla/dom/quota/QuotaManager.h" 17 #include "mozilla/ipc/BackgroundParent.h" 18 19 namespace mozilla::dom::quota { 20 21 NS_IMETHODIMP_(MozExternalRefCountType) CanonicalQuotaObject::AddRef() { 22 QuotaManager* quotaManager = QuotaManager::Get(); 23 if (!quotaManager) { 24 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); 25 26 return ++mRefCnt; 27 } 28 29 MutexAutoLock lock(quotaManager->mQuotaMutex); 30 31 return ++mRefCnt; 32 } 33 34 NS_IMETHODIMP_(MozExternalRefCountType) CanonicalQuotaObject::Release() { 35 QuotaManager* quotaManager = QuotaManager::Get(); 36 if (!quotaManager) { 37 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!"); 38 39 nsrefcnt count = --mRefCnt; 40 if (count == 0) { 41 mRefCnt = 1; 42 delete this; 43 return 0; 44 } 45 46 return mRefCnt; 47 } 48 49 { 50 MutexAutoLock lock(quotaManager->mQuotaMutex); 51 52 --mRefCnt; 53 54 if (mRefCnt > 0) { 55 return mRefCnt; 56 } 57 58 if (mOriginInfo) { 59 mOriginInfo->mCanonicalQuotaObjects.Remove(mPath); 60 } 61 } 62 63 delete this; 64 return 0; 65 } 66 67 bool CanonicalQuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate) { 68 QuotaManager* quotaManager = QuotaManager::Get(); 69 MOZ_ASSERT(quotaManager); 70 71 MutexAutoLock lock(quotaManager->mQuotaMutex); 72 73 return LockedMaybeUpdateSize(aSize, aTruncate); 74 } 75 76 bool CanonicalQuotaObject::IncreaseSize(int64_t aDelta) { 77 MOZ_ASSERT(aDelta >= 0); 78 79 QuotaManager* quotaManager = QuotaManager::Get(); 80 MOZ_ASSERT(quotaManager); 81 82 MutexAutoLock lock(quotaManager->mQuotaMutex); 83 84 AssertNoOverflow(mSize, aDelta); 85 int64_t size = mSize + aDelta; 86 87 return LockedMaybeUpdateSize(size, /* aTruncate */ false); 88 } 89 90 void CanonicalQuotaObject::DisableQuotaCheck() { 91 QuotaManager* quotaManager = QuotaManager::Get(); 92 MOZ_ASSERT(quotaManager); 93 94 MutexAutoLock lock(quotaManager->mQuotaMutex); 95 96 mQuotaCheckDisabled = true; 97 } 98 99 void CanonicalQuotaObject::EnableQuotaCheck() { 100 QuotaManager* quotaManager = QuotaManager::Get(); 101 MOZ_ASSERT(quotaManager); 102 103 MutexAutoLock lock(quotaManager->mQuotaMutex); 104 105 mQuotaCheckDisabled = false; 106 } 107 108 bool CanonicalQuotaObject::LockedMaybeUpdateSize(int64_t aSize, bool aTruncate) 109 MOZ_NO_THREAD_SAFETY_ANALYSIS { 110 QuotaManager* quotaManager = QuotaManager::Get(); 111 MOZ_ASSERT(quotaManager); 112 113 quotaManager->mQuotaMutex.AssertCurrentThreadOwns(); 114 115 if (mWritingDone == false && mOriginInfo) { 116 mWritingDone = true; 117 StorageActivityService::SendActivity(mOriginInfo->mOrigin); 118 } 119 120 if (mQuotaCheckDisabled) { 121 return true; 122 } 123 124 if (mSize == aSize) { 125 return true; 126 } 127 128 if (!mOriginInfo) { 129 mSize = aSize; 130 return true; 131 } 132 133 GroupInfo* groupInfo = mOriginInfo->mGroupInfo; 134 MOZ_ASSERT(groupInfo); 135 136 if (mSize > aSize) { 137 if (aTruncate) { 138 const int64_t delta = mSize - aSize; 139 140 AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, delta); 141 quotaManager->mTemporaryStorageUsage -= delta; 142 143 if (!mOriginInfo->LockedPersisted()) { 144 AssertNoUnderflow(groupInfo->mUsage, delta); 145 groupInfo->mUsage -= delta; 146 } 147 148 AssertNoUnderflow(mOriginInfo->mUsage, delta); 149 mOriginInfo->mUsage -= delta; 150 151 MOZ_ASSERT(mOriginInfo->mClientUsages[mClientType].isSome()); 152 AssertNoUnderflow(mOriginInfo->mClientUsages[mClientType].value(), delta); 153 mOriginInfo->mClientUsages[mClientType] = 154 Some(mOriginInfo->mClientUsages[mClientType].value() - delta); 155 156 mSize = aSize; 157 } 158 return true; 159 } 160 161 MOZ_ASSERT(mSize < aSize); 162 163 const auto& complementaryPersistenceTypes = 164 ComplementaryPersistenceTypes(groupInfo->mPersistenceType); 165 166 uint64_t delta = aSize - mSize; 167 168 AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta); 169 uint64_t newClientUsage = 170 mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta; 171 172 AssertNoOverflow(mOriginInfo->mUsage, delta); 173 uint64_t newUsage = mOriginInfo->mUsage + delta; 174 175 // Temporary storage has no limit for origin usage (there's a group and the 176 // global limit though). 177 178 uint64_t newGroupUsage = groupInfo->mUsage; 179 if (!mOriginInfo->LockedPersisted()) { 180 AssertNoOverflow(groupInfo->mUsage, delta); 181 newGroupUsage += delta; 182 183 uint64_t groupUsage = groupInfo->mUsage; 184 for (const auto& complementaryPersistenceType : 185 complementaryPersistenceTypes) { 186 const auto& complementaryGroupInfo = 187 groupInfo->mGroupInfoPair->LockedGetGroupInfo( 188 complementaryPersistenceType); 189 190 if (complementaryGroupInfo) { 191 AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage); 192 groupUsage += complementaryGroupInfo->mUsage; 193 } 194 } 195 196 // Temporary storage has a hard limit for group usage (20 % of the global 197 // limit). 198 AssertNoOverflow(groupUsage, delta); 199 if (groupUsage + delta > quotaManager->GetGroupLimit()) { 200 return false; 201 } 202 } 203 204 AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta); 205 uint64_t newTemporaryStorageUsage = 206 quotaManager->mTemporaryStorageUsage + delta; 207 208 if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) { 209 // This will block the thread without holding the lock while waitting. 210 211 AutoTArray<RefPtr<OriginDirectoryLock>, 10> locks; 212 uint64_t sizeToBeFreed; 213 214 if (::mozilla::ipc::IsOnBackgroundThread()) { 215 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); 216 217 sizeToBeFreed = quotaManager->CollectOriginsForEviction(delta, locks); 218 } else { 219 sizeToBeFreed = 220 quotaManager->LockedCollectOriginsForEviction(delta, locks); 221 } 222 223 if (!sizeToBeFreed) { 224 uint64_t usage = quotaManager->mTemporaryStorageUsage; 225 226 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); 227 228 NotifyStoragePressure(*quotaManager, usage); 229 230 return false; 231 } 232 233 NS_ASSERTION(sizeToBeFreed >= delta, "Huh?"); 234 235 { 236 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); 237 238 for (const auto& lock : locks) { 239 quotaManager->DeleteOriginDirectory(lock->OriginMetadata()); 240 } 241 } 242 243 // Relocked. 244 245 NS_ASSERTION(mOriginInfo, "How come?!"); 246 247 for (const auto& lock : locks) { 248 MOZ_ASSERT(!(lock->GetPersistenceType() == groupInfo->mPersistenceType && 249 lock->Origin() == mOriginInfo->mOrigin), 250 "Deleted itself!"); 251 252 quotaManager->LockedRemoveQuotaForOrigin(lock->OriginMetadata()); 253 } 254 255 // We unlocked and relocked several times so we need to recompute all the 256 // essential variables and recheck the group limit. 257 258 AssertNoUnderflow(aSize, mSize); 259 delta = aSize - mSize; 260 261 AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta); 262 newClientUsage = mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta; 263 264 AssertNoOverflow(mOriginInfo->mUsage, delta); 265 newUsage = mOriginInfo->mUsage + delta; 266 267 newGroupUsage = groupInfo->mUsage; 268 if (!mOriginInfo->LockedPersisted()) { 269 AssertNoOverflow(groupInfo->mUsage, delta); 270 newGroupUsage += delta; 271 272 uint64_t groupUsage = groupInfo->mUsage; 273 274 for (const auto& complementaryPersistenceType : 275 complementaryPersistenceTypes) { 276 const auto& complementaryGroupInfo = 277 groupInfo->mGroupInfoPair->LockedGetGroupInfo( 278 complementaryPersistenceType); 279 280 if (complementaryGroupInfo) { 281 AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage); 282 groupUsage += complementaryGroupInfo->mUsage; 283 } 284 } 285 286 AssertNoOverflow(groupUsage, delta); 287 if (groupUsage + delta > quotaManager->GetGroupLimit()) { 288 // Unfortunately some other thread increased the group usage in the 289 // meantime and we are not below the group limit anymore. 290 291 // However, the origin eviction must be finalized in this case too. 292 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); 293 294 quotaManager->FinalizeOriginEviction(std::move(locks)); 295 296 return false; 297 } 298 } 299 300 AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta); 301 newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta; 302 303 NS_ASSERTION( 304 newTemporaryStorageUsage <= quotaManager->mTemporaryStorageLimit, 305 "How come?!"); 306 307 // Ok, we successfully freed enough space and the operation can continue 308 // without throwing the quota error. 309 mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage); 310 311 mOriginInfo->mUsage = newUsage; 312 if (!mOriginInfo->LockedPersisted()) { 313 groupInfo->mUsage = newGroupUsage; 314 } 315 quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage; 316 ; 317 318 // Some other thread could increase the size in the meantime, but no more 319 // than this one. 320 MOZ_ASSERT(mSize < aSize); 321 mSize = aSize; 322 323 // Finally, release IO thread only objects and allow next synchronized 324 // ops for the evicted origins. 325 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex); 326 327 quotaManager->FinalizeOriginEviction(std::move(locks)); 328 329 return true; 330 } 331 332 mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage); 333 334 mOriginInfo->mUsage = newUsage; 335 if (!mOriginInfo->LockedPersisted()) { 336 groupInfo->mUsage = newGroupUsage; 337 } 338 quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage; 339 340 mSize = aSize; 341 342 return true; 343 } 344 345 } // namespace mozilla::dom::quota