File: partitioned_lock_manager.h

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (204 lines) | stat: -rw-r--r-- 8,495 bytes parent folder | download | duplicates (5)
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
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_SERVICES_STORAGE_INDEXED_DB_LOCKS_PARTITIONED_LOCK_MANAGER_H_
#define COMPONENTS_SERVICES_STORAGE_INDEXED_DB_LOCKS_PARTITIONED_LOCK_MANAGER_H_

#include <iosfwd>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <vector>

#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/supports_user_data.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock.h"
#include "components/services/storage/indexed_db/locks/partitioned_lock_id.h"

namespace content::indexed_db {

// Used to receive and hold locks from a PartitionedLockManager. This struct
// enables the PartitionedLock objects to always live in the destination of the
// caller's choosing (as opposed to having the locks be an argument in the
// callback, where they could be owned by the task scheduler).
//
// This class must be used and destructed on the same sequence as the
// PartitionedLockManager.
struct PartitionedLockHolder : public base::SupportsUserData {
  PartitionedLockHolder();
  PartitionedLockHolder(const PartitionedLockHolder&) = delete;
  PartitionedLockHolder& operator=(const PartitionedLockHolder&) = delete;
  ~PartitionedLockHolder() override;

  void CancelLockRequest();

  std::vector<PartitionedLock> locks;
  base::OnceClosure on_cancel;
  base::WeakPtrFactory<PartitionedLockHolder> weak_factory{this};
};

// Holds locks of the scopes system. Granted locks are represented by the
// |PartitionedLock| class.
//
//  Invariants for the lock management system:
// * Locks are granted in the order in which they are requested.
// * Locks held by an entity must be acquired all at once. If more locks are
//   needed (where old locks will continue to be held), then all locks must be
//   released first, and then all necessary locks acquired in one acquisition
//   call.
class PartitionedLockManager {
 public:
  // Shared locks can share access to a lock id, while exclusive locks
  // require that they are the only lock for their lock id.
  enum class LockType { kShared, kExclusive };

  // Grabs the current task runner that we are running on to be used for the
  // lock acquisition callbacks.
  PartitionedLockManager();

  PartitionedLockManager(const PartitionedLockManager&) = delete;
  PartitionedLockManager& operator=(const PartitionedLockManager&) = delete;

  ~PartitionedLockManager();

  int64_t LocksHeldForTesting() const;
  int64_t RequestsWaitingForTesting() const;
  int64_t RequestsWaitingForMetrics() const;

  // Acquires locks for the given requests. Lock partitions are treated as
  // completely independent domains.
  struct PartitionedLockRequest {
    PartitionedLockRequest(PartitionedLockId lock_id, LockType type);

    friend bool operator==(const PartitionedLockRequest&,
                           const PartitionedLockRequest&) = default;
    friend auto operator<=>(const PartitionedLockRequest&,
                            const PartitionedLockRequest&) = default;

    PartitionedLockId lock_id;
    LockType type;
  };
  // Acquires locks for the given requests.
  // `compare_priority` must return true iff this acquisition has a *higher*
  // priority than the one represented by the passed holder, and should skip
  // ahead of it in line (`request_queue_`). This property need not be
  // transitive. May be null, which is equivalent to always returning false.
  void AcquireLocks(base::flat_set<PartitionedLockRequest> lock_requests,
                    PartitionedLockHolder& locks_holder,
                    base::OnceClosure callback,
                    base::RepeatingCallback<bool(const PartitionedLockHolder&)>
                        compare_priority = {});

  enum class TestLockResult { kLocked, kFree };
  // Tests to see if the given lock request can be acquired.
  TestLockResult TestLock(PartitionedLockRequest lock_requests);

  // Filter out the list of `PartitionedLockId`s that cannot be acquired given
  // the list of `PartitionedLockRequest`.
  // See `Lock::CanBeAcquired()`.
  std::vector<PartitionedLockId> GetUnacquirableLocks(
      std::vector<PartitionedLockRequest>& lock_requests);

  // Returns true if `held_locks` are blocking any queued request. Only requests
  // for which `filter` returns true are considered. It's possible for
  // `blocked_requests` to be very large, so this is intended to be as efficient
  // as possible.
  bool IsBlockingAnyRequest(
      const base::flat_set<PartitionedLockId>& held_locks,
      base::RepeatingCallback<bool(PartitionedLockHolder*)> filter) const;

  // Outputs the lock state (held & requested locks) into a debug value,
  // suitable for printing an 'internals' or to print during debugging. The
  // `transform` is used to change the lock ids to human-readable values.
  // Note: The human-readable values MUST be unique per lock id, and if to lock
  // ids resolve to the same string, then this function will DCHECK.
  using TransformLockIdToStringFn = std::string(const PartitionedLockId&);

 private:
  // Metadata representing the state of a lockable entity, which is in turn
  // defined by an ID (`PartitionedLockId`). To support shared access, there can
  // be multiple acquisitions of this lock, represented in |acquired_count|.
  struct LockState {
    LockState();
    LockState(const LockState&) = delete;
    LockState(LockState&&) noexcept;
    ~LockState();
    LockState& operator=(const LockState&) = delete;
    LockState& operator=(LockState&&) noexcept;

    bool CanBeAcquired(LockType lock_type) {
      return acquired_count == 0 || (this->access_mode == LockType::kShared &&
                                     lock_type == LockType::kShared);
    }

    // The number of holders sharing the lock.
    int acquired_count = 0;

    // The current access mode. If kExclusive, `acquired_count` must not be more
    // than 1. If `acquired_count` is zero, this is meaningless.
    LockType access_mode = LockType::kShared;
  };

  // Represents a request to grab a number of locks.
  struct AcquisitionRequest {
    AcquisitionRequest();
    ~AcquisitionRequest();

    AcquisitionRequest(AcquisitionRequest&&);
    AcquisitionRequest& operator=(AcquisitionRequest&&) = default;

    // To be called when the locks are all acquired.
    base::OnceClosure acquired_callback;

    // The entities that the request seeks to lock.
    base::flat_set<PartitionedLockRequest> lock_requests;

    // Ownership of the locks will be transferred to this object.
    base::WeakPtr<PartitionedLockHolder> locks_holder;
  };

  // Returns true if the requests are overlapping, i.e. they couldn't
  // simultaneously be filled.
  static bool RequestsAreOverlapping(
      const base::flat_set<PartitionedLockRequest>& requests_a,
      const base::flat_set<PartitionedLockRequest>& requests_b);

  // If locks can be granted to the requester at `requests_iter`, then grant
  // those locks and remove the requester from `request_queue_`, returning an
  // iter pointing to the next request after where the old one was in the list.
  // If locks can't be granted, return an iterator pointing to the next request
  // in the queue (i.e. one after `requests_iter`).
  std::list<AcquisitionRequest>::iterator MaybeGrantLocksAndIterate(
      std::list<AcquisitionRequest>::iterator requests_iter,
      bool notify_synchronously = false);

  void AcquireLock(PartitionedLockRequest request,
                   PartitionedLockHolder& locks_holder);

  bool CanAcquireLock(PartitionedLockId lock_id, LockType type);

  // Called when an acquisition request is to be dropped. This corresponds to
  // `AcquisitionRequest::locks_holder` becoming null.
  void LockRequestCancelled();

  // Called when a granted lock has been released.
  void LockReleased(PartitionedLockId lock_id);

  // The set of all known lockable entities and their current state.
  std::map<PartitionedLockId, LockState> locks_;

  // This queue is FIFO by default, but some requests may cut ahead in line as
  // determined by `compare_priority`.
  std::list<AcquisitionRequest> request_queue_;

  base::WeakPtrFactory<PartitionedLockManager> weak_factory_{this};
};

}  // namespace content::indexed_db

#endif  // COMPONENTS_SERVICES_STORAGE_INDEXED_DB_LOCKS_PARTITIONED_LOCK_MANAGER_H_