blob: d010af0abfaefe84512d17969ded7b35a6e3fd28 [file] [log] [blame]
// Copyright 2015 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 PageMemory_h
#define PageMemory_h
#include "platform/heap/Heap.h"
#include "platform/heap/HeapPage.h"
#include "platform/wtf/Allocator.h"
#include "platform/wtf/Assertions.h"
#include "platform/wtf/Compiler.h"
#include "platform/wtf/allocator/Partitions.h"
namespace blink {
class RegionTree;
class RegionTreeNode;
class MemoryRegion {
USING_FAST_MALLOC(MemoryRegion);
public:
MemoryRegion(Address base, size_t size) : base_(base), size_(size) {
DCHECK_GT(size, 0u);
}
bool Contains(Address addr) const {
return base_ <= addr && addr < (base_ + size_);
}
bool Contains(const MemoryRegion& other) const {
return Contains(other.base_) && Contains(other.base_ + other.size_ - 1);
}
void Release();
WARN_UNUSED_RESULT bool Commit();
void Decommit();
Address Base() const { return base_; }
size_t size() const { return size_; }
private:
Address base_;
size_t size_;
};
// A PageMemoryRegion represents a chunk of reserved virtual address
// space containing a number of blink heap pages. On Windows, reserved
// virtual address space can only be given back to the system as a
// whole. The PageMemoryRegion allows us to do that by keeping track
// of the number of pages using it in order to be able to release all
// of the virtual address space when there are no more pages using it.
class PageMemoryRegion : public MemoryRegion {
public:
~PageMemoryRegion();
void PageDeleted(Address);
void MarkPageUsed(Address page) {
DCHECK(!in_use_[Index(page)]);
in_use_[Index(page)] = true;
}
void MarkPageUnused(Address page) { in_use_[Index(page)] = false; }
static PageMemoryRegion* AllocateLargePage(size_t size,
RegionTree* region_tree) {
return Allocate(size, 1, region_tree);
}
static PageMemoryRegion* AllocateNormalPages(RegionTree* region_tree) {
return Allocate(kBlinkPageSize * kBlinkPagesPerRegion, kBlinkPagesPerRegion,
region_tree);
}
BasePage* PageFromAddress(Address address) {
DCHECK(Contains(address));
if (!in_use_[Index(address)])
return nullptr;
if (is_large_page_)
return PageFromObject(Base());
return PageFromObject(address);
}
private:
PageMemoryRegion(Address base, size_t, unsigned num_pages, RegionTree*);
unsigned Index(Address address) const {
DCHECK(Contains(address));
if (is_large_page_)
return 0;
size_t offset = BlinkPageAddress(address) - Base();
DCHECK_EQ(offset % kBlinkPageSize, 0u);
return offset / kBlinkPageSize;
}
static PageMemoryRegion* Allocate(size_t, unsigned num_pages, RegionTree*);
const bool is_large_page_;
// A thread owns a page, but not a region. Represent the in-use
// bitmap such that thread non-interference comes for free.
bool in_use_[kBlinkPagesPerRegion];
int num_pages_;
RegionTree* region_tree_;
};
// A RegionTree is a simple binary search tree of PageMemoryRegions sorted
// by base addresses.
class RegionTree {
USING_FAST_MALLOC(RegionTree);
public:
RegionTree() : root_(nullptr) {}
void Add(PageMemoryRegion*);
void Remove(PageMemoryRegion*);
PageMemoryRegion* Lookup(Address);
private:
RegionTreeNode* root_;
};
class RegionTreeNode {
USING_FAST_MALLOC(RegionTreeNode);
public:
explicit RegionTreeNode(PageMemoryRegion* region)
: region_(region), left_(nullptr), right_(nullptr) {}
~RegionTreeNode() {
delete left_;
delete right_;
}
void AddTo(RegionTreeNode** context);
private:
PageMemoryRegion* region_;
RegionTreeNode* left_;
RegionTreeNode* right_;
friend RegionTree;
};
// Representation of the memory used for a Blink heap page.
//
// The representation keeps track of two memory regions:
//
// 1. The virtual memory reserved from the system in order to be able
// to free all the virtual memory reserved. Multiple PageMemory
// instances can share the same reserved memory region and
// therefore notify the reserved memory region on destruction so
// that the system memory can be given back when all PageMemory
// instances for that memory are gone.
//
// 2. The writable memory (a sub-region of the reserved virtual
// memory region) that is used for the actual heap page payload.
//
// Guard pages are created before and after the writable memory.
class PageMemory {
USING_FAST_MALLOC(PageMemory);
public:
~PageMemory() {
__lsan_unregister_root_region(writable_.Base(), writable_.size());
reserved_->PageDeleted(WritableStart());
}
WARN_UNUSED_RESULT bool Commit() {
reserved_->MarkPageUsed(WritableStart());
// Check that in-use page isn't also marked as being a non-heap page
// by the current heap's negative cache. That cache is invalidated
// when allocating new pages, but crbug.com/649485 suggests that
// we do get out of sync somehow.
//
// TODO(sof): consider removing check once bug has been diagnosed
// and addressed.
CHECK(!ThreadState::Current()->Heap().IsAddressInHeapDoesNotContainCache(
WritableStart()));
return writable_.Commit();
}
void Decommit() {
reserved_->MarkPageUnused(WritableStart());
writable_.Decommit();
}
void MarkUnused() { reserved_->MarkPageUnused(WritableStart()); }
PageMemoryRegion* Region() { return reserved_; }
Address WritableStart() { return writable_.Base(); }
static PageMemory* SetupPageMemoryInRegion(PageMemoryRegion*,
size_t page_offset,
size_t payload_size);
// Allocate a virtual address space for one blink page with the
// following layout:
//
// [ guard os page | ... payload ... | guard os page ]
// ^---{ aligned to blink page size }
//
// The returned page memory region will be zeroed.
//
static PageMemory* Allocate(size_t payload_size, RegionTree*);
private:
PageMemory(PageMemoryRegion* reserved, const MemoryRegion& writable);
PageMemoryRegion* reserved_;
MemoryRegion writable_;
};
} // namespace blink
#endif