Optional Two-phase heap tracing
[hiphop-php.git] / hphp / util / read-only-arena.cpp
blob8ca7a80a1e24bb1f69b8cfd0a37b064f300b1fa4
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/util/read-only-arena.h"
18 #include <stdlib.h>
20 #include <folly/Exception.h>
21 #include <folly/portability/SysMman.h>
23 #include "hphp/util/assertions.h"
25 namespace HPHP {
27 //////////////////////////////////////////////////////////////////////
29 typedef std::lock_guard<std::mutex> guard;
31 namespace {
34 * Check return codes on mprotect---we're going to hand memory back to malloc
35 * that we've fiddled with protection on, so if something fails it could cause
36 * issues in completely unrelated parts of the code.
38 void checked_mprotect(void* addr, size_t size, int prot) {
39 auto const rval = mprotect(addr, size, prot);
40 always_assert_flog(rval == 0,
41 "mprotect({},{},{})={} failed in ReadOnlyArena",
42 addr, size,
43 prot == PROT_READ ? "R" : "RW",
44 rval);
49 //////////////////////////////////////////////////////////////////////
51 ReadOnlyArena::ReadOnlyArena(size_t minChunkSize)
52 : m_minChunkSize((minChunkSize + (s_pageSize - 1)) & ~(s_pageSize - 1))
53 , m_frontier(nullptr)
54 , m_end(nullptr)
56 always_assert(m_minChunkSize % s_pageSize == 0);
59 ReadOnlyArena::~ReadOnlyArena() {
60 for (auto& chunk : m_chunks) {
61 checked_mprotect(chunk.data(), chunk.size(), PROT_READ|PROT_WRITE);
62 std::free(chunk.data());
66 const void* ReadOnlyArena::allocate(const void* data, size_t dataLen) {
67 guard g(m_mutex);
69 // Round up to the minimal alignment.
70 auto alignedLen =
71 (dataLen + (kMinimalAlignment - 1)) & ~(kMinimalAlignment - 1);
73 ensureFree(alignedLen);
74 always_assert(m_frontier + alignedLen <= m_end);
76 auto const ret = m_frontier;
77 assert((uintptr_t(ret) & (kMinimalAlignment - 1)) == 0);
79 m_frontier += alignedLen;
81 auto pageAddr = reinterpret_cast<unsigned char*>(
82 uintptr_t(ret) & ~(s_pageSize - 1)
84 checked_mprotect(pageAddr, m_frontier - pageAddr, PROT_WRITE|PROT_READ);
85 auto const ucData = static_cast<const unsigned char*>(data);
86 std::copy(ucData, ucData + dataLen, ret);
87 checked_mprotect(pageAddr, m_frontier - pageAddr, PROT_READ);
89 return ret;
92 // Pre: mutex already held, or no other threads may be able to access this
93 // (i.e. it's the ctor).
95 // Post: m_end - m_frontier >= bytes
96 void ReadOnlyArena::ensureFree(size_t bytes) {
97 if (m_end - m_frontier >= bytes) return;
99 // Allocate a multiple of s_pageSize, and no less than m_minChunkSize.
100 if (bytes > m_minChunkSize) {
101 bytes = (bytes + (s_pageSize - 1)) & ~(s_pageSize - 1);
102 } else {
103 bytes = m_minChunkSize;
106 void* vp;
107 if (auto err = posix_memalign(&vp, s_pageSize, bytes)) {
108 folly::throwSystemError(err, "failed to posix_memalign in "
109 "ReadOnlyArena");
111 checked_mprotect(vp, bytes, PROT_READ);
113 auto uc = static_cast<unsigned char*>(vp);
114 m_frontier = uc;
115 m_end = uc + bytes;
116 m_chunks.emplace_back(m_frontier, m_end);
119 size_t ReadOnlyArena::capacity() const {
120 guard g(m_mutex);
121 size_t size = 0;
122 for (auto& chunk : m_chunks) {
123 size += chunk.size();
125 return size;
128 //////////////////////////////////////////////////////////////////////