From 5199556263edd04639eb8b272cfd9ba3bb48e56a Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Tue, 30 Jun 2020 13:33:50 +0100 Subject: [PATCH] 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 --- README.md | 10 +++++++ src/pal/pal_apple.h | 62 +++--------------------------------------- src/pal/pal_haiku.h | 55 +++++-------------------------------- src/pal/pal_posix.h | 66 ++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 82 insertions(+), 111 deletions(-) diff --git a/README.md b/README.md index 47da953..5f69911 100644 --- a/README.md +++ b/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 diff --git a/src/pal/pal_apple.h b/src/pal/pal_apple.h index 604538a..22600f8 100644 --- a/src/pal/pal_apple.h +++ b/src/pal/pal_apple.h @@ -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 - void zero(void* p, size_t size) - { - if (page_aligned || is_aligned_block(p, size)) - { - SNMALLOC_ASSERT(is_aligned_block(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 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 diff --git a/src/pal/pal_haiku.h b/src/pal/pal_haiku.h index 6c5340f..05ba71a 100644 --- a/src/pal/pal_haiku.h +++ b/src/pal/pal_haiku.h @@ -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(p, size)); posix_madvise(p, size, POSIX_MADV_DONTNEED); } - - /** - * OS specific function for zeroing memory - * using MAP_NORESERVE for explicit over commit appliance. - */ - template - void zero(void* p, size_t size) - { - if (page_aligned || is_aligned_block(p, size)) - { - SNMALLOC_ASSERT(is_aligned_block(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 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 diff --git a/src/pal/pal_posix.h b/src/pal/pal_posix.h index 9bed5ae..cb25acc 100644 --- a/src/pal/pal_posix.h +++ b/src/pal/pal_posix.h @@ -30,6 +30,64 @@ namespace snmalloc template 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 + 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 + struct DefaultMMAPFlags + { + 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 + 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 + struct AnonFD + { + /** + * 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::flags, + AnonFD::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::flags, + AnonFD::fd, 0); if (p == MAP_FAILED)