client_discardable_shared_memory_manager.cc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "base/atomic_sequence_num.h"
  8. #include "base/bind.h"
  9. #include "base/format_macros.h"
  10. #include "base/memory/discardable_memory.h"
  11. #include "base/memory/discardable_shared_memory.h"
  12. #include "base/memory/page_size.h"
  13. #include "base/metrics/histogram_functions.h"
  14. #include "base/metrics/histogram_macros.h"
  15. #include "base/process/memory.h"
  16. #include "base/strings/string_number_conversions.h"
  17. #include "base/synchronization/waitable_event.h"
  18. #include "base/system/sys_info.h"
  19. #include "base/threading/thread_task_runner_handle.h"
  20. #include "base/time/time.h"
  21. #include "base/trace_event/memory_dump_manager.h"
  22. #include "base/trace_event/trace_event.h"
  23. #include "build/build_config.h"
  24. #include "components/crash/core/common/crash_key.h"
  25. namespace discardable_memory {
  26. namespace {
  27. const base::Feature kShorterPeriodicPurge{"ShorterPeriodicPurge",
  28. base::FEATURE_DISABLED_BY_DEFAULT};
  29. // Global atomic to generate unique discardable shared memory IDs.
  30. base::AtomicSequenceNumber g_next_discardable_shared_memory_id;
  31. size_t GetDefaultAllocationSize() {
  32. const size_t kOneMegabyteInBytes = 1024 * 1024;
  33. // There is a trade-off between round-trip cost to the browser process and
  34. // memory usage overhead. 4MB is measured as the ideal size according to the
  35. // usage statistics. For low-end devices, we care about lowering the memory
  36. // usage and 1MB is good for the most basic cases.
  37. [[maybe_unused]] const size_t kDefaultAllocationSize =
  38. 4 * kOneMegabyteInBytes;
  39. [[maybe_unused]] const size_t kDefaultLowEndDeviceAllocationSize =
  40. kOneMegabyteInBytes;
  41. #if defined(ARCH_CPU_32_BITS) && !BUILDFLAG(IS_ANDROID)
  42. // On 32 bit architectures, use a smaller chunk, as address space
  43. // fragmentation may make a 4MiB allocation impossible to fulfill in the
  44. // browser process. See crbug.com/983348 for details.
  45. //
  46. // Not on Android, since on this platform total number of file descriptors is
  47. // also a concern.
  48. return kDefaultLowEndDeviceAllocationSize;
  49. #elif BUILDFLAG(IS_FUCHSIA)
  50. // Low end Fuchsia devices may be very constrained, so use smaller allocations
  51. // to save memory. See https://fxbug.dev/55760.
  52. return base::SysInfo::IsLowEndDevice() ? kDefaultLowEndDeviceAllocationSize
  53. : kDefaultAllocationSize;
  54. #else
  55. return kDefaultAllocationSize;
  56. #endif
  57. }
  58. void InitManagerMojoOnIO(
  59. mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
  60. mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> remote) {
  61. manager_mojo->Bind(std::move(remote));
  62. }
  63. void DeletedDiscardableSharedMemoryOnIO(
  64. mojo::Remote<mojom::DiscardableSharedMemoryManager>* manager_mojo,
  65. int32_t id) {
  66. (*manager_mojo)->DeletedDiscardableSharedMemory(id);
  67. }
  68. } // namespace
  69. constexpr base::TimeDelta
  70. ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge;
  71. constexpr base::TimeDelta
  72. ClientDiscardableSharedMemoryManager::kScheduledPurgeInterval;
  73. ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
  74. DiscardableMemoryImpl(
  75. ClientDiscardableSharedMemoryManager* manager,
  76. std::unique_ptr<DiscardableSharedMemoryHeap::Span> span)
  77. : manager_(manager), span_(std::move(span)) {
  78. DCHECK_NE(manager, nullptr);
  79. }
  80. ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
  81. ~DiscardableMemoryImpl() {
  82. base::AutoLock lock(manager_->lock_);
  83. if (!span_) {
  84. DCHECK(!is_locked());
  85. return;
  86. }
  87. manager_->UnlockAndReleaseMemory(this, std::move(span_));
  88. }
  89. bool ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Lock() {
  90. base::AutoLock lock(manager_->lock_);
  91. DCHECK(!is_locked());
  92. if (span_ && manager_->LockSpan(span_.get()))
  93. last_locked_ = base::TimeTicks();
  94. bool locked = is_locked();
  95. UMA_HISTOGRAM_BOOLEAN("Memory.Discardable.LockingSuccess", locked);
  96. return locked;
  97. }
  98. void ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Unlock() {
  99. base::AutoLock lock(manager_->lock_);
  100. DCHECK(is_locked());
  101. DCHECK(span_);
  102. manager_->UnlockSpan(span_.get());
  103. last_locked_ = base::TimeTicks::Now();
  104. }
  105. std::unique_ptr<DiscardableSharedMemoryHeap::Span>
  106. ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::Purge(
  107. base::TimeTicks min_ticks) {
  108. DCHECK(span_);
  109. if (is_locked())
  110. return nullptr;
  111. if (last_locked_ > min_ticks)
  112. return nullptr;
  113. return std::move(span_);
  114. }
  115. void* ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::data()
  116. const {
  117. #if DCHECK_IS_ON()
  118. {
  119. base::AutoLock lock(manager_->lock_);
  120. DCHECK(is_locked());
  121. }
  122. #endif
  123. return reinterpret_cast<void*>(span_->start() * base::GetPageSize());
  124. }
  125. bool ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::is_locked()
  126. const {
  127. return last_locked_.is_null();
  128. }
  129. void ClientDiscardableSharedMemoryManager::DiscardableMemoryImpl::
  130. DiscardForTesting() {
  131. #if DCHECK_IS_ON()
  132. {
  133. base::AutoLock lock(manager_->lock_);
  134. DCHECK(!is_locked());
  135. }
  136. #endif
  137. span_->shared_memory()->Purge(base::Time::Now());
  138. }
  139. base::trace_event::MemoryAllocatorDump* ClientDiscardableSharedMemoryManager::
  140. DiscardableMemoryImpl::CreateMemoryAllocatorDump(
  141. const char* name,
  142. base::trace_event::ProcessMemoryDump* pmd) const {
  143. base::AutoLock lock(manager_->lock_);
  144. return manager_->CreateMemoryAllocatorDump(span_.get(), name, pmd);
  145. }
  146. ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
  147. mojo::PendingRemote<mojom::DiscardableSharedMemoryManager> manager,
  148. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
  149. : ClientDiscardableSharedMemoryManager(io_task_runner) {
  150. manager_mojo_ =
  151. std::make_unique<mojo::Remote<mojom::DiscardableSharedMemoryManager>>();
  152. io_task_runner_->PostTask(
  153. FROM_HERE, base::BindOnce(&InitManagerMojoOnIO, manager_mojo_.get(),
  154. std::move(manager)));
  155. }
  156. ClientDiscardableSharedMemoryManager::ClientDiscardableSharedMemoryManager(
  157. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
  158. : RefCountedDeleteOnSequence<ClientDiscardableSharedMemoryManager>(
  159. base::ThreadTaskRunnerHandle::Get()),
  160. task_runner_(base::ThreadTaskRunnerHandle::Get()),
  161. heap_(std::make_unique<DiscardableSharedMemoryHeap>()),
  162. io_task_runner_(std::move(io_task_runner)),
  163. manager_mojo_(nullptr) {
  164. base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
  165. this, "ClientDiscardableSharedMemoryManager",
  166. base::ThreadTaskRunnerHandle::Get());
  167. }
  168. ClientDiscardableSharedMemoryManager::~ClientDiscardableSharedMemoryManager() {
  169. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  170. base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
  171. this);
  172. // Any memory allocated by a ClientDiscardableSharedMemoryManager must not be
  173. // touched after it is destroyed, or it will cause a use-after-free. This
  174. // check ensures that we stop before that can happen, instead of continuing
  175. // with dangling pointers.
  176. CHECK_EQ(heap_->GetSize(), heap_->GetFreelistSize());
  177. if (heap_->GetSize())
  178. MemoryUsageChanged(0, 0);
  179. // Releasing the |heap_| before posting a task for deleting |manager_mojo_|.
  180. // It is because releasing |heap_| will invoke DeletedDiscardableSharedMemory
  181. // which needs |manager_mojo_|.
  182. heap_.reset();
  183. // Delete the |manager_mojo_| on IO thread, so any pending tasks on IO thread
  184. // will be executed before the |manager_mojo_| is deleted.
  185. bool posted = io_task_runner_->DeleteSoon(FROM_HERE, manager_mojo_.release());
  186. if (!posted)
  187. manager_mojo_.reset();
  188. }
  189. void ClientDiscardableSharedMemoryManager::OnForegrounded() {
  190. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  191. foregrounded_ = true;
  192. }
  193. void ClientDiscardableSharedMemoryManager::OnBackgrounded() {
  194. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  195. foregrounded_ = false;
  196. }
  197. std::unique_ptr<base::DiscardableMemory>
  198. ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableMemory(
  199. size_t size) {
  200. base::AutoLock lock(lock_);
  201. if (!is_purge_scheduled_) {
  202. task_runner_->PostDelayedTask(
  203. FROM_HERE,
  204. base::BindOnce(&ClientDiscardableSharedMemoryManager::ScheduledPurge,
  205. this),
  206. kScheduledPurgeInterval);
  207. is_purge_scheduled_ = true;
  208. }
  209. DCHECK_NE(size, 0u);
  210. auto size_in_kb = static_cast<base::HistogramBase::Sample>(size / 1024);
  211. UMA_HISTOGRAM_CUSTOM_COUNTS("Memory.DiscardableAllocationSize",
  212. size_in_kb, // In KiB
  213. 1,
  214. 4 * 1024 * 1024, // 4 GiB
  215. 50);
  216. // Round up to multiple of page size.
  217. size_t pages =
  218. std::max((size + base::GetPageSize() - 1) / base::GetPageSize(),
  219. static_cast<size_t>(1));
  220. static const size_t allocation_size = GetDefaultAllocationSize();
  221. DCHECK_EQ(allocation_size % base::GetPageSize(), 0u);
  222. // Default allocation size in pages.
  223. size_t allocation_pages = allocation_size / base::GetPageSize();
  224. size_t slack = 0;
  225. // When searching the free lists, allow a slack between required size and
  226. // free span size that is less or equal to |allocation_size|. This is to
  227. // avoid segments larger then |allocation_size| unless they are a perfect
  228. // fit. The result is that large allocations can be reused without reducing
  229. // the ability to discard memory.
  230. if (pages < allocation_pages)
  231. slack = allocation_pages - pages;
  232. size_t heap_size_prior_to_releasing_purged_memory = heap_->GetSize();
  233. for (;;) {
  234. // Search free lists for suitable span.
  235. std::unique_ptr<DiscardableSharedMemoryHeap::Span> free_span =
  236. heap_->SearchFreeLists(pages, slack);
  237. if (!free_span)
  238. break;
  239. // Attempt to lock |free_span|. Delete span and search free lists again
  240. // if locking failed.
  241. if (free_span->shared_memory()->Lock(
  242. free_span->start() * base::GetPageSize() -
  243. reinterpret_cast<size_t>(free_span->shared_memory()->memory()),
  244. free_span->length() * base::GetPageSize()) ==
  245. base::DiscardableSharedMemory::FAILED) {
  246. DCHECK(!free_span->shared_memory()->IsMemoryResident());
  247. // We have to release purged memory before |free_span| can be destroyed.
  248. heap_->ReleasePurgedMemory();
  249. DCHECK(!free_span->shared_memory());
  250. continue;
  251. }
  252. free_span->set_is_locked(true);
  253. if (pages >= allocation_pages) {
  254. UMA_HISTOGRAM_BOOLEAN("Memory.Discardable.LargeAllocationFromFreelist",
  255. true);
  256. }
  257. // Memory usage is guaranteed to have changed after having removed
  258. // at least one span from the free lists.
  259. MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
  260. // Memory in this span is no longer held in the freelist, so we don't want
  261. // to count it towards the total of dirty freelist memory.
  262. heap_->dirty_freed_memory_page_count_ -= free_span->MarkAsClean();
  263. auto discardable_memory =
  264. std::make_unique<DiscardableMemoryImpl>(this, std::move(free_span));
  265. allocated_memory_.insert(discardable_memory.get());
  266. return std::move(discardable_memory);
  267. }
  268. // Release purged memory to free up the address space before we attempt to
  269. // allocate more memory.
  270. heap_->ReleasePurgedMemory();
  271. // Make sure crash keys are up to date in case allocation fails.
  272. if (heap_->GetSize() != heap_size_prior_to_releasing_purged_memory)
  273. MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
  274. size_t pages_to_allocate =
  275. std::max(allocation_size / base::GetPageSize(), pages);
  276. size_t allocation_size_in_bytes = pages_to_allocate * base::GetPageSize();
  277. int32_t new_id = g_next_discardable_shared_memory_id.GetNext();
  278. if (bytes_allocated_limit_for_testing_ &&
  279. heap_->GetSize() >= bytes_allocated_limit_for_testing_) {
  280. return nullptr;
  281. }
  282. // Ask parent process to allocate a new discardable shared memory segment.
  283. std::unique_ptr<base::DiscardableSharedMemory> shared_memory =
  284. AllocateLockedDiscardableSharedMemory(allocation_size_in_bytes, new_id);
  285. if (!shared_memory)
  286. return nullptr;
  287. // Create span for allocated memory.
  288. // Spans are managed by |heap_| (the member of
  289. // the ClientDiscardableSharedMemoryManager), so it is safe to use
  290. // base::Unretained(this) here.
  291. std::unique_ptr<DiscardableSharedMemoryHeap::Span> new_span(heap_->Grow(
  292. std::move(shared_memory), allocation_size_in_bytes, new_id,
  293. base::BindOnce(
  294. &ClientDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory,
  295. base::Unretained(this), new_id)));
  296. new_span->set_is_locked(true);
  297. // Unlock and insert any left over memory into free lists.
  298. if (pages < pages_to_allocate) {
  299. std::unique_ptr<DiscardableSharedMemoryHeap::Span> leftover =
  300. heap_->Split(new_span.get(), pages);
  301. leftover->shared_memory()->Unlock(
  302. leftover->start() * base::GetPageSize() -
  303. reinterpret_cast<size_t>(leftover->shared_memory()->memory()),
  304. leftover->length() * base::GetPageSize());
  305. leftover->set_is_locked(false);
  306. heap_->MergeIntoFreeListsClean(std::move(leftover));
  307. }
  308. if (pages >= allocation_pages) {
  309. UMA_HISTOGRAM_BOOLEAN("Memory.Discardable.LargeAllocationFromFreelist",
  310. false);
  311. }
  312. MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
  313. auto discardable_memory =
  314. std::make_unique<DiscardableMemoryImpl>(this, std::move(new_span));
  315. allocated_memory_.insert(discardable_memory.get());
  316. return std::move(discardable_memory);
  317. }
  318. bool ClientDiscardableSharedMemoryManager::OnMemoryDump(
  319. const base::trace_event::MemoryDumpArgs& args,
  320. base::trace_event::ProcessMemoryDump* pmd) {
  321. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  322. base::AutoLock lock(lock_);
  323. if (foregrounded_) {
  324. const size_t total_size = heap_->GetSize() / 1024; // in KiB
  325. const size_t freelist_size = heap_->GetFreelistSize() / 1024; // in KiB
  326. base::UmaHistogramCounts1M("Memory.Discardable.FreelistSize.Foreground",
  327. freelist_size);
  328. base::UmaHistogramCounts1M("Memory.Discardable.VirtualSize.Foreground",
  329. total_size);
  330. base::UmaHistogramCounts1M("Memory.Discardable.Size.Foreground",
  331. total_size - freelist_size);
  332. }
  333. base::UmaHistogramCounts1M(
  334. "Memory.Discardable.FreelistSize.Dirty",
  335. heap_->dirty_freed_memory_page_count_ * base::GetPageSize() / 1024);
  336. return heap_->OnMemoryDump(args, pmd);
  337. }
  338. size_t ClientDiscardableSharedMemoryManager::GetBytesAllocated() const {
  339. base::AutoLock lock(lock_);
  340. return GetBytesAllocatedLocked();
  341. }
  342. size_t ClientDiscardableSharedMemoryManager::GetBytesAllocatedLocked() const {
  343. return heap_->GetSize() - heap_->GetFreelistSize();
  344. }
  345. void ClientDiscardableSharedMemoryManager::BackgroundPurge() {
  346. PurgeUnlockedMemory(base::TimeDelta());
  347. }
  348. void ClientDiscardableSharedMemoryManager::ScheduledPurge() {
  349. DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  350. // From local testing and UMA, memory usually accumulates slowly in renderers,
  351. // and can sit idle for hours. We purge only the old memory, as this should
  352. // recover the memory without adverse latency effects.
  353. // TODO(crbug.com/1123679): Determine if |kMinAgeForScheduledPurge| and the
  354. // constant from |ScheduledPurge| need to be tuned.
  355. if (base::FeatureList::IsEnabled(kShorterPeriodicPurge)) {
  356. PurgeUnlockedMemory(
  357. ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge / 2);
  358. } else {
  359. PurgeUnlockedMemory(
  360. ClientDiscardableSharedMemoryManager::kMinAgeForScheduledPurge);
  361. }
  362. bool should_schedule = false;
  363. {
  364. base::AutoLock lock(lock_);
  365. should_schedule = GetBytesAllocatedLocked() != 0;
  366. is_purge_scheduled_ = should_schedule;
  367. }
  368. if (should_schedule) {
  369. task_runner_->PostDelayedTask(
  370. FROM_HERE,
  371. base::BindOnce(&ClientDiscardableSharedMemoryManager::ScheduledPurge,
  372. this),
  373. kScheduledPurgeInterval);
  374. }
  375. }
  376. void ClientDiscardableSharedMemoryManager::PurgeUnlockedMemory(
  377. base::TimeDelta min_age) {
  378. {
  379. base::AutoLock lock(lock_);
  380. auto now = base::TimeTicks::Now();
  381. // Iterate this way in order to avoid invalidating the iterator while
  382. // removing elements from |allocated_memory_| as we iterate over it.
  383. for (auto it = allocated_memory_.begin(); it != allocated_memory_.end();
  384. /* nop */) {
  385. auto prev = it++;
  386. DiscardableMemoryImpl* mem = *prev;
  387. // This assert is only required because the static checker can't figure
  388. // out that |mem->manager_->lock_| is the same as |this->lock_|, as
  389. // verified by the DCHECK.
  390. DCHECK_EQ(&lock_, &mem->manager_->lock_);
  391. mem->manager_->lock_.AssertAcquired();
  392. auto span = mem->Purge(now - min_age);
  393. if (span) {
  394. allocated_memory_.erase(prev);
  395. ReleaseSpan(std::move(span));
  396. }
  397. }
  398. }
  399. ReleaseFreeMemory();
  400. }
  401. void ClientDiscardableSharedMemoryManager::ReleaseFreeMemory() {
  402. TRACE_EVENT0("blink",
  403. "ClientDiscardableSharedMemoryManager::ReleaseFreeMemory()");
  404. base::AutoLock lock(lock_);
  405. size_t heap_size_prior_to_releasing_memory = heap_->GetSize();
  406. // Release both purged and free memory.
  407. heap_->ReleasePurgedMemory();
  408. heap_->ReleaseFreeMemory();
  409. if (heap_->GetSize() != heap_size_prior_to_releasing_memory)
  410. MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
  411. }
  412. bool ClientDiscardableSharedMemoryManager::LockSpan(
  413. DiscardableSharedMemoryHeap::Span* span) {
  414. if (!span->shared_memory())
  415. return false;
  416. size_t offset = span->start() * base::GetPageSize() -
  417. reinterpret_cast<size_t>(span->shared_memory()->memory());
  418. size_t length = span->length() * base::GetPageSize();
  419. switch (span->shared_memory()->Lock(offset, length)) {
  420. case base::DiscardableSharedMemory::SUCCESS:
  421. span->set_is_locked(true);
  422. return true;
  423. case base::DiscardableSharedMemory::PURGED:
  424. span->shared_memory()->Unlock(offset, length);
  425. span->set_is_locked(false);
  426. return false;
  427. case base::DiscardableSharedMemory::FAILED:
  428. return false;
  429. }
  430. NOTREACHED();
  431. return false;
  432. }
  433. void ClientDiscardableSharedMemoryManager::UnlockSpan(
  434. DiscardableSharedMemoryHeap::Span* span) {
  435. DCHECK(span->shared_memory());
  436. size_t offset = span->start() * base::GetPageSize() -
  437. reinterpret_cast<size_t>(span->shared_memory()->memory());
  438. size_t length = span->length() * base::GetPageSize();
  439. span->set_is_locked(false);
  440. return span->shared_memory()->Unlock(offset, length);
  441. }
  442. void ClientDiscardableSharedMemoryManager::UnlockAndReleaseMemory(
  443. DiscardableMemoryImpl* memory,
  444. std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
  445. memory->manager_->lock_.AssertAcquired();
  446. // lock_.AssertAcquired();
  447. if (memory->is_locked()) {
  448. UnlockSpan(span.get());
  449. }
  450. DCHECK(span);
  451. auto removed = allocated_memory_.erase(memory);
  452. DCHECK_EQ(removed, 1u);
  453. ReleaseSpan(std::move(span));
  454. }
  455. void ClientDiscardableSharedMemoryManager::ReleaseSpan(
  456. std::unique_ptr<DiscardableSharedMemoryHeap::Span> span) {
  457. DCHECK(span);
  458. // Delete span instead of merging it into free lists if memory is gone.
  459. if (!span->shared_memory())
  460. return;
  461. heap_->MergeIntoFreeLists(std::move(span));
  462. // Bytes of free memory changed.
  463. MemoryUsageChanged(heap_->GetSize(), heap_->GetFreelistSize());
  464. }
  465. base::trace_event::MemoryAllocatorDump*
  466. ClientDiscardableSharedMemoryManager::CreateMemoryAllocatorDump(
  467. DiscardableSharedMemoryHeap::Span* span,
  468. const char* name,
  469. base::trace_event::ProcessMemoryDump* pmd) const {
  470. return heap_->CreateMemoryAllocatorDump(span, name, pmd);
  471. }
  472. std::unique_ptr<base::DiscardableSharedMemory>
  473. ClientDiscardableSharedMemoryManager::AllocateLockedDiscardableSharedMemory(
  474. size_t size,
  475. int32_t id) {
  476. TRACE_EVENT2("renderer",
  477. "ClientDiscardableSharedMemoryManager::"
  478. "AllocateLockedDiscardableSharedMemory",
  479. "size", size, "id", id);
  480. static crash_reporter::CrashKeyString<24>
  481. discardable_memory_ipc_requested_size(
  482. "discardable-memory-ipc-requested-size");
  483. static crash_reporter::CrashKeyString<24> discardable_memory_ipc_error_cause(
  484. "discardable-memory-ipc-error-cause");
  485. base::UnsafeSharedMemoryRegion region;
  486. base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL,
  487. base::WaitableEvent::InitialState::NOT_SIGNALED);
  488. base::ScopedClosureRunner event_signal_runner(
  489. base::BindOnce(&base::WaitableEvent::Signal, base::Unretained(&event)));
  490. io_task_runner_->PostTask(
  491. FROM_HERE,
  492. base::BindOnce(&ClientDiscardableSharedMemoryManager::AllocateOnIO,
  493. base::Unretained(this), size, id, &region,
  494. std::move(event_signal_runner)));
  495. // Waiting until IPC has finished on the IO thread.
  496. event.Wait();
  497. // This is likely address space exhaustion in the the browser process. We
  498. // don't want to crash the browser process for that, which is why the check
  499. // is here, and not there.
  500. if (!region.IsValid()) {
  501. discardable_memory_ipc_error_cause.Set("browser side");
  502. discardable_memory_ipc_requested_size.Set(base::NumberToString(size));
  503. return nullptr;
  504. }
  505. auto memory =
  506. std::make_unique<base::DiscardableSharedMemory>(std::move(region));
  507. if (!memory->Map(size)) {
  508. discardable_memory_ipc_error_cause.Set("client side");
  509. discardable_memory_ipc_requested_size.Set(base::NumberToString(size));
  510. return nullptr;
  511. }
  512. discardable_memory_ipc_error_cause.Clear();
  513. discardable_memory_ipc_requested_size.Clear();
  514. return memory;
  515. }
  516. void ClientDiscardableSharedMemoryManager::AllocateOnIO(
  517. size_t size,
  518. int32_t id,
  519. base::UnsafeSharedMemoryRegion* region,
  520. base::ScopedClosureRunner closure_runner) {
  521. (*manager_mojo_)
  522. ->AllocateLockedDiscardableSharedMemory(
  523. static_cast<uint32_t>(size), id,
  524. base::BindOnce(
  525. &ClientDiscardableSharedMemoryManager::AllocateCompletedOnIO,
  526. base::Unretained(this), region, std::move(closure_runner)));
  527. }
  528. void ClientDiscardableSharedMemoryManager::AllocateCompletedOnIO(
  529. base::UnsafeSharedMemoryRegion* region,
  530. base::ScopedClosureRunner closure_runner,
  531. base::UnsafeSharedMemoryRegion ret_region) {
  532. *region = std::move(ret_region);
  533. }
  534. void ClientDiscardableSharedMemoryManager::DeletedDiscardableSharedMemory(
  535. int32_t id) {
  536. io_task_runner_->PostTask(FROM_HERE,
  537. base::BindOnce(&DeletedDiscardableSharedMemoryOnIO,
  538. manager_mojo_.get(), id));
  539. }
  540. void ClientDiscardableSharedMemoryManager::MemoryUsageChanged(
  541. size_t new_bytes_total,
  542. size_t new_bytes_free) const {
  543. static crash_reporter::CrashKeyString<24> discardable_memory_allocated(
  544. "discardable-memory-allocated");
  545. discardable_memory_allocated.Set(base::NumberToString(new_bytes_total));
  546. static crash_reporter::CrashKeyString<24> discardable_memory_free(
  547. "discardable-memory-free");
  548. discardable_memory_free.Set(base::NumberToString(new_bytes_free));
  549. }
  550. } // namespace discardable_memory