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"
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();
23 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
28 MutexAutoLock
lock(quotaManager
->mQuotaMutex
);
33 NS_IMETHODIMP_(MozExternalRefCountType
) CanonicalQuotaObject::Release() {
34 QuotaManager
* quotaManager
= QuotaManager::Get();
36 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
38 nsrefcnt count
= --mRefCnt
;
49 MutexAutoLock
lock(quotaManager
->mQuotaMutex
);
58 mOriginInfo
->mCanonicalQuotaObjects
.Remove(mPath
);
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
,
109 QuotaManager
* quotaManager
= QuotaManager::Get();
110 MOZ_ASSERT(quotaManager
);
112 quotaManager
->mQuotaMutex
.AssertCurrentThreadOwns();
114 if (mWritingDone
== false && mOriginInfo
) {
116 StorageActivityService::SendActivity(mOriginInfo
->mOrigin
);
119 if (mQuotaCheckDisabled
) {
123 if (mSize
== aSize
) {
132 GroupInfo
* groupInfo
= mOriginInfo
->mGroupInfo
;
133 MOZ_ASSERT(groupInfo
);
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
);
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
197 AssertNoOverflow(groupUsage
, delta
);
198 if (groupUsage
+ delta
> quotaManager
->GetGroupLimit()) {
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
);
219 quotaManager
->LockedCollectOriginsForEviction(delta
, locks
);
222 if (!sizeToBeFreed
) {
223 uint64_t usage
= quotaManager
->mTemporaryStorageUsage
;
225 MutexAutoUnlock
autoUnlock(quotaManager
->mQuotaMutex
);
227 quotaManager
->NotifyStoragePressure(usage
);
232 NS_ASSERTION(sizeToBeFreed
>= delta
, "Huh?");
235 MutexAutoUnlock
autoUnlock(quotaManager
->mQuotaMutex
);
237 for (const auto& lock
: locks
) {
238 quotaManager
->DeleteOriginDirectory(lock
->OriginMetadata());
244 NS_ASSERTION(mOriginInfo
, "How come?!");
246 for (const auto& lock
: locks
) {
247 MOZ_ASSERT(!(lock
->GetPersistenceType() == groupInfo
->mPersistenceType
&&
248 lock
->Origin() == mOriginInfo
->mOrigin
),
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
));
299 AssertNoOverflow(quotaManager
->mTemporaryStorageUsage
, delta
);
300 newTemporaryStorageUsage
= quotaManager
->mTemporaryStorageUsage
+ delta
;
303 newTemporaryStorageUsage
<= quotaManager
->mTemporaryStorageLimit
,
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
319 MOZ_ASSERT(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
));
331 mOriginInfo
->mClientUsages
[mClientType
] = Some(newClientUsage
);
333 mOriginInfo
->mUsage
= newUsage
;
334 if (!mOriginInfo
->LockedPersisted()) {
335 groupInfo
->mUsage
= newGroupUsage
;
337 quotaManager
->mTemporaryStorageUsage
= newTemporaryStorageUsage
;
344 } // namespace mozilla::dom::quota