blob: 314d5014bd9285d8140645ec6292d40d9b8b10e8 [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/HeapPage.h"
#include "wtf/Allocator.h"
#include "wtf/Assertions.h"
#include "wtf/allocator/PageAllocator.h"
#if OS(POSIX)
#include <sys/mman.h>
#include <unistd.h>
#endif
namespace blink {
class RegionTree;
class RegionTreeNode;
class MemoryRegion {
USING_FAST_MALLOC(MemoryRegion);
public:
MemoryRegion(Address base, size_t size)
: m_base(base)
, m_size(size)
{
ASSERT(size > 0);
}
bool contains(Address addr) const
{
return m_base <= addr && addr < (m_base + m_size);
}
bool contains(const MemoryRegion& other) const
{
return contains(other.m_base) && contains(other.m_base + other.m_size - 1);
}
void release();
WARN_UNUSED_RETURN bool commit();
void decommit();
Address base() const { return m_base; }
size_t size() const { return m_size; }
private:
Address m_base;
size_t m_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)
{
ASSERT(!m_inUse[index(page)]);
m_inUse[index(page)] = true;
}
void markPageUnused(Address page)
{
m_inUse[index(page)] = false;
}
static PageMemoryRegion* allocateLargePage(size_t size, RegionTree* regionTree)
{
return allocate(size, 1, regionTree);
}
static PageMemoryRegion* allocateNormalPages(RegionTree* regionTree)
{
return allocate(blinkPageSize * blinkPagesPerRegion, blinkPagesPerRegion, regionTree);
}
BasePage* pageFromAddress(Address address)
{
ASSERT(contains(address));
if (!m_inUse[index(address)])
return nullptr;
if (m_isLargePage)
return pageFromObject(base());
return pageFromObject(address);
}
private:
PageMemoryRegion(Address base, size_t, unsigned numPages, RegionTree*);
unsigned index(Address address) const
{
ASSERT(contains(address));
if (m_isLargePage)
return 0;
size_t offset = blinkPageAddress(address) - base();
ASSERT(offset % blinkPageSize == 0);
return offset / blinkPageSize;
}
static PageMemoryRegion* allocate(size_t, unsigned numPages, RegionTree*);
const bool m_isLargePage;
// A thread owns a page, but not a region. Represent the in-use
// bitmap such that thread non-interference comes for free.
bool m_inUse[blinkPagesPerRegion];
int m_numPages;
RegionTree* m_regionTree;
};
// A RegionTree is a simple binary search tree of PageMemoryRegions sorted
// by base addresses.
class RegionTree {
USING_FAST_MALLOC(RegionTree);
public:
RegionTree() : m_root(nullptr) { }
void add(PageMemoryRegion*);
void remove(PageMemoryRegion*);
PageMemoryRegion* lookup(Address);
private:
Mutex m_mutex;
RegionTreeNode* m_root;
};
class RegionTreeNode {
USING_FAST_MALLOC(RegionTreeNode);
public:
explicit RegionTreeNode(PageMemoryRegion* region)
: m_region(region)
, m_left(nullptr)
, m_right(nullptr)
{
}
~RegionTreeNode()
{
delete m_left;
delete m_right;
}
void addTo(RegionTreeNode** context);
private:
PageMemoryRegion* m_region;
RegionTreeNode* m_left;
RegionTreeNode* m_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(m_writable.base(), m_writable.size());
m_reserved->pageDeleted(writableStart());
}
WARN_UNUSED_RETURN bool commit()
{
m_reserved->markPageUsed(writableStart());
return m_writable.commit();
}
void decommit()
{
m_reserved->markPageUnused(writableStart());
m_writable.decommit();
}
void markUnused() { m_reserved->markPageUnused(writableStart()); }
PageMemoryRegion* region() { return m_reserved; }
Address writableStart() { return m_writable.base(); }
static PageMemory* setupPageMemoryInRegion(PageMemoryRegion*, size_t pageOffset, size_t payloadSize);
// 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 payloadSize, RegionTree*);
private:
PageMemory(PageMemoryRegion* reserved, const MemoryRegion& writable);
PageMemoryRegion* m_reserved;
MemoryRegion m_writable;
};
} // namespace blink
#endif