#pragma once #include "freelist.h" #include "superslab.h" #include namespace snmalloc { class Slab { private: uint16_t address_to_index(address_t p) { // Get the offset from the slab for a memory location. return static_cast(p - address_cast(this)); } public: static Metaslab& get_meta(Slab* self) { Superslab* super = Superslab::get(self); return super->get_meta(self); } /** * Given a bumpptr and a fast_free_list head reference, builds a new free * list, and stores it in the fast_free_list. It will only create a page * worth of allocations, or one if the allocation size is larger than a * page. */ static SNMALLOC_FAST_PATH void alloc_new_list( void*& bumpptr, FreeListIter& fast_free_list, size_t rsize, LocalEntropy& entropy) { void* slab_end = pointer_align_up(pointer_offset(bumpptr, 1)); FreeListBuilder b; SNMALLOC_ASSERT(b.empty()); b.open(bumpptr); // This code needs generalising, but currently applies // various offsets with a stride of seven to increase chance of catching // accidental OOB write. std::array start_index = {3, 5, 0, 2, 4, 1, 6}; for (size_t offset : start_index) { void* newbumpptr = pointer_offset(bumpptr, rsize * offset); while (newbumpptr < slab_end) { b.add(newbumpptr, entropy); newbumpptr = pointer_offset(newbumpptr, rsize * start_index.size()); } } bumpptr = slab_end; SNMALLOC_ASSERT(!b.empty()); b.close(fast_free_list, entropy); } // Returns true, if it deallocation can proceed without changing any status // bits. Note that this does remove the use from the meta slab, so it // doesn't need doing on the slow path. // // This is pre-factored to take an explicit self parameter so that we can // eventually annotate that pointer with additional information. static SNMALLOC_FAST_PATH bool dealloc_fast(Slab* self, Superslab* super, void* p, LocalEntropy& entropy) { Metaslab& meta = super->get_meta(self); SNMALLOC_ASSERT(!meta.is_unused()); if (unlikely(meta.return_object())) return false; // Update the head and the next pointer in the free list. meta.free_queue.add(p, entropy); return true; } // If dealloc fast returns false, then call this. // This does not need to remove the "use" as done by the fast path. // Returns a complex return code for managing the superslab meta data. // i.e. This deallocation could make an entire superslab free. // // This is pre-factored to take an explicit self parameter so that we can // eventually annotate that pointer with additional information. static SNMALLOC_SLOW_PATH typename Superslab::Action dealloc_slow( Slab* self, SlabList* sl, Superslab* super, void* p, LocalEntropy& entropy) { Metaslab& meta = super->get_meta(self); meta.debug_slab_invariant(self, entropy); if (meta.is_full()) { auto allocated = get_slab_capacity( meta.sizeclass(), Metaslab::is_short(Metaslab::get_slab(p))); // We are not on the sizeclass list. if (allocated == 1) { // Dealloc on the superslab. if (Metaslab::is_short(self)) return super->dealloc_short_slab(); return super->dealloc_slab(self); } meta.free_queue.add(p, entropy); // Remove trigger threshold from how many we need before we have fully // freed the slab. meta.needed() = allocated - meta.threshold_for_waking_slab(Metaslab::is_short(self)); // Push on the list of slabs for this sizeclass. sl->insert_prev(&meta); meta.debug_slab_invariant(self, entropy); return Superslab::NoSlabReturn; } #ifdef CHECK_CLIENT size_t count = 1; // Check free list is well-formed on platforms with // integers as pointers. FreeListIter fl; meta.free_queue.close(fl, entropy); while (!fl.empty()) { fl.take(entropy); count++; } #endif meta.remove(); if (Metaslab::is_short(self)) return super->dealloc_short_slab(); return super->dealloc_slab(self); } }; } // namespace snmalloc