Reduce code duplication. (#220)
* Reduce code duplication. The Apple and Haiku PALs both had *almost* identical code to the POSIX PAL, differing only in some small argument variables. This is fragile and easy to accidentally get out of sync. For example, the changes to `reserve_at_least` involved copying identical code into multiple PALs and it's easy to accidentally miss one. This change introduces two optional fields on POSIX-derived PALs: - `default_mmap_flags` allows a PAL to provide additional `MAP_*` flags to all `mmap` calls. - `anonymous_memory_fd` allows the PAL to override the default file descriptor used for memory mappings. If a PAL does not provide these, default values are used. Fixes #219
This commit is contained in:
10
README.md
10
README.md
@@ -243,6 +243,16 @@ The [Windows](src/pal/pal_windows.h), and
|
||||
[FreeBSD kernel](src/pal/pal_freebsd_kernel.h) implementations give examples of
|
||||
non-POSIX environments that snmalloc supports.
|
||||
|
||||
The POSIX PAL uses `mmap` to map memory.
|
||||
Some POSIX or POSIX-like systems require minor tweaks to this behaviour.
|
||||
Rather than requiring these to copy and paste the code, a PAL that inherits from the POSIX PAL can define one or both of these (`static constexpr`) fields to customise the `mmap` behaviour.
|
||||
|
||||
- `default_mmap_flags` allows a PAL to provide additional `MAP_*`
|
||||
flags to all `mmap` calls.
|
||||
- `anonymous_memory_fd` allows the PAL to override the default file
|
||||
descriptor used for memory mappings.
|
||||
|
||||
|
||||
# Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
|
||||
@@ -25,70 +25,16 @@ namespace snmalloc
|
||||
static constexpr uint64_t pal_features = PALBSD::pal_features;
|
||||
|
||||
/**
|
||||
* OS specific function for zeroing memory with the Apple application
|
||||
* tag id.
|
||||
*
|
||||
* See comment below.
|
||||
*/
|
||||
template<bool page_aligned = false>
|
||||
void zero(void* p, size_t size)
|
||||
{
|
||||
if (page_aligned || is_aligned_block<page_size>(p, size))
|
||||
{
|
||||
SNMALLOC_ASSERT(is_aligned_block<page_size>(p, size));
|
||||
void* r = mmap(
|
||||
p,
|
||||
size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
|
||||
pal_anon_id,
|
||||
0);
|
||||
|
||||
if (r != MAP_FAILED)
|
||||
return;
|
||||
}
|
||||
|
||||
bzero(p, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve memory with the Apple application tag id.
|
||||
*
|
||||
* See comment below.
|
||||
*/
|
||||
std::pair<void*, size_t> reserve_at_least(size_t size)
|
||||
{
|
||||
// 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,
|
||||
pal_anon_id,
|
||||
0);
|
||||
|
||||
if (p == MAP_FAILED)
|
||||
error("Out of memory");
|
||||
|
||||
return {p, size_request};
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* Anonymous page tag ID
|
||||
* Anonymous page tag ID.
|
||||
*
|
||||
* Darwin platform allows to gives an ID to anonymous pages via
|
||||
* the VM_MAKE_TAG's macro, from 240 up to 255 are guaranteed
|
||||
* to be free of usage, however eventually a lower could be taken
|
||||
* (e.g. LLVM sanitizers has 99) so we can monitor their states
|
||||
* via vmmap for instance.
|
||||
* via vmmap for instance. This value is provided to `mmap` as the file
|
||||
* descriptor for the mapping.
|
||||
*/
|
||||
static constexpr int pal_anon_id = VM_MAKE_TAG(PALAnonID);
|
||||
static constexpr int anonymous_memory_fd = VM_MAKE_TAG(PALAnonID);
|
||||
};
|
||||
} // namespace snmalloc
|
||||
#endif
|
||||
|
||||
@@ -21,6 +21,12 @@ namespace snmalloc
|
||||
*/
|
||||
static constexpr uint64_t pal_features = PALPOSIX::pal_features;
|
||||
|
||||
/**
|
||||
* Haiku requires an explicit no-reserve flag in `mmap` to guarantee lazy
|
||||
* commit.
|
||||
*/
|
||||
static constexpr int default_mmap_flags = MAP_NORESERVE;
|
||||
|
||||
/**
|
||||
* Notify platform that we will not be needing these pages.
|
||||
* Haiku does not provide madvise call per say only the posix equivalent.
|
||||
@@ -30,55 +36,6 @@ namespace snmalloc
|
||||
SNMALLOC_ASSERT(is_aligned_block<page_size>(p, size));
|
||||
posix_madvise(p, size, POSIX_MADV_DONTNEED);
|
||||
}
|
||||
|
||||
/**
|
||||
* OS specific function for zeroing memory
|
||||
* using MAP_NORESERVE for explicit over commit appliance.
|
||||
*/
|
||||
template<bool page_aligned = false>
|
||||
void zero(void* p, size_t size)
|
||||
{
|
||||
if (page_aligned || is_aligned_block<page_size>(p, size))
|
||||
{
|
||||
SNMALLOC_ASSERT(is_aligned_block<page_size>(p, size));
|
||||
void* r = mmap(
|
||||
p,
|
||||
size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_NORESERVE,
|
||||
-1,
|
||||
0);
|
||||
|
||||
if (r != MAP_FAILED)
|
||||
return;
|
||||
}
|
||||
|
||||
bzero(p, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserve memory using MAP_NORESERVE for explicit
|
||||
* over commit applicance.
|
||||
*/
|
||||
std::pair<void*, size_t> reserve_at_least(size_t size)
|
||||
{
|
||||
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 | MAP_NORESERVE,
|
||||
-1,
|
||||
0);
|
||||
|
||||
if (p == MAP_FAILED)
|
||||
error("Out of memory");
|
||||
|
||||
return {p, size_request};
|
||||
}
|
||||
};
|
||||
} // namespace snmalloc
|
||||
#endif
|
||||
|
||||
@@ -30,6 +30,64 @@ namespace snmalloc
|
||||
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
|
||||
@@ -128,8 +186,8 @@ namespace snmalloc
|
||||
p,
|
||||
size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
|
||||
-1,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | DefaultMMAPFlags<OS>::flags,
|
||||
AnonFD<OS>::fd,
|
||||
0);
|
||||
|
||||
if (r != MAP_FAILED)
|
||||
@@ -159,8 +217,8 @@ namespace snmalloc
|
||||
nullptr,
|
||||
size_request,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | DefaultMMAPFlags<OS>::flags,
|
||||
AnonFD<OS>::fd,
|
||||
0);
|
||||
|
||||
if (p == MAP_FAILED)
|
||||
|
||||
Reference in New Issue
Block a user