diff --git a/src/snmalloc/backend/backend.h b/src/snmalloc/backend/backend.h index 5bf16a6..cbff62b 100644 --- a/src/snmalloc/backend/backend.h +++ b/src/snmalloc/backend/backend.h @@ -24,135 +24,72 @@ namespace snmalloc class BackendAllocator : public CommonConfig { public: + class PageMapEntry; using Pal = PAL; - using SlabMetadata = FrontendSlabMetadata; - class Pagemap + private: + using ConcretePagemap = + FlatPagemap; + + public: + /** + * Example of type stored in the pagemap. + * The following class could be replaced by: + * + * ``` + * using PageMapEntry = FrontendMetaEntry; + * ``` + * + * The full form here provides an example of how to extend the pagemap + * entries. It also guarantees that the front end never directly + * constructs meta entries, it only ever reads them or modifies them in + * place. + */ + class PageMapEntry : public FrontendMetaEntry { + /** + * The private initialising constructor is usable only by this back end. + */ friend class BackendAllocator; - public: /** - * Export the type stored in the pagemap. - * The following class could be replaced by: - * - * ``` - * using Entry = FrontendMetaEntry; - * ``` - * - * The full form here provides an example of how to extend the pagemap - * entries. It also guarantees that the front end never directly - * constructs meta entries, it only ever reads them or modifies them in - * place. + * The private default constructor is usable only by the pagemap. */ - class Entry : public FrontendMetaEntry - { - /** - * The private initialising constructor is usable only by this back end. - */ - friend class BackendAllocator; - - /** - * The private default constructor is usable only by the pagemap. - */ - friend class FlatPagemap; - - /** - * The only constructor that creates newly initialised meta entries. - * This is callable only by the back end. The front end may copy, - * query, and update these entries, but it may not create them - * directly. This contract allows the back end to store any arbitrary - * metadata in meta entries when they are first constructed. - */ - SNMALLOC_FAST_PATH - Entry(SlabMetadata* meta, uintptr_t ras) - : FrontendMetaEntry(meta, ras) - {} - - /** - * Default constructor. This must be callable from the pagemap. - */ - SNMALLOC_FAST_PATH Entry() = default; - - /** - * Copy assignment is used only by the pagemap. - */ - Entry& operator=(const Entry& other) - { - FrontendMetaEntry::operator=(other); - return *this; - } - }; - - private: - SNMALLOC_REQUIRE_CONSTINIT - static inline FlatPagemap - concretePagemap; + friend ConcretePagemap; /** - * Set the metadata associated with a chunk. + * The only constructor that creates newly initialised meta entries. + * This is callable only by the back end. The front end may copy, + * query, and update these entries, but it may not create them + * directly. This contract allows the back end to store any arbitrary + * metadata in meta entries when they are first constructed. */ SNMALLOC_FAST_PATH - static void set_metaentry(address_t p, size_t size, const Entry& t) - { - for (address_t a = p; a < p + size; a += MIN_CHUNK_SIZE) - { - concretePagemap.set(a, t); - } - } + PageMapEntry(SlabMetadata* meta, uintptr_t ras) + : FrontendMetaEntry(meta, ras) + {} - public: /** - * Get the metadata associated with a chunk. - * - * Set template parameter to true if it not an error - * to access a location that is not backed by a chunk. + * Copy assignment is used only by the pagemap. */ - template - SNMALLOC_FAST_PATH static const auto& get_metaentry(address_t p) + PageMapEntry& operator=(const PageMapEntry& other) { - return concretePagemap.template get(p); + FrontendMetaEntry::operator=(other); + return *this; } /** - * Get the metadata associated with a chunk. - * - * Set template parameter to true if it not an error - * to access a location that is not backed by a chunk. + * Default constructor. This must be callable from the pagemap. */ - template - SNMALLOC_FAST_PATH static auto& get_metaentry_mut(address_t p) - { - return concretePagemap.template get_mut(p); - } - - static void register_range(address_t p, size_t sz) - { - concretePagemap.register_range(p, sz); - } - - /** - * Return the bounds of the memory this back-end manages as a pair of - * addresses (start then end). This is available iff this is a - * fixed-range Backend. - */ - template - static SNMALLOC_FAST_PATH - std::enable_if_t> - get_bounds() - { - static_assert( - fixed_range_ == fixed_range, "Don't set SFINAE parameter!"); - - return concretePagemap.get_bounds(); - } - - static bool is_initialised() - { - return concretePagemap.is_initialised(); - } + SNMALLOC_FAST_PATH PageMapEntry() = default; }; + using Pagemap = BasicPagemap< + BackendAllocator, + PAL, + ConcretePagemap, + PageMapEntry, + fixed_range>; #if defined(_WIN32) || defined(__CHERI_PURE_CAPABILITY__) static constexpr bool CONSOLIDATE_PAL_ALLOCS = false; diff --git a/src/snmalloc/backend_helpers/pagemap.h b/src/snmalloc/backend_helpers/pagemap.h index ec7bbdf..15ff51c 100644 --- a/src/snmalloc/backend_helpers/pagemap.h +++ b/src/snmalloc/backend_helpers/pagemap.h @@ -325,4 +325,105 @@ namespace snmalloc body[p >> SHIFT] = t; } }; + + /** + * This is a generic implementation of the backend's interface to the page + * map. It takes a concrete page map implementation (probably FlatPageMap + * above) and entry type. It is friends with the backend passed in as a + * template parameter so that the backend can initialise the concrete page map + * and use set_metaentry which no one else should use. + */ + template< + typename Backend, + typename PAL, + typename ConcreteMap, + typename PageMapEntry, + bool fixed_range> + class BasicPagemap + { + public: + /** + * Export the type stored in the pagemap. + */ + using Entry = PageMapEntry; + + private: + friend Backend; + + /** + * Instance of the concrete pagemap, accessible to the backend so that + * it can call the init method whose type dependent on fixed_range. + */ + SNMALLOC_REQUIRE_CONSTINIT + static inline ConcreteMap concretePagemap; + + /** + * Set the metadata associated with a chunk. + */ + SNMALLOC_FAST_PATH + static void set_metaentry(address_t p, size_t size, const Entry& t) + { + for (address_t a = p; a < p + size; a += MIN_CHUNK_SIZE) + { + concretePagemap.set(a, t); + } + } + + public: + /** + * Get the metadata associated with a chunk. + * + * Set template parameter to true if it not an error + * to access a location that is not backed by a chunk. + */ + template + SNMALLOC_FAST_PATH static const auto& get_metaentry(address_t p) + { + return concretePagemap.template get(p); + } + + /** + * Get the metadata associated with a chunk. + * + * Set template parameter to true if it not an error + * to access a location that is not backed by a chunk. + */ + template + SNMALLOC_FAST_PATH static auto& get_metaentry_mut(address_t p) + { + return concretePagemap.template get_mut(p); + } + + /** + * Register a range in the pagemap as in-use, requiring it to allow writing + * to the underlying memory. + */ + static void register_range(address_t p, size_t sz) + { + concretePagemap.register_range(p, sz); + } + + /** + * Return the bounds of the memory this back-end manages as a pair of + * addresses (start then end). This is available iff this is a + * fixed-range Backend. + */ + template + static SNMALLOC_FAST_PATH + std::enable_if_t> + get_bounds() + { + static_assert(fixed_range_ == fixed_range, "Don't set SFINAE parameter!"); + + return concretePagemap.get_bounds(); + } + + /** + * Return whether the pagemap is initialised, ready for access. + */ + static bool is_initialised() + { + return concretePagemap.is_initialised(); + } + }; } // namespace snmalloc