Avi Drissman | 8ba1bad | 2022-09-13 19:22:36 | [diff] [blame] | 1 | // Copyright 2020 The Chromium Authors |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
Leonid Baraz | 2b2e0b5 | 2022-12-13 03:25:07 | [diff] [blame] | 5 | #ifndef COMPONENTS_REPORTING_RESOURCES_RESOURCE_MANAGER_H_ |
| 6 | #define COMPONENTS_REPORTING_RESOURCES_RESOURCE_MANAGER_H_ |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 7 | |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 8 | #include <atomic> |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 9 | #include <cstdint> |
Arthur Sonzogni | c571efb | 2024-01-26 20:26:18 | [diff] [blame] | 10 | #include <optional> |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 11 | #include <queue> |
| 12 | #include <utility> |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 13 | |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 14 | #include "base/functional/callback_forward.h" |
Leonid Baraz | 960ea8f | 2022-05-17 01:01:06 | [diff] [blame] | 15 | #include "base/memory/ref_counted.h" |
| 16 | #include "base/memory/scoped_refptr.h" |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 17 | #include "base/task/sequenced_task_runner.h" |
| 18 | #include "base/thread_annotations.h" |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 19 | |
| 20 | namespace reporting { |
| 21 | |
Leonid Baraz | 2b2e0b5 | 2022-12-13 03:25:07 | [diff] [blame] | 22 | // Resource management class. The class is thread-safe. |
| 23 | // Each resource instance is created with its own total size; the rest of the |
| 24 | // functionality is identical. All APIs are non-blocking. |
| 25 | class ResourceManager : public base::RefCountedThreadSafe<ResourceManager> { |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 26 | public: |
Leonid Baraz | 2b2e0b5 | 2022-12-13 03:25:07 | [diff] [blame] | 27 | explicit ResourceManager(uint64_t total_size); |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 28 | |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 29 | // Needs to be called before attempting to allocate specified size. |
| 30 | // Returns true if requested amount can be allocated. |
| 31 | // After that the caller can actually allocate it or must call |
| 32 | // |Discard| if decided not to allocate. |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 33 | bool Reserve(uint64_t size); |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 34 | |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 35 | // Reverts reservation, arranges for callbacks calls as necessary. |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 36 | // Must be called after the specified amount is released. |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 37 | void Discard(uint64_t size); |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 38 | |
| 39 | // Returns total amount. |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 40 | uint64_t GetTotal() const; |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 41 | |
| 42 | // Returns current used amount. |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 43 | uint64_t GetUsed() const; |
| 44 | |
| 45 | // Registers a callback to be invoked once there is specified amount |
| 46 | // of resource available (does not reserve it, so once called back |
| 47 | // the respective code must attempt to reserve again, and if unsuccessful, |
| 48 | // may need ot re-register the callback). |
| 49 | // Callbacks will be invoked in the context of the sequenced task runner |
| 50 | // it was registered in. |
| 51 | void RegisterCallback(uint64_t size, base::OnceClosure cb); |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 52 | |
| 53 | // Test only: Sets non-default usage limit. |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 54 | void Test_SetTotal(uint64_t test_total); |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 55 | |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 56 | private: |
Leonid Baraz | 2b2e0b5 | 2022-12-13 03:25:07 | [diff] [blame] | 57 | friend class base::RefCountedThreadSafe<ResourceManager>; |
Leonid Baraz | 960ea8f | 2022-05-17 01:01:06 | [diff] [blame] | 58 | |
Leonid Baraz | 2b2e0b5 | 2022-12-13 03:25:07 | [diff] [blame] | 59 | ~ResourceManager(); |
Leonid Baraz | 0be7196b | 2022-12-13 00:02:52 | [diff] [blame] | 60 | |
| 61 | // Flushes as many callbacks as possible given the current resource |
| 62 | // availability. Callbacks only signal that resource may be available, |
| 63 | // the resumed task must try to actually reserve it after that. |
| 64 | void FlushCallbacks(); |
| 65 | |
| 66 | uint64_t total_; // Remains constant in prod code, changes only in tests. |
| 67 | std::atomic<uint64_t> used_{0}; |
| 68 | |
| 69 | // Sequenced task runner for callbacks handling (not for calling callbacks!) |
| 70 | const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; |
| 71 | SEQUENCE_CHECKER(sequence_checker_); |
| 72 | |
| 73 | // Queue of pairs [size, callback]. |
| 74 | // When `Discard` leaves enough space available (even momentarily), |
| 75 | // calls as many of the callbacks as fit in that size, in the queue order. |
| 76 | // Note that in a meantime reservation may change - the called back code |
| 77 | // must attempt reservation before using it. |
| 78 | std::queue<std::pair<uint64_t, base::OnceClosure>> resource_callbacks_ |
| 79 | GUARDED_BY_CONTEXT(sequence_checker_); |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 80 | }; |
| 81 | |
| 82 | // Moveable RAII class used for scoped Reserve-Discard. |
| 83 | // |
| 84 | // Usage: |
| 85 | // { |
Leonid Baraz | 960ea8f | 2022-05-17 01:01:06 | [diff] [blame] | 86 | // ScopedReservation reservation(1024u, options.memory_resource()); |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 87 | // if (!reservation.reserved()) { |
| 88 | // // Allocation failed. |
| 89 | // return; |
| 90 | // } |
| 91 | // // Allocation succeeded. |
| 92 | // ... |
| 93 | // } // Automatically discarded. |
| 94 | // |
Leonid Baraz | a206429 | 2022-07-07 00:18:41 | [diff] [blame] | 95 | // Can be handed over to another owner by move-constructor or using HandOver |
| 96 | // method: |
| 97 | // { |
| 98 | // ScopedReservation summary; |
| 99 | // for (const uint64_t size : sizes) { |
| 100 | // ScopedReservation single_reservation(size, resource); |
| 101 | // ... |
| 102 | // summary.HandOver(single_reservation); |
| 103 | // } |
| 104 | // } |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 105 | class ScopedReservation { |
| 106 | public: |
Leonid Baraz | a206429 | 2022-07-07 00:18:41 | [diff] [blame] | 107 | // Zero-size reservation with no resource interface attached. |
| 108 | // reserved() returns false. |
| 109 | ScopedReservation() noexcept; |
| 110 | // Specified reservation, must have resource interface attached. |
Leonid Baraz | 2b2e0b5 | 2022-12-13 03:25:07 | [diff] [blame] | 111 | ScopedReservation(uint64_t size, |
| 112 | scoped_refptr<ResourceManager> resource_manager) noexcept; |
Leonid Baraz | b455392 | 2022-07-12 23:32:53 | [diff] [blame] | 113 | // New reservation on the same resource interface as |other_reservation|. |
| 114 | ScopedReservation(uint64_t size, |
| 115 | const ScopedReservation& other_reservation) noexcept; |
| 116 | // Move constructor. |
Leonid Baraz | af0a319f | 2022-06-15 02:17:47 | [diff] [blame] | 117 | ScopedReservation(ScopedReservation&& other) noexcept; |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 118 | ScopedReservation(const ScopedReservation& other) = delete; |
Leonid Baraz | af0a319f | 2022-06-15 02:17:47 | [diff] [blame] | 119 | ScopedReservation& operator=(ScopedReservation&& other) = delete; |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 120 | ScopedReservation& operator=(const ScopedReservation& other) = delete; |
| 121 | ~ScopedReservation(); |
| 122 | |
| 123 | bool reserved() const; |
Leonid Baraz | af0a319f | 2022-06-15 02:17:47 | [diff] [blame] | 124 | |
| 125 | // Reduces reservation to |new_size|. |
Santiago Castano Moreno | 150ba5f | 2021-11-12 21:34:52 | [diff] [blame] | 126 | bool Reduce(uint64_t new_size); |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 127 | |
Leonid Baraz | af0a319f | 2022-06-15 02:17:47 | [diff] [blame] | 128 | // Adds |other| to |this| without assigning or releasing any reservation. |
| 129 | // Used for seamless transition from one reservation to another (more generic |
| 130 | // than std::move). Resets |other| to non-reserved state upon return from this |
| 131 | // method. |
| 132 | void HandOver(ScopedReservation& other); |
| 133 | |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 134 | private: |
Leonid Baraz | 2b2e0b5 | 2022-12-13 03:25:07 | [diff] [blame] | 135 | scoped_refptr<ResourceManager> resource_manager_; |
Arthur Sonzogni | c571efb | 2024-01-26 20:26:18 | [diff] [blame] | 136 | std::optional<uint64_t> size_; |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 137 | }; |
| 138 | |
Leonid Baraz | 3e8af4b | 2020-11-23 19:04:47 | [diff] [blame] | 139 | } // namespace reporting |
| 140 | |
Leonid Baraz | 2b2e0b5 | 2022-12-13 03:25:07 | [diff] [blame] | 141 | #endif // COMPONENTS_REPORTING_RESOURCES_RESOURCE_MANAGER_H_ |