Bug 1700051: part 46) Const-qualify `mozInlineSpellStatus::mAnchorRange`. r=smaug
[gecko.git] / dom / quota / DirectoryLockImpl.cpp
blob7bd84ba49811dd344290aa8495b2291648ed6386
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 "DirectoryLockImpl.h"
9 #include "mozilla/ReverseIterator.h"
10 #include "mozilla/dom/quota/Client.h"
11 #include "mozilla/dom/quota/QuotaManager.h"
13 namespace mozilla::dom::quota {
15 DirectoryLockImpl::DirectoryLockImpl(
16 MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
17 const Nullable<PersistenceType>& aPersistenceType,
18 const nsACString& aSuffix, const nsACString& aGroup,
19 const OriginScope& aOriginScope, const Nullable<Client::Type>& aClientType,
20 const bool aExclusive, const bool aInternal,
21 const ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag)
22 : mQuotaManager(std::move(aQuotaManager)),
23 mPersistenceType(aPersistenceType),
24 mSuffix(aSuffix),
25 mGroup(aGroup),
26 mOriginScope(aOriginScope),
27 mClientType(aClientType),
28 mId(mQuotaManager->GenerateDirectoryLockId()),
29 mExclusive(aExclusive),
30 mInternal(aInternal),
31 mShouldUpdateLockIdTable(aShouldUpdateLockIdTableFlag ==
32 ShouldUpdateLockIdTableFlag::Yes),
33 mRegistered(false) {
34 AssertIsOnOwningThread();
35 MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
36 MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
37 MOZ_ASSERT_IF(!aInternal,
38 aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
39 MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
40 MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
41 MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
42 MOZ_ASSERT_IF(!aInternal, aClientType.Value() < Client::TypeMax());
45 DirectoryLockImpl::~DirectoryLockImpl() {
46 AssertIsOnOwningThread();
48 // We must call UnregisterDirectoryLock before unblocking other locks because
49 // UnregisterDirectoryLock also updates the origin last access time and the
50 // access flag (if the last lock for given origin is unregistered). One of the
51 // blocked locks could be requested by the clear/reset operation which stores
52 // cached information about origins in storage.sqlite. So if the access flag
53 // is not updated before unblocking the lock for reset/clear, we might store
54 // invalid information which can lead to omitting origin initialization during
55 // next temporary storage initialization.
56 if (mRegistered) {
57 mQuotaManager->UnregisterDirectoryLock(*this);
60 MOZ_ASSERT(!mRegistered);
62 for (NotNull<RefPtr<DirectoryLockImpl>> blockingLock : mBlocking) {
63 blockingLock->MaybeUnblock(*this);
66 mBlocking.Clear();
69 #ifdef DEBUG
71 void DirectoryLockImpl::AssertIsOnOwningThread() const {
72 mQuotaManager->AssertIsOnOwningThread();
75 #endif // DEBUG
77 bool DirectoryLockImpl::Overlaps(const DirectoryLockImpl& aLock) const {
78 AssertIsOnOwningThread();
80 // If the persistence types don't overlap, the op can proceed.
81 if (!aLock.mPersistenceType.IsNull() && !mPersistenceType.IsNull() &&
82 aLock.mPersistenceType.Value() != mPersistenceType.Value()) {
83 return false;
86 // If the origin scopes don't overlap, the op can proceed.
87 bool match = aLock.mOriginScope.Matches(mOriginScope);
88 if (!match) {
89 return false;
92 // If the client types don't overlap, the op can proceed.
93 if (!aLock.mClientType.IsNull() && !mClientType.IsNull() &&
94 aLock.mClientType.Value() != mClientType.Value()) {
95 return false;
98 // Otherwise, when all attributes overlap (persistence type, origin scope and
99 // client type) the op must wait.
100 return true;
103 bool DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl& aLock) const {
104 AssertIsOnOwningThread();
106 // Waiting is never required if the ops in comparison represent shared locks.
107 if (!aLock.mExclusive && !mExclusive) {
108 return false;
111 // Wait if the ops overlap.
112 return Overlaps(aLock);
115 void DirectoryLockImpl::NotifyOpenListener() {
116 AssertIsOnOwningThread();
118 if (mInvalidated) {
119 (*mOpenListener)->DirectoryLockFailed();
120 } else {
121 (*mOpenListener)
122 ->DirectoryLockAcquired(static_cast<UniversalDirectoryLock*>(this));
125 mOpenListener.destroy();
127 mQuotaManager->RemovePendingDirectoryLock(*this);
129 mPending.Flip();
132 void DirectoryLockImpl::Acquire(RefPtr<OpenDirectoryListener> aOpenListener) {
133 AssertIsOnOwningThread();
134 MOZ_ASSERT(aOpenListener);
136 mOpenListener.init(WrapNotNullUnchecked(std::move(aOpenListener)));
138 mQuotaManager->AddPendingDirectoryLock(*this);
140 // See if this lock needs to wait.
141 bool blocked = false;
143 // XXX It is probably unnecessary to iterate this in reverse order.
144 for (DirectoryLockImpl* const existingLock :
145 Reversed(mQuotaManager->mDirectoryLocks)) {
146 if (MustWaitFor(*existingLock)) {
147 existingLock->AddBlockingLock(*this);
148 AddBlockedOnLock(*existingLock);
149 blocked = true;
153 mQuotaManager->RegisterDirectoryLock(*this);
155 // Otherwise, notify the open listener immediately.
156 if (!blocked) {
157 NotifyOpenListener();
158 return;
161 if (!mExclusive || !mInternal) {
162 return;
165 // All the locks that block this new exclusive internal lock need to be
166 // invalidated. We also need to notify clients to abort operations for them.
167 QuotaManager::DirectoryLockIdTableArray lockIds;
168 lockIds.SetLength(Client::TypeMax());
170 const auto& blockedOnLocks = GetBlockedOnLocks();
171 MOZ_ASSERT(!blockedOnLocks.IsEmpty());
173 for (DirectoryLockImpl* blockedOnLock : blockedOnLocks) {
174 if (!blockedOnLock->IsInternal()) {
175 blockedOnLock->Invalidate();
177 // Clients don't have to handle pending locks. Invalidation is sufficient
178 // in that case (once a lock is ready and the listener needs to be
179 // notified, we will call DirectoryLockFailed instead of
180 // DirectoryLockAcquired which should release any remaining references to
181 // the lock).
182 if (!blockedOnLock->IsPending()) {
183 lockIds[blockedOnLock->ClientType()].Put(blockedOnLock->Id());
188 mQuotaManager->AbortOperationsForLocks(lockIds);
191 void DirectoryLockImpl::AcquireImmediately() {
192 AssertIsOnOwningThread();
194 #ifdef DEBUG
195 for (const DirectoryLockImpl* const existingLock :
196 mQuotaManager->mDirectoryLocks) {
197 MOZ_ASSERT(!MustWaitFor(*existingLock));
199 #endif
201 mQuotaManager->RegisterDirectoryLock(*this);
204 RefPtr<ClientDirectoryLock> DirectoryLockImpl::Specialize(
205 PersistenceType aPersistenceType,
206 const quota::OriginMetadata& aOriginMetadata,
207 Client::Type aClientType) const {
208 AssertIsOnOwningThread();
209 MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
210 MOZ_ASSERT(!aOriginMetadata.mGroup.IsEmpty());
211 MOZ_ASSERT(!aOriginMetadata.mOrigin.IsEmpty());
212 MOZ_ASSERT(aClientType < Client::TypeMax());
213 MOZ_ASSERT(!mOpenListener);
214 MOZ_ASSERT(mBlockedOn.IsEmpty());
216 if (NS_WARN_IF(mExclusive)) {
217 return nullptr;
220 RefPtr<DirectoryLockImpl> lock = Create(
221 mQuotaManager, Nullable<PersistenceType>(aPersistenceType),
222 aOriginMetadata.mSuffix, aOriginMetadata.mGroup,
223 OriginScope::FromOrigin(aOriginMetadata.mOrigin),
224 Nullable<Client::Type>(aClientType),
225 /* aExclusive */ false, mInternal, ShouldUpdateLockIdTableFlag::Yes);
226 if (NS_WARN_IF(!Overlaps(*lock))) {
227 return nullptr;
230 #ifdef DEBUG
231 for (DirectoryLockImpl* const existingLock :
232 Reversed(mQuotaManager->mDirectoryLocks)) {
233 if (existingLock != this && !existingLock->MustWaitFor(*this)) {
234 MOZ_ASSERT(!existingLock->MustWaitFor(*lock));
237 #endif
239 for (const auto& blockedLock : mBlocking) {
240 if (blockedLock->MustWaitFor(*lock)) {
241 lock->AddBlockingLock(*blockedLock);
242 blockedLock->AddBlockedOnLock(*lock);
246 mQuotaManager->RegisterDirectoryLock(*lock);
248 if (mInvalidated) {
249 lock->Invalidate();
252 return lock;
255 void DirectoryLockImpl::Log() const {
256 AssertIsOnOwningThread();
258 if (!QM_LOG_TEST()) {
259 return;
262 QM_LOG(("DirectoryLockImpl [%p]", this));
264 nsCString persistenceType;
265 if (mPersistenceType.IsNull()) {
266 persistenceType.AssignLiteral("null");
267 } else {
268 persistenceType.Assign(PersistenceTypeToString(mPersistenceType.Value()));
270 QM_LOG((" mPersistenceType: %s", persistenceType.get()));
272 QM_LOG((" mGroup: %s", mGroup.get()));
274 nsCString originScope;
275 if (mOriginScope.IsOrigin()) {
276 originScope.AssignLiteral("origin:");
277 originScope.Append(mOriginScope.GetOrigin());
278 } else if (mOriginScope.IsPrefix()) {
279 originScope.AssignLiteral("prefix:");
280 originScope.Append(mOriginScope.GetOriginNoSuffix());
281 } else if (mOriginScope.IsPattern()) {
282 originScope.AssignLiteral("pattern:");
283 // Can't call GetJSONPattern since it only works on the main thread.
284 } else {
285 MOZ_ASSERT(mOriginScope.IsNull());
286 originScope.AssignLiteral("null");
288 QM_LOG((" mOriginScope: %s", originScope.get()));
290 const auto clientType = mClientType.IsNull()
291 ? nsAutoCString{"null"_ns}
292 : Client::TypeToText(mClientType.Value());
293 QM_LOG((" mClientType: %s", clientType.get()));
295 nsCString blockedOnString;
296 for (auto blockedOn : mBlockedOn) {
297 blockedOnString.Append(
298 nsPrintfCString(" [%p]", static_cast<void*>(blockedOn)));
300 QM_LOG((" mBlockedOn:%s", blockedOnString.get()));
302 QM_LOG((" mExclusive: %d", mExclusive));
304 QM_LOG((" mInternal: %d", mInternal));
306 QM_LOG((" mInvalidated: %d", static_cast<bool>(mInvalidated)));
308 for (auto blockedOn : mBlockedOn) {
309 blockedOn->Log();
313 } // namespace mozilla::dom::quota