• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/allocator/partition_allocator/memory_reclaimer.h"
6 
7 #include "base/allocator/partition_allocator/partition_alloc.h"
8 #include "base/allocator/partition_allocator/partition_alloc_base/no_destructor.h"
9 #include "base/allocator/partition_allocator/partition_alloc_buildflags.h"
10 #include "base/allocator/partition_allocator/partition_alloc_check.h"
11 #include "base/allocator/partition_allocator/partition_alloc_config.h"
12 
13 #if BUILDFLAG(USE_STARSCAN)
14 #include "base/allocator/partition_allocator/starscan/pcscan.h"
15 #endif
16 
17 namespace partition_alloc {
18 
19 // static
Instance()20 MemoryReclaimer* MemoryReclaimer::Instance() {
21   static internal::base::NoDestructor<MemoryReclaimer> instance;
22   return instance.get();
23 }
24 
RegisterPartition(PartitionRoot<> * partition)25 void MemoryReclaimer::RegisterPartition(PartitionRoot<>* partition) {
26   internal::ScopedGuard lock(lock_);
27   PA_DCHECK(partition);
28   auto it_and_whether_inserted = partitions_.insert(partition);
29   PA_DCHECK(it_and_whether_inserted.second);
30 }
31 
UnregisterPartition(PartitionRoot<internal::ThreadSafe> * partition)32 void MemoryReclaimer::UnregisterPartition(
33     PartitionRoot<internal::ThreadSafe>* partition) {
34   internal::ScopedGuard lock(lock_);
35   PA_DCHECK(partition);
36   size_t erased_count = partitions_.erase(partition);
37   PA_DCHECK(erased_count == 1u);
38 }
39 
40 MemoryReclaimer::MemoryReclaimer() = default;
41 MemoryReclaimer::~MemoryReclaimer() = default;
42 
ReclaimAll()43 void MemoryReclaimer::ReclaimAll() {
44   constexpr int kFlags = PurgeFlags::kDecommitEmptySlotSpans |
45                          PurgeFlags::kDiscardUnusedSystemPages |
46                          PurgeFlags::kAggressiveReclaim;
47   Reclaim(kFlags);
48 }
49 
ReclaimNormal()50 void MemoryReclaimer::ReclaimNormal() {
51   constexpr int kFlags = PurgeFlags::kDecommitEmptySlotSpans |
52                          PurgeFlags::kDiscardUnusedSystemPages;
53   Reclaim(kFlags);
54 }
55 
Reclaim(int flags)56 void MemoryReclaimer::Reclaim(int flags) {
57   internal::ScopedGuard lock(
58       lock_);  // Has to protect from concurrent (Un)Register calls.
59 
60   // PCScan quarantines freed slots. Trigger the scan first to let it call
61   // FreeNoHooksImmediate on slots that pass the quarantine.
62   //
63   // In turn, FreeNoHooksImmediate may add slots to thread cache. Purge it next
64   // so that the slots are actually freed. (This is done synchronously only for
65   // the current thread.)
66   //
67   // Lastly decommit empty slot spans and lastly try to discard unused pages at
68   // the end of the remaining active slots.
69 #if PA_CONFIG(STARSCAN_ENABLE_STARSCAN_ON_RECLAIM) && BUILDFLAG(USE_STARSCAN)
70   {
71     using PCScan = internal::PCScan;
72     const auto invocation_mode = flags & PurgeFlags::kAggressiveReclaim
73                                      ? PCScan::InvocationMode::kForcedBlocking
74                                      : PCScan::InvocationMode::kBlocking;
75     PCScan::PerformScanIfNeeded(invocation_mode);
76   }
77 #endif  // PA_CONFIG(STARSCAN_ENABLE_STARSCAN_ON_RECLAIM) &&
78         // BUILDFLAG(USE_STARSCAN)
79 
80 #if PA_CONFIG(THREAD_CACHE_SUPPORTED)
81   // Don't completely empty the thread cache outside of low memory situations,
82   // as there is periodic purge which makes sure that it doesn't take too much
83   // space.
84   if (flags & PurgeFlags::kAggressiveReclaim) {
85     ThreadCacheRegistry::Instance().PurgeAll();
86   }
87 #endif  // PA_CONFIG(THREAD_CACHE_SUPPORTED)
88 
89   for (auto* partition : partitions_) {
90     partition->PurgeMemory(flags);
91   }
92 }
93 
ResetForTesting()94 void MemoryReclaimer::ResetForTesting() {
95   internal::ScopedGuard lock(lock_);
96   partitions_.clear();
97 }
98 
99 }  // namespace partition_alloc
100