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
|
// 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 CHROME_BROWSER_WEB_APPLICATIONS_LOCKS_PARTITIONED_LOCK_MANAGER_H_
#define CHROME_BROWSER_WEB_APPLICATIONS_LOCKS_PARTITIONED_LOCK_MANAGER_H_
#include <iosfwd>
#include <list>
#include <memory>
#include <set>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/supports_user_data.h"
#include "base/task/sequenced_task_runner.h"
#include "chrome/browser/web_applications/locks/partitioned_lock.h"
#include "chrome/browser/web_applications/locks/partitioned_lock_id.h"
namespace base {
class Value;
}
namespace web_app {
// 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;
base::WeakPtr<PartitionedLockHolder> AsWeakPtr() {
return weak_factory.GetWeakPtr();
}
std::vector<PartitionedLock> locks;
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:
// * All calls must happen from the same sequenced task runner.
// * 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:
using LocksAcquiredCallback = base::OnceClosure;
// 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;
struct PartitionedLockRequest {
PartitionedLockRequest(PartitionedLockId lock_id, LockType type);
PartitionedLockId lock_id;
LockType type;
};
// Acquires locks for the given requests. Lock partitions are treated as
// completely independent domains. The request is aborted and the `callback`
// is not called if `locks_holder` is destroyed.
// The `callback` is guaranteed to be called asynchronously when all locks are
// granted. Locks are requested and granted in order according to the sorting
// order of `lock_id` in the request, where requesting the next lock does not
// occur until the previous lock is granted.
void AcquireLocks(base::flat_set<PartitionedLockRequest> lock_requests,
PartitionedLockHolder& locks_holder,
LocksAcquiredCallback callback,
const base::Location& location = FROM_HERE);
enum class TestLockResult { kLocked, kFree };
// Tests to see if the given lock request can be acquired.
TestLockResult TestLock(PartitionedLockRequest lock_requests);
// Gets the request location of all locks currently held and queued for the
// given requests.
std::vector<base::Location> GetHeldAndQueuedLockLocations(
const base::flat_set<PartitionedLockRequest>& requests) const;
// 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 the lock requests that are blocked on the provided `lock_id`.
std::set<PartitionedLockHolder*> GetQueuedRequests(
const PartitionedLockId& lock_id) 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&);
base::Value ToDebugValue(TransformLockIdToStringFn transform) const;
private:
struct LockRequest {
public:
LockRequest();
LockRequest(LockRequest&&) noexcept;
LockRequest(LockType type,
base::WeakPtr<PartitionedLockHolder> locks_holder,
base::OnceClosure acquire_next_lock_or_post_completion,
const base::Location& location);
~LockRequest();
LockType requested_type = LockType::kShared;
base::WeakPtr<PartitionedLockHolder> locks_holder;
base::OnceClosure acquire_next_lock_or_post_completion;
base::Location location;
};
// Represents a lock, which has a lock_id. To support shared access, there can
// be multiple acquisitions of this lock, represented in |acquired_count|.
// Also holds the pending requests for this lock.
struct Lock {
Lock();
Lock(const Lock&) = delete;
Lock(Lock&&) noexcept;
~Lock();
Lock& operator=(const Lock&) = delete;
Lock& operator=(Lock&&) noexcept;
bool CanBeAcquired(LockType lock_type) {
return acquired_count == 0 ||
(queue.empty() && this->lock_mode == LockType::kShared &&
lock_type == LockType::kShared);
}
int acquired_count = 0;
base::flat_set<base::Location> request_locations;
LockType lock_mode = LockType::kShared;
std::list<LockRequest> queue;
};
using LocksMap = base::flat_map<PartitionedLockId, Lock>;
// This must not add or remove from the `locks_` storage.
void AcquireNextLockOrPostCompletion(
std::unique_ptr<base::flat_set<PartitionedLockRequest>> requests,
base::flat_set<PartitionedLockRequest>::iterator current,
base::WeakPtr<PartitionedLockHolder> locks_holder,
base::OnceClosure on_all_acquired,
const base::Location& location);
void LockReleased(base::Location request_location, PartitionedLockId lock_id);
SEQUENCE_CHECKER(sequence_checker_);
const scoped_refptr<base::SequencedTaskRunner> task_runner_;
LocksMap locks_;
base::WeakPtrFactory<PartitionedLockManager> weak_factory_{this};
};
bool operator<(const PartitionedLockManager::PartitionedLockRequest& x,
const PartitionedLockManager::PartitionedLockRequest& y);
bool operator==(const PartitionedLockManager::PartitionedLockRequest& x,
const PartitionedLockManager::PartitionedLockRequest& y);
} // namespace web_app
#endif // CHROME_BROWSER_WEB_APPLICATIONS_LOCKS_PARTITIONED_LOCK_MANAGER_H_
|