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:
David Chisnall
2020-06-30 13:33:50 +01:00
committed by GitHub
parent 4a3102fedb
commit 5199556263
4 changed files with 82 additions and 111 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)