dom/quota/QuotaObject.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:39b5759f98ae
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=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 "QuotaObject.h"
8
9 #include "QuotaManager.h"
10 #include "Utilities.h"
11
12 USING_QUOTA_NAMESPACE
13
14 void
15 QuotaObject::AddRef()
16 {
17 QuotaManager* quotaManager = QuotaManager::Get();
18 if (!quotaManager) {
19 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
20
21 ++mRefCnt;
22
23 return;
24 }
25
26 MutexAutoLock lock(quotaManager->mQuotaMutex);
27
28 ++mRefCnt;
29 }
30
31 void
32 QuotaObject::Release()
33 {
34 QuotaManager* quotaManager = QuotaManager::Get();
35 if (!quotaManager) {
36 NS_ERROR("Null quota manager, this shouldn't happen, possible leak!");
37
38 nsrefcnt count = --mRefCnt;
39 if (count == 0) {
40 mRefCnt = 1;
41 delete this;
42 }
43
44 return;
45 }
46
47 {
48 MutexAutoLock lock(quotaManager->mQuotaMutex);
49
50 --mRefCnt;
51
52 if (mRefCnt > 0) {
53 return;
54 }
55
56 if (mOriginInfo) {
57 mOriginInfo->mQuotaObjects.Remove(mPath);
58 }
59 }
60
61 delete this;
62 }
63
64 void
65 QuotaObject::UpdateSize(int64_t aSize)
66 {
67 QuotaManager* quotaManager = QuotaManager::Get();
68 NS_ASSERTION(quotaManager, "Shouldn't be null!");
69
70 MutexAutoLock lock(quotaManager->mQuotaMutex);
71
72 if (!mOriginInfo) {
73 return;
74 }
75
76 GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
77
78 if (groupInfo->IsForTemporaryStorage()) {
79 quotaManager->mTemporaryStorageUsage -= mSize;
80 }
81 groupInfo->mUsage -= mSize;
82 mOriginInfo->mUsage -= mSize;
83
84 mSize = aSize;
85
86 mOriginInfo->mUsage += mSize;
87 groupInfo->mUsage += mSize;
88 if (groupInfo->IsForTemporaryStorage()) {
89 quotaManager->mTemporaryStorageUsage += mSize;
90 }
91 }
92
93 bool
94 QuotaObject::MaybeAllocateMoreSpace(int64_t aOffset, int32_t aCount)
95 {
96 int64_t end = aOffset + aCount;
97
98 QuotaManager* quotaManager = QuotaManager::Get();
99 NS_ASSERTION(quotaManager, "Shouldn't be null!");
100
101 MutexAutoLock lock(quotaManager->mQuotaMutex);
102
103 if (mSize >= end || !mOriginInfo) {
104 return true;
105 }
106
107 GroupInfo* groupInfo = mOriginInfo->mGroupInfo;
108
109 if (groupInfo->IsForPersistentStorage()) {
110 uint64_t newUsage = mOriginInfo->mUsage - mSize + end;
111
112 if (newUsage > mOriginInfo->mLimit) {
113 // This will block the thread, but it will also drop the mutex while
114 // waiting. The mutex will be reacquired again when the waiting is
115 // finished.
116 if (!quotaManager->LockedQuotaIsLifted()) {
117 return false;
118 }
119
120 // Threads raced, the origin info removal has been done by some other
121 // thread.
122 if (!mOriginInfo) {
123 // The other thread could allocate more space.
124 if (end > mSize) {
125 mSize = end;
126 }
127
128 return true;
129 }
130
131 nsCString group = mOriginInfo->mGroupInfo->mGroup;
132 nsCString origin = mOriginInfo->mOrigin;
133
134 mOriginInfo->LockedClearOriginInfos();
135 NS_ASSERTION(!mOriginInfo,
136 "Should have cleared in LockedClearOriginInfos!");
137
138 quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_PERSISTENT,
139 group, origin);
140
141 // Some other thread could increase the size without blocking (increasing
142 // the origin usage without hitting the limit), but no more than this one.
143 NS_ASSERTION(mSize < end, "This shouldn't happen!");
144
145 mSize = end;
146
147 return true;
148 }
149
150 mOriginInfo->mUsage = newUsage;
151
152 groupInfo->mUsage = groupInfo->mUsage - mSize + end;
153
154 mSize = end;
155
156 return true;
157 }
158
159 NS_ASSERTION(groupInfo->mPersistenceType == PERSISTENCE_TYPE_TEMPORARY,
160 "Huh?");
161
162 uint64_t delta = end - mSize;
163
164 uint64_t newUsage = mOriginInfo->mUsage + delta;
165
166 // Temporary storage has no limit for origin usage (there's a group and the
167 // global limit though).
168
169 uint64_t newGroupUsage = groupInfo->mUsage + delta;
170
171 // Temporary storage has a hard limit for group usage (20 % of the global
172 // limit).
173 if (newGroupUsage > quotaManager->GetGroupLimit()) {
174 return false;
175 }
176
177 uint64_t newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage +
178 delta;
179
180 if (newTemporaryStorageUsage > quotaManager->mTemporaryStorageLimit) {
181 // This will block the thread without holding the lock while waitting.
182
183 nsAutoTArray<OriginInfo*, 10> originInfos;
184 uint64_t sizeToBeFreed =
185 quotaManager->LockedCollectOriginsForEviction(delta, originInfos);
186
187 if (!sizeToBeFreed) {
188 return false;
189 }
190
191 NS_ASSERTION(sizeToBeFreed >= delta, "Huh?");
192
193 {
194 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
195
196 for (uint32_t i = 0; i < originInfos.Length(); i++) {
197 quotaManager->DeleteTemporaryFilesForOrigin(originInfos[i]->mOrigin);
198 }
199 }
200
201 // Relocked.
202
203 NS_ASSERTION(mOriginInfo, "How come?!");
204
205 nsTArray<nsCString> origins;
206 for (uint32_t i = 0; i < originInfos.Length(); i++) {
207 OriginInfo* originInfo = originInfos[i];
208
209 NS_ASSERTION(originInfo != mOriginInfo, "Deleted itself!");
210
211 nsCString group = originInfo->mGroupInfo->mGroup;
212 nsCString origin = originInfo->mOrigin;
213 quotaManager->LockedRemoveQuotaForOrigin(PERSISTENCE_TYPE_TEMPORARY,
214 group, origin);
215
216 #ifdef DEBUG
217 originInfos[i] = nullptr;
218 #endif
219
220 origins.AppendElement(origin);
221 }
222
223 // We unlocked and relocked several times so we need to recompute all the
224 // essential variables and recheck the group limit.
225
226 delta = end - mSize;
227
228 newUsage = mOriginInfo->mUsage + delta;
229
230 newGroupUsage = groupInfo->mUsage + delta;
231
232 if (newGroupUsage > quotaManager->GetGroupLimit()) {
233 // Unfortunately some other thread increased the group usage in the
234 // meantime and we are not below the group limit anymore.
235
236 // However, the origin eviction must be finalized in this case too.
237 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
238
239 quotaManager->FinalizeOriginEviction(origins);
240
241 return false;
242 }
243
244 newTemporaryStorageUsage = quotaManager->mTemporaryStorageUsage + delta;
245
246 NS_ASSERTION(newTemporaryStorageUsage <=
247 quotaManager->mTemporaryStorageLimit, "How come?!");
248
249 // Ok, we successfully freed enough space and the operation can continue
250 // without throwing the quota error.
251
252 mOriginInfo->mUsage = newUsage;
253 groupInfo->mUsage = newGroupUsage;
254 quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;;
255
256 // Some other thread could increase the size in the meantime, but no more
257 // than this one.
258 NS_ASSERTION(mSize < end, "This shouldn't happen!");
259 mSize = end;
260
261 // Finally, release IO thread only objects and allow next synchronized
262 // ops for the evicted origins.
263 MutexAutoUnlock autoUnlock(quotaManager->mQuotaMutex);
264
265 quotaManager->FinalizeOriginEviction(origins);
266
267 return true;
268 }
269
270 mOriginInfo->mUsage = newUsage;
271 groupInfo->mUsage = newGroupUsage;
272 quotaManager->mTemporaryStorageUsage = newTemporaryStorageUsage;
273
274 mSize = end;
275
276 return true;
277 }
278
279 void
280 OriginInfo::LockedDecreaseUsage(int64_t aSize)
281 {
282 AssertCurrentThreadOwnsQuotaMutex();
283
284 mUsage -= aSize;
285
286 mGroupInfo->mUsage -= aSize;
287
288 if (mGroupInfo->IsForTemporaryStorage()) {
289 QuotaManager* quotaManager = QuotaManager::Get();
290 NS_ASSERTION(quotaManager, "Shouldn't be null!");
291
292 quotaManager->mTemporaryStorageUsage -= aSize;
293 }
294 }
295
296 // static
297 PLDHashOperator
298 OriginInfo::ClearOriginInfoCallback(const nsAString& aKey,
299 QuotaObject* aValue,
300 void* aUserArg)
301 {
302 NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
303 NS_ASSERTION(aValue, "Null pointer!");
304
305 aValue->mOriginInfo = nullptr;
306
307 return PL_DHASH_NEXT;
308 }
309
310 already_AddRefed<OriginInfo>
311 GroupInfo::LockedGetOriginInfo(const nsACString& aOrigin)
312 {
313 AssertCurrentThreadOwnsQuotaMutex();
314
315 for (uint32_t index = 0; index < mOriginInfos.Length(); index++) {
316 nsRefPtr<OriginInfo>& originInfo = mOriginInfos[index];
317
318 if (originInfo->mOrigin == aOrigin) {
319 nsRefPtr<OriginInfo> result = originInfo;
320 return result.forget();
321 }
322 }
323
324 return nullptr;
325 }
326
327 void
328 GroupInfo::LockedAddOriginInfo(OriginInfo* aOriginInfo)
329 {
330 AssertCurrentThreadOwnsQuotaMutex();
331
332 NS_ASSERTION(!mOriginInfos.Contains(aOriginInfo),
333 "Replacing an existing entry!");
334 mOriginInfos.AppendElement(aOriginInfo);
335
336 mUsage += aOriginInfo->mUsage;
337
338 if (IsForTemporaryStorage()) {
339 QuotaManager* quotaManager = QuotaManager::Get();
340 NS_ASSERTION(quotaManager, "Shouldn't be null!");
341
342 quotaManager->mTemporaryStorageUsage += aOriginInfo->mUsage;
343 }
344 }
345
346 void
347 GroupInfo::LockedRemoveOriginInfo(const nsACString& aOrigin)
348 {
349 AssertCurrentThreadOwnsQuotaMutex();
350
351 for (uint32_t index = 0; index < mOriginInfos.Length(); index++) {
352 if (mOriginInfos[index]->mOrigin == aOrigin) {
353 mUsage -= mOriginInfos[index]->mUsage;
354
355 if (IsForTemporaryStorage()) {
356 QuotaManager* quotaManager = QuotaManager::Get();
357 NS_ASSERTION(quotaManager, "Shouldn't be null!");
358
359 quotaManager->mTemporaryStorageUsage -= mOriginInfos[index]->mUsage;
360 }
361
362 mOriginInfos.RemoveElementAt(index);
363
364 return;
365 }
366 }
367 }
368
369 void
370 GroupInfo::LockedRemoveOriginInfos()
371 {
372 AssertCurrentThreadOwnsQuotaMutex();
373
374 for (uint32_t index = mOriginInfos.Length(); index > 0; index--) {
375 mUsage -= mOriginInfos[index - 1]->mUsage;
376
377 if (IsForTemporaryStorage()) {
378 QuotaManager* quotaManager = QuotaManager::Get();
379 NS_ASSERTION(quotaManager, "Shouldn't be null!");
380
381 quotaManager->mTemporaryStorageUsage -= mOriginInfos[index - 1]->mUsage;
382 }
383
384 mOriginInfos.RemoveElementAt(index - 1);
385 }
386 }
387
388 void
389 GroupInfo::LockedRemoveOriginInfosForPattern(const nsACString& aPattern)
390 {
391 AssertCurrentThreadOwnsQuotaMutex();
392
393 for (uint32_t index = mOriginInfos.Length(); index > 0; index--) {
394 if (PatternMatchesOrigin(aPattern, mOriginInfos[index - 1]->mOrigin)) {
395 mUsage -= mOriginInfos[index - 1]->mUsage;
396
397 if (IsForTemporaryStorage()) {
398 QuotaManager* quotaManager = QuotaManager::Get();
399 NS_ASSERTION(quotaManager, "Shouldn't be null!");
400
401 quotaManager->mTemporaryStorageUsage -= mOriginInfos[index - 1]->mUsage;
402 }
403
404 mOriginInfos.RemoveElementAt(index - 1);
405 }
406 }
407 }
408
409 nsRefPtr<GroupInfo>&
410 GroupInfoPair::GetGroupInfoForPersistenceType(PersistenceType aPersistenceType)
411 {
412 switch (aPersistenceType) {
413 case PERSISTENCE_TYPE_PERSISTENT:
414 return mPersistentStorageGroupInfo;
415 case PERSISTENCE_TYPE_TEMPORARY:
416 return mTemporaryStorageGroupInfo;
417
418 case PERSISTENCE_TYPE_INVALID:
419 default:
420 MOZ_CRASH("Bad persistence type value!");
421 return mPersistentStorageGroupInfo;
422 }
423 }

mercurial