#ifndef V8_HEAP_CPPGC_PAGE_MEMORY_H_
#define V8_HEAP_CPPGC_PAGE_MEMORY_H_
#include <map>
#include <memory>
#include <unordered_map>
#include <vector>
#include "include/cppgc/platform.h"
#include "src/base/macros.h"
#include "src/base/platform/mutex.h"
#include "src/heap/cppgc/globals.h"
#include "src/heap/cppgc/heap-config.h"
namespace cppgc {
namespace internal {
class V8_EXPORT_PRIVATE MemoryRegion final {
public:
MemoryRegion() = default;
MemoryRegion(Address base, size_t size) : base_(base), size_(size) {
DCHECK(base);
DCHECK_LT(0u, size);
}
Address base() const { return base_; }
size_t size() const { return size_; }
Address end() const { return base_ + size_; }
bool Contains(ConstAddress addr) const {
return (reinterpret_cast<uintptr_t>(addr) -
reinterpret_cast<uintptr_t>(base_)) < size_;
}
bool Contains(const MemoryRegion& other) const {
return base_ <= other.base() && other.end() <= end();
}
private:
Address base_ = nullptr;
size_t size_ = 0;
};
class V8_EXPORT_PRIVATE PageMemoryRegion final {
public:
PageMemoryRegion(PageAllocator&, MemoryRegion);
~PageMemoryRegion();
const MemoryRegion region() const { return reserved_region_; }
inline Address Lookup(ConstAddress address) const {
const MemoryRegion memory_region = region();
return memory_region.Contains(address) ? memory_region.base() : nullptr;
}
PageAllocator& allocator() const { return allocator_; }
PageMemoryRegion(const PageMemoryRegion&) = delete;
PageMemoryRegion& operator=(const PageMemoryRegion&) = delete;
void UnprotectForTesting();
private:
PageAllocator& allocator_;
const MemoryRegion reserved_region_;
};
class V8_EXPORT_PRIVATE PageMemoryRegionTree final {
public:
PageMemoryRegionTree();
~PageMemoryRegionTree();
void Add(PageMemoryRegion*);
void Remove(PageMemoryRegion*);
inline PageMemoryRegion* Lookup(ConstAddress) const;
private:
std::map<ConstAddress, PageMemoryRegion*> set_;
};
class V8_EXPORT_PRIVATE NormalPageMemoryPool final {
public:
void Add(PageMemoryRegion*);
PageMemoryRegion* Take();
size_t pooled() const { return pool_.size(); }
size_t PooledMemory() const;
void ReleasePooledPages(PageAllocator& allocator);
auto& get_raw_pool_for_testing() { return pool_; }
void SetDecommitPooledPages(bool value) { decommit_pooled_pages_ = value; }
static constexpr bool kDefaultDecommitPooledPage = false;
private:
struct PooledPageMemoryRegion {
explicit PooledPageMemoryRegion(PageMemoryRegion* region)
: region(region) {}
PageMemoryRegion* region;
bool is_decommitted = false;
bool is_discarded = false;
};
std::vector<PooledPageMemoryRegion> pool_;
bool decommit_pooled_pages_ = kDefaultDecommitPooledPage;
};
class V8_EXPORT_PRIVATE PageBackend final {
public:
PageBackend(PageAllocator& normal_page_allocator,
PageAllocator& large_page_allocator);
~PageBackend();
Address TryAllocateNormalPageMemory();
void FreeNormalPageMemory(Address writeable_base);
Address TryAllocateLargePageMemory(size_t size);
void FreeLargePageMemory(Address writeable_base);
inline Address Lookup(ConstAddress) const;
PageBackend(const PageBackend&) = delete;
PageBackend& operator=(const PageBackend&) = delete;
void ReleasePooledPages();
PageMemoryRegionTree& get_page_memory_region_tree_for_testing() {
return page_memory_region_tree_;
}
NormalPageMemoryPool& page_pool() { return page_pool_; }
private:
mutable v8::base::Mutex mutex_;
PageAllocator& normal_page_allocator_;
PageAllocator& large_page_allocator_;
NormalPageMemoryPool page_pool_;
PageMemoryRegionTree page_memory_region_tree_;
std::unordered_map<PageMemoryRegion*, std::unique_ptr<PageMemoryRegion>>
normal_page_memory_regions_;
std::unordered_map<PageMemoryRegion*, std::unique_ptr<PageMemoryRegion>>
large_page_memory_regions_;
};
PageMemoryRegion* PageMemoryRegionTree::Lookup(ConstAddress address) const {
auto it = set_.upper_bound(address);
if (it == set_.begin()) {
return nullptr;
}
auto* result = std::next(it, -1)->second;
if (address < result->region().end()) {
return result;
}
return nullptr;
}
Address PageBackend::Lookup(ConstAddress address) const {
v8::base::MutexGuard guard(&mutex_);
PageMemoryRegion* pmr = page_memory_region_tree_.Lookup(address);
return pmr ? pmr->Lookup(address) : nullptr;
}
}
}
#endif