madv_free_discardable_memory_posix.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. // Copyright 2019 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. #ifndef BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_
  5. #define BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_
  6. #include <stddef.h>
  7. #include <stdint.h>
  8. #include <atomic>
  9. #include <vector>
  10. #include "base/base_export.h"
  11. #include "base/callback.h"
  12. #include "base/memory/discardable_memory.h"
  13. #include "base/memory/raw_ptr.h"
  14. #include "base/sequence_checker.h"
  15. #include "base/threading/thread_collision_warner.h"
  16. #include "build/build_config.h"
  17. namespace base {
  18. // Discardable memory backed by the MADV_FREE advice value, available since
  19. // Linux 4.5.
  20. //
  21. // When unlocked, this implementation of discardable memory will
  22. // apply the MADV_FREE advice value to all pages within the allocated range,
  23. // causing pages to be discarded instead of swapped upon memory pressure.
  24. // When pages are discarded, they become zero-fill-on-demand pages.
  25. // Attempting to unlock an already-unlocked instance is undefined behaviour.
  26. //
  27. // When locked, all pages will be checked for eviction. If any page has
  28. // been discarded, the entire allocated range is unmapped and the lock fails.
  29. // After a failed lock, the instance remains unlocked but any further attempts
  30. // to lock will fail. Additionally, the discardable memory instance is
  31. // invalidated and access to memory obtained via data() is undefined behaviour.
  32. // Attempting to lock an already-locked instance is undefined behaviour. If no
  33. // page in the allocated range has been discarded, then lock succeeds and the
  34. // allocated range of memory is available for use without any page fault,
  35. // additional allocations, or memory zeroing.
  36. //
  37. // If DCHECK_IS_ON(), additional checks are added to ensure that the discardable
  38. // memory instance is being used correctly. These checks are not present by
  39. // default, as some incur a significant performance penalty or do not warrant
  40. // crashing the process. These checks are:
  41. // - Do not allow lock while already locked or unlock while already unlocked
  42. // - Do not allow memory access via data() if instance is deallocated after
  43. // Lock() (although invalid memory can still be accessed through existing
  44. // pointers)
  45. // - After Unlock(), disallow read or write of memory pointed to by data()
  46. // with PROT_NONE until next Lock()
  47. //
  48. // Caveats:
  49. // [1]: The smallest allocation unit is the size of a page, so it is
  50. // unsuitable for small allocations.
  51. //
  52. // [2]: The size of a discardable memory instance must be greater than 0 bytes.
  53. //
  54. class BASE_EXPORT MadvFreeDiscardableMemoryPosix : public DiscardableMemory {
  55. public:
  56. MadvFreeDiscardableMemoryPosix(size_t size_in_pages,
  57. std::atomic<size_t>* allocator_byte_count);
  58. MadvFreeDiscardableMemoryPosix(const MadvFreeDiscardableMemoryPosix&) =
  59. delete;
  60. MadvFreeDiscardableMemoryPosix& operator=(
  61. const MadvFreeDiscardableMemoryPosix&) = delete;
  62. ~MadvFreeDiscardableMemoryPosix() override;
  63. bool Lock() override;
  64. void Unlock() override;
  65. void* data() const override;
  66. bool IsLockedForTesting() const;
  67. void DiscardForTesting() override;
  68. trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump(
  69. const char* name,
  70. trace_event::ProcessMemoryDump* pmd) const override;
  71. protected:
  72. size_t GetPageCount() const { return allocated_pages_; }
  73. bool IsValid() const;
  74. void SetKeepMemoryForTesting(bool keep_memory);
  75. // Force page discard by applying MADV_DONTNEED hint on a page.
  76. // Has the same effect as if the page was naturally discarded during
  77. // memory pressure due to MADV_FREE (i.e. zero-fill-on-demand pages for
  78. // anonymous private mappings).
  79. // Note that MADV_DONTNEED takes effect immediately for non-shared mappings.
  80. void DiscardPage(size_t page_index);
  81. private:
  82. bool LockPage(size_t page_index);
  83. void UnlockPage(size_t page_index);
  84. bool Deallocate();
  85. // Gets whether this instance has been discarded (but not yet unmapped).
  86. bool IsDiscarded() const;
  87. // Get whether all pages in this discardable memory instance are resident.
  88. bool IsResident() const;
  89. const size_t size_in_bytes_;
  90. const size_t allocated_pages_;
  91. // Pointer to allocator memory usage metric for updating upon allocation and
  92. // destruction.
  93. raw_ptr<std::atomic<size_t>> allocator_byte_count_;
  94. raw_ptr<void> data_;
  95. bool is_locked_ = true;
  96. // If true, MADV_FREE will not be set on Unlock().
  97. bool keep_memory_for_testing_ = false;
  98. // Stores the first word of a page for use during locking.
  99. std::vector<std::atomic<intptr_t>> page_first_word_;
  100. DFAKE_MUTEX(thread_collision_warner_);
  101. };
  102. enum class MadvFreeSupport { kUnsupported, kSupported };
  103. BASE_EXPORT MadvFreeSupport GetMadvFreeSupport();
  104. } // namespace base
  105. #endif // BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_