Bug 1833854 - Part 7: Add the FOR_EACH_GC_TUNABLE macro to describe tunable GC parame...
[gecko.git] / dom / quota / CanonicalQuotaObject.cpp
blob9c284cb4719b6daa83961117eff2ce984819d55d
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/. */
7 #include "CanonicalQuotaObject.h"
9 #include "GroupInfo.h"
10 #include "GroupInfoPair.h"
11 #include "mozilla/dom/StorageActivityService.h"
12 #include "mozilla/dom/quota/AssertionsImpl.h"
13 #include "mozilla/dom/quota/DirectoryLock.h"
14 #include "mozilla/dom/quota/QuotaManager.h"
15 #include "mozilla/ipc/BackgroundParent.h"
16 #include "OriginInfo.h"
18 namespace mozilla::dom::quota {
20 NS_IMETHODIMP_(MozExternalRefCountType) CanonicalQuotaObject::AddRef() {
21 QuotaManager* quotaManager = QuotaManager::Get();
22 if (!quotaManager) {
23 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
25 return ++mRefCnt;
28 MutexAutoLock lock(quotaManager->mQuotaMutex);
30 return ++mRefCnt;
33 NS_IMETHODIMP_(MozExternalRefCountType) CanonicalQuotaObject::Release() {
34 QuotaManager* quotaManager = QuotaManager::Get();
35 if (!quotaManager) {
36 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
38 nsrefcnt count = --mRefCnt;
39 if (count == 0) {
40 mRefCnt = 1;
41 delete this;
42 return 0;
45 return mRefCnt;
49 MutexAutoLock lock(quotaManager->mQuotaMutex);
51 --mRefCnt;
53 if (mRefCnt > 0) {
54 return mRefCnt;
57 if (mOriginInfo) {
58 mOriginInfo->mCanonicalQuotaObjects.Remove(mPath);
62 delete this;
63 return 0;
66 bool CanonicalQuotaObject::MaybeUpdateSize(int64_t aSize, bool aTruncate) {
67 QuotaManager* quotaManager = QuotaManager::Get();
68 MOZ_ASSERT(quotaManager);
70 MutexAutoLock lock(quotaManager->mQuotaMutex);
72 return LockedMaybeUpdateSize(aSize, aTruncate);
75 bool CanonicalQuotaObject::IncreaseSize(int64_t aDelta) {
76 MOZ_ASSERT(aDelta >= 0);
78 QuotaManager* quotaManager = QuotaManager::Get();
79 MOZ_ASSERT(quotaManager);
81 MutexAutoLock lock(quotaManager->mQuotaMutex);
83 AssertNoOverflow(mSize, aDelta);
84 int64_t size = mSize + aDelta;
86 return LockedMaybeUpdateSize(size, /* aTruncate */ false);
89 void CanonicalQuotaObject::DisableQuotaCheck() {
90 QuotaManager* quotaManager = QuotaManager::Get();
91 MOZ_ASSERT(quotaManager);
93 MutexAutoLock lock(quotaManager->mQuotaMutex);
95 mQuotaCheckDisabled = true;
98 void CanonicalQuotaObject::EnableQuotaCheck() {
99 QuotaManager* quotaManager = QuotaManager::Get();
100 MOZ_ASSERT(quotaManager);
102 MutexAutoLock lock(quotaManager->mQuotaMutex);
104 mQuotaCheckDisabled = false;
107 bool CanonicalQuotaObject::LockedMaybeUpdateSize(int64_t aSize,
108 bool aTruncate) {
109 QuotaManager* quotaManager = QuotaManager::Get();
110 MOZ_ASSERT(quotaManager);
112 quotaManager->mQuotaMutex.AssertCurrentThreadOwns();
114 if (mWritingDone == false && mOriginInfo) {
115 mWritingDone = true;
116 StorageActivityService::SendActivity(mOriginInfo->mOrigin);
119 if (mQuotaCheckDisabled) {
120 return true;
123 if (mSize == aSize) {
124 return true;
127 if (!mOriginInfo) {
128 mSize = aSize;
129 return true;
132 GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
133 MOZ_ASSERT(groupInfo);
135 if (mSize > aSize) {
136 if (aTruncate) {
137 const int64_t delta = mSize - aSize;
139 AssertNoUnderflow(quotaManager->mTemporaryStorageUsage, delta);
140 quotaManager->mTemporaryStorageUsage -= delta;
142 if (!mOriginInfo->LockedPersisted()) {
143 AssertNoUnderflow(groupInfo->mUsage, delta);
144 groupInfo->mUsage -= delta;
147 AssertNoUnderflow(mOriginInfo->mUsage, delta);
148 mOriginInfo->mUsage -= delta;
150 MOZ_ASSERT(mOriginInfo->mClientUsages[mClientType].isSome());
151 AssertNoUnderflow(mOriginInfo->mClientUsages[mClientType].value(), delta);
152 mOriginInfo->mClientUsages[mClientType] =
153 Some(mOriginInfo->mClientUsages[mClientType].value() - delta);
155 mSize = aSize;
157 return true;
160 MOZ_ASSERT(mSize < aSize);
162 const auto& complementaryPersistenceTypes =
163 ComplementaryPersistenceTypes(groupInfo->mPersistenceType);
165 uint64_t delta = aSize - mSize;
167 AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta);
168 uint64_t newClientUsage =
169 mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta;
171 AssertNoOverflow(mOriginInfo->mUsage, delta);
172 uint64_t newUsage = mOriginInfo->mUsage + delta;
174 // Temporary storage has no limit for origin usage (there's a group and the
175 // global limit though).
177 uint64_t newGroupUsage = groupInfo->mUsage;
178 if (!mOriginInfo->LockedPersisted()) {
179 AssertNoOverflow(groupInfo->mUsage, delta);
180 newGroupUsage += delta;
182 uint64_t groupUsage = groupInfo->mUsage;
183 for (const auto& complementaryPersistenceType :
184 complementaryPersistenceTypes) {
185 const auto& complementaryGroupInfo =
186 groupInfo->mGroupInfoPair->LockedGetGroupInfo(
187 complementaryPersistenceType);
189 if (complementaryGroupInfo) {
190 AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
191 groupUsage += complementaryGroupInfo->mUsage;
195 // Temporary storage has a hard limit for group usage (20 % of the global
196 // limit).
197 AssertNoOverflow(groupUsage, delta);
198 if (groupUsage + delta > quotaManager->GetGroupLimit()) {
199 return false;
203 AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
204 uint64_t newTemporaryStorageUsage =
205 quotaManager->mTemporaryStorageUsage + delta;
207 if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) {
208 // This will block the thread without holding the lock while waitting.
210 AutoTArray<RefPtr<OriginDirectoryLock>, 10> locks;
211 uint64_t sizeToBeFreed;
213 if (::mozilla::ipc::IsOnBackgroundThread()) {
214 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
216 sizeToBeFreed = quotaManager->CollectOriginsForEviction(delta, locks);
217 } else {
218 sizeToBeFreed =
219 quotaManager->LockedCollectOriginsForEviction(delta, locks);
222 if (!sizeToBeFreed) {
223 uint64_t usage = quotaManager->mTemporaryStorageUsage;
225 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
227 quotaManager->NotifyStoragePressure(usage);
229 return false;
232 NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
235 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
237 for (const auto& lock : locks) {
238 quotaManager->DeleteOriginDirectory(lock->OriginMetadata());
242 // Relocked.
244 NS_ASSERTION(mOriginInfo, "How come?!");
246 for (const auto& lock : locks) {
247 MOZ_ASSERT(!(lock->GetPersistenceType() == groupInfo->mPersistenceType &&
248 lock->Origin() == mOriginInfo->mOrigin),
249 "Deleted itself!");
251 quotaManager->LockedRemoveQuotaForOrigin(lock->OriginMetadata());
254 // We unlocked and relocked several times so we need to recompute all the
255 // essential variables and recheck the group limit.
257 AssertNoUnderflow(aSize, mSize);
258 delta = aSize - mSize;
260 AssertNoOverflow(mOriginInfo->mClientUsages[mClientType].valueOr(0), delta);
261 newClientUsage = mOriginInfo->mClientUsages[mClientType].valueOr(0) + delta;
263 AssertNoOverflow(mOriginInfo->mUsage, delta);
264 newUsage = mOriginInfo->mUsage + delta;
266 newGroupUsage = groupInfo->mUsage;
267 if (!mOriginInfo->LockedPersisted()) {
268 AssertNoOverflow(groupInfo->mUsage, delta);
269 newGroupUsage += delta;
271 uint64_t groupUsage = groupInfo->mUsage;
273 for (const auto& complementaryPersistenceType :
274 complementaryPersistenceTypes) {
275 const auto& complementaryGroupInfo =
276 groupInfo->mGroupInfoPair->LockedGetGroupInfo(
277 complementaryPersistenceType);
279 if (complementaryGroupInfo) {
280 AssertNoOverflow(groupUsage, complementaryGroupInfo->mUsage);
281 groupUsage += complementaryGroupInfo->mUsage;
285 AssertNoOverflow(groupUsage, delta);
286 if (groupUsage + delta > quotaManager->GetGroupLimit()) {
287 // Unfortunately some other thread increased the group usage in the
288 // meantime and we are not below the group limit anymore.
290 // However, the origin eviction must be finalized in this case too.
291 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
293 quotaManager->FinalizeOriginEviction(std::move(locks));
295 return false;
299 AssertNoOverflow(quotaManager->mTemporaryStorageUsage, delta);
300 newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta;
302 NS_ASSERTION(
303 newTemporaryStorageUsage <= quotaManager->mTemporaryStorageLimit,
304 "How come?!");
306 // Ok, we successfully freed enough space and the operation can continue
307 // without throwing the quota error.
308 mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage);
310 mOriginInfo->mUsage = newUsage;
311 if (!mOriginInfo->LockedPersisted()) {
312 groupInfo->mUsage = newGroupUsage;
314 quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
317 // Some other thread could increase the size in the meantime, but no more
318 // than this one.
319 MOZ_ASSERT(mSize < aSize);
320 mSize = aSize;
322 // Finally, release IO thread only objects and allow next synchronized
323 // ops for the evicted origins.
324 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
326 quotaManager->FinalizeOriginEviction(std::move(locks));
328 return true;
331 mOriginInfo->mClientUsages[mClientType] = Some(newClientUsage);
333 mOriginInfo->mUsage = newUsage;
334 if (!mOriginInfo->LockedPersisted()) {
335 groupInfo->mUsage = newGroupUsage;
337 quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
339 mSize = aSize;
341 return true;
344 } // namespace mozilla::dom::quota