summaryrefslogtreecommitdiffstats
path: root/dom/quota/DirectoryLockImpl.h
blob: 818d1c81bd45ca29c6ffb08edaea6a927c2fa639 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef DOM_QUOTA_DIRECTORYLOCKIMPL_H_
#define DOM_QUOTA_DIRECTORYLOCKIMPL_H_

#include "mozilla/InitializedOnce.h"
#include "mozilla/MozPromise.h"
#include "mozilla/dom/FlippedOnce.h"
#include "mozilla/dom/quota/CommonMetadata.h"
#include "mozilla/dom/quota/DirectoryLock.h"
#include "mozilla/dom/quota/OriginScope.h"

namespace mozilla::dom::quota {

enum class ShouldUpdateLockIdTableFlag { No, Yes };

class DirectoryLockImpl final : public ClientDirectoryLock,
                                public UniversalDirectoryLock {
  const NotNull<RefPtr<QuotaManager>> mQuotaManager;

  const Nullable<PersistenceType> mPersistenceType;
  const nsCString mSuffix;
  const nsCString mGroup;
  const OriginScope mOriginScope;
  const Nullable<Client::Type> mClientType;
  LazyInitializedOnceEarlyDestructible<
      const NotNull<RefPtr<OpenDirectoryListener>>>
      mOpenListener;
  MozPromiseHolder<BoolPromise> mAcquirePromiseHolder;

  nsTArray<NotNull<DirectoryLockImpl*>> mBlocking;
  nsTArray<NotNull<DirectoryLockImpl*>> mBlockedOn;

  const int64_t mId;

  const bool mExclusive;

  // Internal quota manager operations use this flag to prevent directory lock
  // registraction/unregistration from updating origin access time, etc.
  const bool mInternal;

  const bool mShouldUpdateLockIdTable;

  bool mRegistered;
  FlippedOnce<true> mPending;
  FlippedOnce<false> mInvalidated;

#ifdef DEBUG
  FlippedOnce<false> mAcquired;
#endif

 public:
  DirectoryLockImpl(MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
                    const Nullable<PersistenceType>& aPersistenceType,
                    const nsACString& aSuffix, const nsACString& aGroup,
                    const OriginScope& aOriginScope,
                    const Nullable<Client::Type>& aClientType, bool aExclusive,
                    bool aInternal,
                    ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag);

  static RefPtr<ClientDirectoryLock> Create(
      MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
      PersistenceType aPersistenceType,
      const quota::OriginMetadata& aOriginMetadata, Client::Type aClientType,
      bool aExclusive) {
    return Create(std::move(aQuotaManager),
                  Nullable<PersistenceType>(aPersistenceType),
                  aOriginMetadata.mSuffix, aOriginMetadata.mGroup,
                  OriginScope::FromOrigin(aOriginMetadata.mOrigin),
                  Nullable<Client::Type>(aClientType), aExclusive, false,
                  ShouldUpdateLockIdTableFlag::Yes);
  }

  static RefPtr<OriginDirectoryLock> CreateForEviction(
      MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
      PersistenceType aPersistenceType,
      const quota::OriginMetadata& aOriginMetadata) {
    MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
    MOZ_ASSERT(!aOriginMetadata.mOrigin.IsEmpty());

    return Create(std::move(aQuotaManager),
                  Nullable<PersistenceType>(aPersistenceType),
                  aOriginMetadata.mSuffix, aOriginMetadata.mGroup,
                  OriginScope::FromOrigin(aOriginMetadata.mOrigin),
                  Nullable<Client::Type>(),
                  /* aExclusive */ true, /* aInternal */ true,
                  ShouldUpdateLockIdTableFlag::No);
  }

  static RefPtr<UniversalDirectoryLock> CreateInternal(
      MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
      const Nullable<PersistenceType>& aPersistenceType,
      const OriginScope& aOriginScope,
      const Nullable<Client::Type>& aClientType, bool aExclusive) {
    return Create(std::move(aQuotaManager), aPersistenceType, ""_ns, ""_ns,
                  aOriginScope, aClientType, aExclusive, true,
                  ShouldUpdateLockIdTableFlag::Yes);
  }

  void AssertIsOnOwningThread() const
#ifdef DEBUG
      ;
#else
  {
  }
#endif

  bool IsInternal() const { return mInternal; }

  void SetRegistered(bool aRegistered) { mRegistered = aRegistered; }

  bool IsPending() const { return mPending; }

  // Ideally, we would have just one table (instead of these two:
  // QuotaManager::mDirectoryLocks and QuotaManager::mDirectoryLockIdTable) for
  // all registered locks. However, some directory locks need to be accessed off
  // the PBackground thread, so the access must be protected by the quota mutex.
  // The problem is that directory locks for eviction must be currently created
  // while the mutex lock is already acquired. So we decided to have two tables
  // for now and to not register directory locks for eviction in
  // QuotaManager::mDirectoryLockIdTable. This can be improved in future after
  // some refactoring of the mutex locking.
  bool ShouldUpdateLockIdTable() const { return mShouldUpdateLockIdTable; }

  bool ShouldUpdateLockTable() {
    return !mInternal &&
           mPersistenceType.Value() != PERSISTENCE_TYPE_PERSISTENT;
  }

  bool Overlaps(const DirectoryLockImpl& aLock) const;

  // Test whether this DirectoryLock needs to wait for the given lock.
  bool MustWaitFor(const DirectoryLockImpl& aLock) const;

  void AddBlockingLock(DirectoryLockImpl& aLock) {
    AssertIsOnOwningThread();

    mBlocking.AppendElement(WrapNotNull(&aLock));
  }

  const nsTArray<NotNull<DirectoryLockImpl*>>& GetBlockedOnLocks() {
    return mBlockedOn;
  }

  void AddBlockedOnLock(DirectoryLockImpl& aLock) {
    AssertIsOnOwningThread();

    mBlockedOn.AppendElement(WrapNotNull(&aLock));
  }

  void MaybeUnblock(DirectoryLockImpl& aLock) {
    AssertIsOnOwningThread();

    mBlockedOn.RemoveElement(&aLock);
    if (mBlockedOn.IsEmpty()) {
      NotifyOpenListener();
    }
  }

  void NotifyOpenListener();

  void Invalidate() {
    AssertIsOnOwningThread();

    mInvalidated.EnsureFlipped();
  }

  // DirectoryLock interface

  NS_INLINE_DECL_REFCOUNTING(DirectoryLockImpl, override)

  int64_t Id() const override { return mId; }

  void Acquire(RefPtr<OpenDirectoryListener> aOpenListener) override;

  RefPtr<BoolPromise> Acquire() override;

  void AcquireImmediately() override;

  void AssertIsAcquiredExclusively() override
#ifdef DEBUG
      ;
#else
  {
  }
#endif

  void Log() const override;

  // OriginDirectoryLock interface

  PersistenceType GetPersistenceType() const override {
    MOZ_DIAGNOSTIC_ASSERT(!mPersistenceType.IsNull());

    return mPersistenceType.Value();
  }

  quota::OriginMetadata OriginMetadata() const override {
    MOZ_DIAGNOSTIC_ASSERT(!mGroup.IsEmpty());

    return quota::OriginMetadata{mSuffix, mGroup, nsCString(Origin()),
                                 GetPersistenceType()};
  }

  const nsACString& Origin() const override {
    MOZ_DIAGNOSTIC_ASSERT(mOriginScope.IsOrigin());
    MOZ_DIAGNOSTIC_ASSERT(!mOriginScope.GetOrigin().IsEmpty());

    return mOriginScope.GetOrigin();
  }

  // ClientDirectoryLock interface

  Client::Type ClientType() const override {
    MOZ_DIAGNOSTIC_ASSERT(!mClientType.IsNull());
    MOZ_DIAGNOSTIC_ASSERT(mClientType.Value() < Client::TypeMax());

    return mClientType.Value();
  }

  // UniversalDirectoryLock interface

  const Nullable<PersistenceType>& NullablePersistenceType() const override {
    return mPersistenceType;
  }

  const OriginScope& GetOriginScope() const override { return mOriginScope; }

  const Nullable<Client::Type>& NullableClientType() const override {
    return mClientType;
  }

  RefPtr<ClientDirectoryLock> SpecializeForClient(
      PersistenceType aPersistenceType,
      const quota::OriginMetadata& aOriginMetadata,
      Client::Type aClientType) const override;

 private:
  ~DirectoryLockImpl();

  static RefPtr<DirectoryLockImpl> Create(
      MovingNotNull<RefPtr<QuotaManager>> aQuotaManager,
      const Nullable<PersistenceType>& aPersistenceType,
      const nsACString& aSuffix, const nsACString& aGroup,
      const OriginScope& aOriginScope,
      const Nullable<Client::Type>& aClientType, bool aExclusive,
      bool aInternal,
      ShouldUpdateLockIdTableFlag aShouldUpdateLockIdTableFlag) {
    MOZ_ASSERT_IF(aOriginScope.IsOrigin(), !aOriginScope.GetOrigin().IsEmpty());
    MOZ_ASSERT_IF(!aInternal, !aPersistenceType.IsNull());
    MOZ_ASSERT_IF(!aInternal,
                  aPersistenceType.Value() != PERSISTENCE_TYPE_INVALID);
    MOZ_ASSERT_IF(!aInternal, !aGroup.IsEmpty());
    MOZ_ASSERT_IF(!aInternal, aOriginScope.IsOrigin());
    MOZ_ASSERT_IF(!aInternal, !aClientType.IsNull());
    MOZ_ASSERT_IF(!aInternal, aClientType.Value() < Client::TypeMax());

    return MakeRefPtr<DirectoryLockImpl>(
        std::move(aQuotaManager), aPersistenceType, aSuffix, aGroup,
        aOriginScope, aClientType, aExclusive, aInternal,
        aShouldUpdateLockIdTableFlag);
  }

  void AcquireInternal();
};

}  // namespace mozilla::dom::quota

#endif  // DOM_QUOTA_DIRECTORYLOCKIMPL_H_