// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_ #define BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_ #include #include #include #include #include "base/base_export.h" #include "base/callback.h" #include "base/memory/discardable_memory.h" #include "base/memory/raw_ptr.h" #include "base/sequence_checker.h" #include "base/threading/thread_collision_warner.h" #include "build/build_config.h" namespace base { // Discardable memory backed by the MADV_FREE advice value, available since // Linux 4.5. // // When unlocked, this implementation of discardable memory will // apply the MADV_FREE advice value to all pages within the allocated range, // causing pages to be discarded instead of swapped upon memory pressure. // When pages are discarded, they become zero-fill-on-demand pages. // Attempting to unlock an already-unlocked instance is undefined behaviour. // // When locked, all pages will be checked for eviction. If any page has // been discarded, the entire allocated range is unmapped and the lock fails. // After a failed lock, the instance remains unlocked but any further attempts // to lock will fail. Additionally, the discardable memory instance is // invalidated and access to memory obtained via data() is undefined behaviour. // Attempting to lock an already-locked instance is undefined behaviour. If no // page in the allocated range has been discarded, then lock succeeds and the // allocated range of memory is available for use without any page fault, // additional allocations, or memory zeroing. // // If DCHECK_IS_ON(), additional checks are added to ensure that the discardable // memory instance is being used correctly. These checks are not present by // default, as some incur a significant performance penalty or do not warrant // crashing the process. These checks are: // - Do not allow lock while already locked or unlock while already unlocked // - Do not allow memory access via data() if instance is deallocated after // Lock() (although invalid memory can still be accessed through existing // pointers) // - After Unlock(), disallow read or write of memory pointed to by data() // with PROT_NONE until next Lock() // // Caveats: // [1]: The smallest allocation unit is the size of a page, so it is // unsuitable for small allocations. // // [2]: The size of a discardable memory instance must be greater than 0 bytes. // class BASE_EXPORT MadvFreeDiscardableMemoryPosix : public DiscardableMemory { public: MadvFreeDiscardableMemoryPosix(size_t size_in_pages, std::atomic* allocator_byte_count); MadvFreeDiscardableMemoryPosix(const MadvFreeDiscardableMemoryPosix&) = delete; MadvFreeDiscardableMemoryPosix& operator=( const MadvFreeDiscardableMemoryPosix&) = delete; ~MadvFreeDiscardableMemoryPosix() override; bool Lock() override; void Unlock() override; void* data() const override; bool IsLockedForTesting() const; void DiscardForTesting() override; trace_event::MemoryAllocatorDump* CreateMemoryAllocatorDump( const char* name, trace_event::ProcessMemoryDump* pmd) const override; protected: size_t GetPageCount() const { return allocated_pages_; } bool IsValid() const; void SetKeepMemoryForTesting(bool keep_memory); // Force page discard by applying MADV_DONTNEED hint on a page. // Has the same effect as if the page was naturally discarded during // memory pressure due to MADV_FREE (i.e. zero-fill-on-demand pages for // anonymous private mappings). // Note that MADV_DONTNEED takes effect immediately for non-shared mappings. void DiscardPage(size_t page_index); private: bool LockPage(size_t page_index); void UnlockPage(size_t page_index); bool Deallocate(); // Gets whether this instance has been discarded (but not yet unmapped). bool IsDiscarded() const; // Get whether all pages in this discardable memory instance are resident. bool IsResident() const; const size_t size_in_bytes_; const size_t allocated_pages_; // Pointer to allocator memory usage metric for updating upon allocation and // destruction. raw_ptr> allocator_byte_count_; raw_ptr data_; bool is_locked_ = true; // If true, MADV_FREE will not be set on Unlock(). bool keep_memory_for_testing_ = false; // Stores the first word of a page for use during locking. std::vector> page_first_word_; DFAKE_MUTEX(thread_collision_warner_); }; enum class MadvFreeSupport { kUnsupported, kSupported }; BASE_EXPORT MadvFreeSupport GetMadvFreeSupport(); } // namespace base #endif // BASE_MEMORY_MADV_FREE_DISCARDABLE_MEMORY_POSIX_H_