forked from microsoft/snmalloc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpal_posix.h
230 lines (210 loc) · 6.76 KB
/
pal_posix.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#pragma once
#include "../ds/address.h"
#if defined(BACKTRACE_HEADER)
# include BACKTRACE_HEADER
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <utility>
extern "C" int puts(const char* str);
namespace snmalloc
{
/**
* Platform abstraction layer for generic POSIX systems.
*
* This provides the lowest common denominator for POSIX systems. It should
* work on pretty much any POSIX system, but won't necessarily be the most
* efficient implementation. Subclasses should provide more efficient
* implementations using platform-specific functionality.
*
* The template parameter for this is the subclass and is used for explicit
* up casts to allow this class to call non-virtual methods on the templated
* version.
*/
template<class OS>
class PALPOSIX
{
/**
* Helper class to access the `default_mmap_flags` field of `OS` if one
* exists or a default value if not. This provides the default version,
* which is used if `OS::default_mmap_flags` does not exist.
*/
template<typename T, typename = int>
struct DefaultMMAPFlags
{
/**
* If `OS::default_mmap_flags` does not exist, use 0. This value is
* or'd with the other mmap flags and so a value of 0 is a no-op.
*/
static const int flags = 0;
};
/**
* Helper class to access the `default_mmap_flags` field of `OS` if one
* exists or a default value if not. This provides the version that
* accesses the field, allowing other PALs to provide extra arguments to
* the `mmap` calls used here.
*/
template<typename T>
struct DefaultMMAPFlags<T, decltype((void)T::default_mmap_flags, 0)>
{
static const int flags = T::default_mmap_flags;
};
/**
* Helper class to allow `OS` to provide the file descriptor used for
* anonymous memory. This is the default version, which provides the POSIX
* default of -1.
*/
template<typename T, typename = int>
struct AnonFD
{
/**
* If `OS::anonymous_memory_fd` does not exist, use -1. This value is
* defined by POSIX.
*/
static const int fd = -1;
};
/**
* Helper class to allow `OS` to provide the file descriptor used for
* anonymous memory. This exposes the `anonymous_memory_fd` field in `OS`.
*/
template<typename T>
struct AnonFD<T, decltype((void)T::anonymous_memory_fd, 0)>
{
/**
* The PAL's provided file descriptor for anonymous memory. This is
* used, for example, on Apple platforms, which use the file descriptor
* in a `MAP_ANONYMOUS` mapping to encode metadata about the owner of the
* mapping.
*/
static const int fd = T::anonymous_memory_fd;
};
public:
/**
* Bitmap of PalFeatures flags indicating the optional features that this
* PAL supports.
*
* POSIX systems are assumed to support lazy commit.
*/
static constexpr uint64_t pal_features = LazyCommit;
static constexpr size_t page_size = 0x1000;
static void print_stack_trace()
{
#ifdef BACKTRACE_HEADER
constexpr int SIZE = 1024;
void* buffer[SIZE];
auto nptrs = backtrace(buffer, SIZE);
fflush(stdout);
backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO);
puts("");
fflush(stdout);
#endif
}
/**
* Report a fatal error an exit.
*/
[[noreturn]] static void error(const char* const str) noexcept
{
puts(str);
print_stack_trace();
abort();
}
/**
* Notify platform that we will not be using these pages.
*
* This does nothing in a generic POSIX implementation. Most POSIX systems
* provide an `madvise` call that can be used to return pages to the OS in
* high memory pressure conditions, though on Linux this seems to impose
* too much of a performance penalty.
*/
void notify_not_using(void* p, size_t size) noexcept
{
SNMALLOC_ASSERT(is_aligned_block<OS::page_size>(p, size));
#ifdef USE_POSIX_COMMIT_CHECKS
mprotect(p, size, PROT_NONE);
#else
UNUSED(p);
UNUSED(size);
#endif
}
/**
* Notify platform that we will be using these pages.
*
* On POSIX platforms, lazy commit means that this is a no-op, unless we
* are also zeroing the pages in which case we call the platform's `zero`
* function.
*/
template<ZeroMem zero_mem>
void notify_using(void* p, size_t size) noexcept
{
SNMALLOC_ASSERT(
is_aligned_block<OS::page_size>(p, size) || (zero_mem == NoZero));
#ifdef USE_POSIX_COMMIT_CHECKS
mprotect(p, size, PROT_READ | PROT_WRITE);
#else
UNUSED(p);
UNUSED(size);
#endif
if constexpr (zero_mem == YesZero)
static_cast<OS*>(this)->template zero<true>(p, size);
}
/**
* OS specific function for zeroing memory.
*
* The generic POSIX implementation uses mmap to map anonymous memory over
* the range for ranges larger than a page. The underlying OS is assumed
* to provide new CoW copies of the zero page.
*
* Note: On most systems it is faster for a single page to zero the memory
* explicitly than do this, we should probably tweak the threshold for
* calling bzero at some point.
*/
template<bool page_aligned = false>
void zero(void* p, size_t size) noexcept
{
if (page_aligned || is_aligned_block<OS::page_size>(p, size))
{
SNMALLOC_ASSERT(is_aligned_block<OS::page_size>(p, size));
void* r = mmap(
p,
size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | DefaultMMAPFlags<OS>::flags,
AnonFD<OS>::fd,
0);
if (r != MAP_FAILED)
return;
}
bzero(p, size);
}
/**
* Reserve memory.
*
* POSIX platforms support lazy commit, and so this also puts the memory in
* the lazy commit state (i.e. pages will be allocated on first use).
*
* POSIX does not define a portable interface for specifying alignment
* greater than a page.
*/
std::pair<void*, size_t> reserve_at_least(size_t size) noexcept
{
// Magic number for over-allocating chosen by the Pal
// These should be further refined based on experiments.
constexpr size_t min_size =
bits::is64() ? bits::one_at_bit(32) : bits::one_at_bit(28);
auto size_request = bits::max(size, min_size);
void* p = mmap(
nullptr,
size_request,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | DefaultMMAPFlags<OS>::flags,
AnonFD<OS>::fd,
0);
if (p == MAP_FAILED)
OS::error("Out of memory");
return {p, size_request};
}
};
} // namespace snmalloc