2 +----------------------------------------------------------------------+
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 +----------------------------------------------------------------------+
17 #include "hphp/util/managed-arena.h"
19 #include "hphp/util/address-range.h"
20 #include "hphp/util/assertions.h"
22 #if USE_JEMALLOC_EXTENT_HOOKS
24 namespace HPHP
{ namespace alloc
{
26 static_assert(sizeof(RangeState
) <= 64, "");
27 static_assert(alignof(RangeState
) <= 64, "");
28 using RangeStateStorage
= std::aligned_storage
<sizeof(RangeState
), 64>::type
;
30 RangeArenaStorage g_lowerArena
{};
31 RangeArenaStorage g_lowArena
{};
32 RangeArenaStorage g_highArena
{};
33 RangeArenaStorage g_coldArena
{};
34 RangeArenaStorage g_lowColdArena
{};
35 RangeStateStorage g_ranges
[3];
37 PreMappedArena
* g_arena0
; // arena 0, if we end up injecting pages
38 std::vector
<PreMappedArena
*> g_local_arenas
; // keyed by numa node id
40 NEVER_INLINE RangeState
& getRange(AddrRangeClass index
) {
41 auto result
= reinterpret_cast<RangeState
*>(g_ranges
+ index
);
43 static std::atomic_flag lock
= ATOMIC_FLAG_INIT
;
44 while (lock
.test_and_set(std::memory_order_acquire
)) {
45 // Spin while another thread initializes the ranges. We don't really reach
46 // here, because the function is called very early during process
47 // initialization when there is only one thread. Do it just for extra
48 // safety in case someone starts to abuse the code.
50 if (!result
->high()) {
51 new (&(g_ranges
[AddrRangeClass::VeryLow
]))
52 RangeState(kLowArenaMinAddr
, 2ull << 30);
53 new (&(g_ranges
[AddrRangeClass::Low
]))
54 RangeState(2ull << 30, kLowArenaMaxAddr
);
55 new (&(g_ranges
[AddrRangeClass::Uncounted
]))
56 RangeState(kLowArenaMaxAddr
, kHighArenaMaxAddr
);
58 lock
.clear(std::memory_order_release
);
59 assertx(result
->high());
64 //////////////////////////////////////////////////////////////////////
66 template<typename ExtentAllocator
>
67 std::string ManagedArena
<ExtentAllocator
>::reportStats() {
69 using Traits
= extent_allocator_traits
<ExtentAllocator
>;
70 std::snprintf(buffer
, sizeof(buffer
),
71 "Arena %d: capacity %zd, max_capacity %zd, used %zd\n",
73 ExtentAllocator::allocatedSize(),
74 ExtentAllocator::maxCapacity(),
75 s_pageSize
* mallctl_pactive(id()));
76 return std::string
{buffer
};
79 template<typename ExtentAllocator
>
80 size_t ManagedArena
<ExtentAllocator
>::unusedSize() {
81 auto const active
= s_pageSize
* mallctl_pactive(id());
82 return ExtentAllocator::allocatedSize() - active
;
85 template<typename ExtentAllocator
>
86 void ManagedArena
<ExtentAllocator
>::create() {
87 using Traits
= extent_allocator_traits
<ExtentAllocator
>;
88 assertx(m_arenaId
== kInvalidArena
);
89 size_t idSize
= sizeof(m_arenaId
);
90 if (mallctl("arenas.create", &m_arenaId
, &idSize
, nullptr, 0)) {
91 throw std::runtime_error
{"arenas.create"};
94 ssize_t decay_ms
= Traits::get_decay_ms();
95 std::snprintf(command
, sizeof(command
),
96 "arena.%d.dirty_decay_ms", m_arenaId
);
97 if (mallctl(command
, nullptr, nullptr, &decay_ms
, sizeof(decay_ms
))) {
98 throw std::runtime_error
{command
};
103 template<typename ExtentAllocator
>
104 void ManagedArena
<ExtentAllocator
>::updateHook() {
105 using Traits
= extent_allocator_traits
<ExtentAllocator
>;
106 if (auto hooks_ptr
= Traits::get_hooks()) {
107 // We need to do `GetByArenaId()` in custom extent hooks, so register the
108 // arena in the global list. It is important that we do this before actually
109 // updating the hooks.
110 assertx(GetByArenaId
<ManagedArena
>(m_arenaId
) == nullptr);
111 bool registered
= false;
112 for (auto& i
: g_arenas
) {
121 throw std::out_of_range
{
122 "too many ManagedArena's, check MAX_MANAGED_ARENA_COUNT"};
126 std::snprintf(command
, sizeof(command
), "arena.%d.extent_hooks", m_arenaId
);
127 auto fallback
= Traits::get_fallback(this);
128 size_t oldSize
= sizeof(extent_hooks_t
*);
129 if (mallctl(command
, fallback
, (fallback
? &oldSize
: nullptr),
130 &hooks_ptr
, sizeof(hooks_ptr
))) {
131 throw std::runtime_error
{command
};
133 #if (JEMALLOC_VERSION_MAJOR > 5) || \
134 ((JEMALLOC_VERSION_MAJOR == 5) && (JEMALLOC_VERSION_MINOR >= 1))
135 // Avoid asking excessive memory through the hook, in order to make better
136 // use of preallocated pages.
137 std::snprintf(command
, sizeof(command
),
138 "arena.%d.retain_grow_limit", m_arenaId
);
139 size_t limit
= 16ull << 20;
140 if (mallctl(command
, nullptr, nullptr, &limit
, sizeof(limit
))) {
141 throw std::runtime_error
{command
};
147 template void ManagedArena
<MultiRangeExtentAllocator
>::create();
148 template void ManagedArena
<MultiRangeExtentAllocator
>::updateHook();
149 template size_t ManagedArena
<MultiRangeExtentAllocator
>::unusedSize();
150 template std::string ManagedArena
<MultiRangeExtentAllocator
>::reportStats();
152 template void ManagedArena
<DefaultExtentAllocator
>::create();
153 template void ManagedArena
<DefaultExtentAllocator
>::updateHook();
154 template void ManagedArena
<RangeFallbackExtentAllocator
>::create();
155 template void ManagedArena
<RangeFallbackExtentAllocator
>::updateHook();