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
),
26 mOriginScope(aOriginScope
),
27 mClientType(aClientType
),
28 mId(mQuotaManager
->GenerateDirectoryLockId()),
29 mExclusive(aExclusive
),
31 mShouldUpdateLockIdTable(aShouldUpdateLockIdTableFlag
==
32 ShouldUpdateLockIdTableFlag::Yes
),
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 for (NotNull
<RefPtr
<DirectoryLockImpl
>> blockingLock
: mBlocking
) {
49 blockingLock
->MaybeUnblock(*this);
55 mQuotaManager
->UnregisterDirectoryLock(*this);
58 MOZ_ASSERT(!mRegistered
);
63 void DirectoryLockImpl::AssertIsOnOwningThread() const {
64 mQuotaManager
->AssertIsOnOwningThread();
69 bool DirectoryLockImpl::Overlaps(const DirectoryLockImpl
& aLock
) const {
70 AssertIsOnOwningThread();
72 // If the persistence types don't overlap, the op can proceed.
73 if (!aLock
.mPersistenceType
.IsNull() && !mPersistenceType
.IsNull() &&
74 aLock
.mPersistenceType
.Value() != mPersistenceType
.Value()) {
78 // If the origin scopes don't overlap, the op can proceed.
79 bool match
= aLock
.mOriginScope
.Matches(mOriginScope
);
84 // If the client types don't overlap, the op can proceed.
85 if (!aLock
.mClientType
.IsNull() && !mClientType
.IsNull() &&
86 aLock
.mClientType
.Value() != mClientType
.Value()) {
90 // Otherwise, when all attributes overlap (persistence type, origin scope and
91 // client type) the op must wait.
95 bool DirectoryLockImpl::MustWaitFor(const DirectoryLockImpl
& aLock
) const {
96 AssertIsOnOwningThread();
98 // Waiting is never required if the ops in comparison represent shared locks.
99 if (!aLock
.mExclusive
&& !mExclusive
) {
103 // Wait if the ops overlap.
104 return Overlaps(aLock
);
107 void DirectoryLockImpl::NotifyOpenListener() {
108 AssertIsOnOwningThread();
111 (*mOpenListener
)->DirectoryLockFailed();
114 ->DirectoryLockAcquired(static_cast<UniversalDirectoryLock
*>(this));
117 mOpenListener
.destroy();
119 mQuotaManager
->RemovePendingDirectoryLock(*this);
124 void DirectoryLockImpl::Acquire(RefPtr
<OpenDirectoryListener
> aOpenListener
) {
125 AssertIsOnOwningThread();
126 MOZ_ASSERT(aOpenListener
);
128 mOpenListener
.init(WrapNotNullUnchecked(std::move(aOpenListener
)));
130 mQuotaManager
->AddPendingDirectoryLock(*this);
132 // See if this lock needs to wait.
133 bool blocked
= false;
135 // XXX It is probably unnecessary to iterate this in reverse order.
136 for (DirectoryLockImpl
* const existingLock
:
137 Reversed(mQuotaManager
->mDirectoryLocks
)) {
138 if (MustWaitFor(*existingLock
)) {
139 existingLock
->AddBlockingLock(*this);
140 AddBlockedOnLock(*existingLock
);
145 mQuotaManager
->RegisterDirectoryLock(*this);
147 // Otherwise, notify the open listener immediately.
149 NotifyOpenListener();
153 if (!mExclusive
|| !mInternal
) {
157 // All the locks that block this new exclusive internal lock need to be
158 // invalidated. We also need to notify clients to abort operations for them.
159 QuotaManager::DirectoryLockIdTableArray lockIds
;
160 lockIds
.SetLength(Client::TypeMax());
162 const auto& blockedOnLocks
= GetBlockedOnLocks();
163 MOZ_ASSERT(!blockedOnLocks
.IsEmpty());
165 for (DirectoryLockImpl
* blockedOnLock
: blockedOnLocks
) {
166 if (!blockedOnLock
->IsInternal()) {
167 blockedOnLock
->Invalidate();
169 // Clients don't have to handle pending locks. Invalidation is sufficient
170 // in that case (once a lock is ready and the listener needs to be
171 // notified, we will call DirectoryLockFailed instead of
172 // DirectoryLockAcquired which should release any remaining references to
174 if (!blockedOnLock
->IsPending()) {
175 lockIds
[blockedOnLock
->ClientType()].Put(blockedOnLock
->Id());
180 mQuotaManager
->AbortOperationsForLocks(lockIds
);
183 void DirectoryLockImpl::AcquireImmediately() {
184 AssertIsOnOwningThread();
187 for (const DirectoryLockImpl
* const existingLock
:
188 mQuotaManager
->mDirectoryLocks
) {
189 MOZ_ASSERT(!MustWaitFor(*existingLock
));
193 mQuotaManager
->RegisterDirectoryLock(*this);
196 RefPtr
<ClientDirectoryLock
> DirectoryLockImpl::Specialize(
197 PersistenceType aPersistenceType
,
198 const quota::OriginMetadata
& aOriginMetadata
,
199 Client::Type aClientType
) const {
200 AssertIsOnOwningThread();
201 MOZ_ASSERT(aPersistenceType
!= PERSISTENCE_TYPE_INVALID
);
202 MOZ_ASSERT(!aOriginMetadata
.mGroup
.IsEmpty());
203 MOZ_ASSERT(!aOriginMetadata
.mOrigin
.IsEmpty());
204 MOZ_ASSERT(aClientType
< Client::TypeMax());
205 MOZ_ASSERT(!mOpenListener
);
206 MOZ_ASSERT(mBlockedOn
.IsEmpty());
208 if (NS_WARN_IF(mExclusive
)) {
212 RefPtr
<DirectoryLockImpl
> lock
= Create(
213 mQuotaManager
, Nullable
<PersistenceType
>(aPersistenceType
),
214 aOriginMetadata
.mSuffix
, aOriginMetadata
.mGroup
,
215 OriginScope::FromOrigin(aOriginMetadata
.mOrigin
),
216 Nullable
<Client::Type
>(aClientType
),
217 /* aExclusive */ false, mInternal
, ShouldUpdateLockIdTableFlag::Yes
);
218 if (NS_WARN_IF(!Overlaps(*lock
))) {
223 for (DirectoryLockImpl
* const existingLock
:
224 Reversed(mQuotaManager
->mDirectoryLocks
)) {
225 if (existingLock
!= this && !existingLock
->MustWaitFor(*this)) {
226 MOZ_ASSERT(!existingLock
->MustWaitFor(*lock
));
231 for (const auto& blockedLock
: mBlocking
) {
232 if (blockedLock
->MustWaitFor(*lock
)) {
233 lock
->AddBlockingLock(*blockedLock
);
234 blockedLock
->AddBlockedOnLock(*lock
);
238 mQuotaManager
->RegisterDirectoryLock(*lock
);
247 void DirectoryLockImpl::Log() const {
248 AssertIsOnOwningThread();
250 if (!QM_LOG_TEST()) {
254 QM_LOG(("DirectoryLockImpl [%p]", this));
256 nsCString persistenceType
;
257 if (mPersistenceType
.IsNull()) {
258 persistenceType
.AssignLiteral("null");
260 persistenceType
.Assign(PersistenceTypeToString(mPersistenceType
.Value()));
262 QM_LOG((" mPersistenceType: %s", persistenceType
.get()));
264 QM_LOG((" mGroup: %s", mGroup
.get()));
266 nsCString originScope
;
267 if (mOriginScope
.IsOrigin()) {
268 originScope
.AssignLiteral("origin:");
269 originScope
.Append(mOriginScope
.GetOrigin());
270 } else if (mOriginScope
.IsPrefix()) {
271 originScope
.AssignLiteral("prefix:");
272 originScope
.Append(mOriginScope
.GetOriginNoSuffix());
273 } else if (mOriginScope
.IsPattern()) {
274 originScope
.AssignLiteral("pattern:");
275 // Can't call GetJSONPattern since it only works on the main thread.
277 MOZ_ASSERT(mOriginScope
.IsNull());
278 originScope
.AssignLiteral("null");
280 QM_LOG((" mOriginScope: %s", originScope
.get()));
282 const auto clientType
= mClientType
.IsNull()
283 ? nsAutoCString
{"null"_ns
}
284 : Client::TypeToText(mClientType
.Value());
285 QM_LOG((" mClientType: %s", clientType
.get()));
287 nsCString blockedOnString
;
288 for (auto blockedOn
: mBlockedOn
) {
289 blockedOnString
.Append(
290 nsPrintfCString(" [%p]", static_cast<void*>(blockedOn
)));
292 QM_LOG((" mBlockedOn:%s", blockedOnString
.get()));
294 QM_LOG((" mExclusive: %d", mExclusive
));
296 QM_LOG((" mInternal: %d", mInternal
));
298 QM_LOG((" mInvalidated: %d", static_cast<bool>(mInvalidated
)));
300 for (auto blockedOn
: mBlockedOn
) {
305 } // namespace mozilla::dom::quota