File: discardable_shared_memory_manager.h

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 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 (211 lines) | stat: -rw-r--r-- 8,374 bytes parent folder | download | duplicates (9)
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
// Copyright 2014 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_DISCARDABLE_MEMORY_SERVICE_DISCARDABLE_SHARED_MEMORY_MANAGER_H_
#define COMPONENTS_DISCARDABLE_MEMORY_SERVICE_DISCARDABLE_SHARED_MEMORY_MANAGER_H_

#include <stddef.h>
#include <stdint.h>

#include <memory>
#include <unordered_map>
#include <vector>

#include "base/format_macros.h"
#include "base/functional/callback.h"
#include "base/memory/discardable_memory_allocator.h"
#include "base/memory/discardable_shared_memory.h"
#include "base/memory/memory_pressure_listener.h"
#include "base/memory/ref_counted.h"
#include "base/memory/unsafe_shared_memory_region.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process_handle.h"
#include "base/synchronization/lock.h"
#include "base/task/current_thread.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/thread_annotations.h"
#include "base/trace_event/memory_dump_provider.h"
#include "components/discardable_memory/common/discardable_memory_export.h"
#include "components/discardable_memory/public/mojom/discardable_shared_memory_manager.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"

namespace base {
class WaitableEvent;
}

namespace discardable_memory {

namespace {
class TestDiscardableSharedMemoryManager;
}  // namespace

// Implementation of DiscardableMemoryAllocator that allocates and manages
// discardable memory segments for the process which hosts this class, and
// for remote processes which request discardable memory from this class via
// IPC.
// This class is thread-safe and instances can safely be used on any thread.
class DISCARDABLE_MEMORY_EXPORT DiscardableSharedMemoryManager
    : public base::DiscardableMemoryAllocator,
      public base::trace_event::MemoryDumpProvider,
      public base::CurrentThread::DestructionObserver {
 public:
  DiscardableSharedMemoryManager();

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

  ~DiscardableSharedMemoryManager() override;

  // Returns the global instance of DiscardableSharedMemoryManager, usable from
  // any thread. May return null if no DiscardableSharedMemoryManager has been
  // created in the current process.
  static DiscardableSharedMemoryManager* Get();

  // Bind the manager to a mojo interface receiver.
  void Bind(
      mojo::PendingReceiver<mojom::DiscardableSharedMemoryManager> receiver);

  // Overridden from base::DiscardableMemoryAllocator:
  std::unique_ptr<base::DiscardableMemory> AllocateLockedDiscardableMemory(
      size_t size) override;

  // Overridden from base::trace_event::MemoryDumpProvider:
  bool OnMemoryDump(const base::trace_event::MemoryDumpArgs& args,
                    base::trace_event::ProcessMemoryDump* pmd) override;

  // This allocates a discardable memory segment for |process_handle|.
  // A valid shared memory region is returned on success.
  void AllocateLockedDiscardableSharedMemoryForClient(
      int client_id,
      size_t size,
      int32_t id,
      base::UnsafeSharedMemoryRegion* shared_memory_region);

  // Call this to notify the manager that client process associated with
  // |client_id| has deleted discardable memory segment with |id|.
  void ClientDeletedDiscardableSharedMemory(int32_t id, int client_id);

  // Call this to notify the manager that client associated with |client_id|
  // has been removed. The manager will use this to release memory segments
  // allocated for client to the OS.
  void ClientRemoved(int client_id);

  // The maximum number of bytes of memory that may be allocated. This will
  // cause memory usage to be reduced if currently above |limit|.
  void SetMemoryLimit(size_t limit);

  // Reduce memory usage if above current memory limit.
  void EnforceMemoryPolicy();

  // Returns bytes of allocated discardable memory.
  size_t GetBytesAllocated() const override;

  void ReleaseFreeMemory() override {
    // Do nothing since we already subscribe to memory pressure notifications.
  }

 private:
  friend TestDiscardableSharedMemoryManager;

  class MemorySegment : public base::RefCountedThreadSafe<MemorySegment> {
   public:
    MemorySegment(std::unique_ptr<base::DiscardableSharedMemory> memory);

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

    base::DiscardableSharedMemory* memory() const { return memory_.get(); }

   private:
    friend class base::RefCountedThreadSafe<MemorySegment>;

    ~MemorySegment();

    std::unique_ptr<base::DiscardableSharedMemory> memory_;
  };

  static bool CompareMemoryUsageTime(const scoped_refptr<MemorySegment>& a,
                                     const scoped_refptr<MemorySegment>& b) {
    // In this system, LRU memory segment is evicted first.
    return a->memory()->last_known_usage() > b->memory()->last_known_usage();
  }

  // base::CurrentThread::DestructionObserver implementation:
  void WillDestroyCurrentMessageLoop() override;

  void AllocateLockedDiscardableSharedMemory(
      int client_id,
      size_t size,
      int32_t id,
      base::UnsafeSharedMemoryRegion* shared_memory_region);
  void DeletedDiscardableSharedMemory(int32_t id, int client_id);
  // Virtual for tests.
  virtual void OnMemoryPressure(
      base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level);
  void ReduceMemoryUsageUntilWithinMemoryLimit()
      EXCLUSIVE_LOCKS_REQUIRED(lock_);
  void ReduceMemoryUsageUntilWithinLimit(size_t limit)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);
  void ReleaseMemory(base::DiscardableSharedMemory* memory)
      EXCLUSIVE_LOCKS_REQUIRED(lock_);
  void BytesAllocatedChanged(size_t new_bytes_allocated) const;

  // Virtual for tests.
  virtual base::Time Now() const;
  virtual void ScheduleEnforceMemoryPolicy() EXCLUSIVE_LOCKS_REQUIRED(lock_);

  // Invalidate weak pointers for the mojo thread.
  void InvalidateMojoThreadWeakPtrs(base::WaitableEvent* event);

  // Create `memory_pressure_listener_` on a worker thread to receive memory
  // pressure notifications there.
  void CreateMemoryPressureListenerOnWorkerThread();

  int32_t next_client_id_;

  mutable base::Lock lock_;
  using MemorySegmentMap =
      std::unordered_map<int32_t, scoped_refptr<MemorySegment>>;
  using ClientMap = std::unordered_map<int, MemorySegmentMap>;
  ClientMap clients_ GUARDED_BY(lock_);
  // Note: The elements in |segments_| are arranged in such a way that they form
  // a heap. The LRU memory segment always first.
  using MemorySegmentVector = std::vector<scoped_refptr<MemorySegment>>;
  MemorySegmentVector segments_ GUARDED_BY(lock_);
  size_t default_memory_limit_ GUARDED_BY(lock_);
  size_t memory_limit_ GUARDED_BY(lock_);
  size_t bytes_allocated_ GUARDED_BY(lock_);
  std::unique_ptr<base::MemoryPressureListener> memory_pressure_listener_
      GUARDED_BY(lock_);
  scoped_refptr<base::SingleThreadTaskRunner> enforce_memory_policy_task_runner_
      GUARDED_BY(lock_);
  base::RepeatingClosure enforce_memory_policy_callback_ GUARDED_BY(lock_);
  bool enforce_memory_policy_pending_ GUARDED_BY(lock_);

  // The message loop for running mojom::DiscardableSharedMemoryManager
  // implementations.
  // TODO(altimin,gab): Allow weak pointers to be deleted on other threads
  // when the thread is gone and remove this.
  // A prerequisite for this is allowing objects to be bound to the lifetime
  // of a sequence directly.
  base::CurrentThread mojo_thread_message_loop_;
  scoped_refptr<base::SingleThreadTaskRunner> mojo_thread_task_runner_;

  // A task runner to create `memory_pressure_listener_` on worker threads so
  // that `OnMemoryPressure` notification happens on the worker thread too.
  scoped_refptr<base::SequencedTaskRunner> memory_pressure_task_runner_;

  base::WeakPtrFactory<DiscardableSharedMemoryManager> weak_ptr_factory_{this};

  // WeakPtrFractory for generating weak pointers used in the mojo thread.
  base::WeakPtrFactory<DiscardableSharedMemoryManager>
      mojo_thread_weak_ptr_factory_{this};
};

}  // namespace discardable_memory

#endif  // COMPONENTS_DISCARDABLE_MEMORY_SERVICE_DISCARDABLE_SHARED_MEMORY_MANAGER_H_