Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fully static PALs and simplified inheritance #245

Merged
merged 8 commits into from
Sep 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ macro(clangformat_targets)
else ()
message(STATUS "Generating clangformat target using ${CLANG_FORMAT}")
file(GLOB_RECURSE ALL_SOURCE_FILES *.cc *.h *.hh)
# clangformat does not yet understand concepts well; for the moment, don't
# ask it to format them. See https://reviews.llvm.org/D79773
list(FILTER ALL_SOURCE_FILES EXCLUDE REGEX "src/pal/pal_concept\.h$")
add_custom_target(
clangformat
COMMAND ${CLANG_FORMAT}
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,20 +160,20 @@ your system.
The PAL must implement the following methods:

```c++
[[noreturn]] void error(const char* const str) noexcept;
[[noreturn]] static void error(const char* const str) noexcept;
```
Report a fatal error and exit.

```c++
void notify_not_using(void* p, size_t size) noexcept;
static void notify_not_using(void* p, size_t size) noexcept;
```
Notify the system that the range of memory from `p` to `p` + `size` is no
longer in use, allowing the underlying physical pages to recycled for other
purposes.

```c++
template<ZeroMem zero_mem>
void notify_using(void* p, size_t size) noexcept;
static void notify_using(void* p, size_t size) noexcept;
```
Notify the system that the range of memory from `p` to `p` + `size` is now in use.
On systems that lazily provide physical memory to virtual mappings, this
Expand All @@ -183,7 +183,7 @@ responsible for ensuring that the newly requested memory is full of zeros.

```c++
template<bool page_aligned = false>
void zero(void* p, size_t size) noexcept;
static void zero(void* p, size_t size) noexcept;
```
Zero the range of memory from `p` to `p` + `size`.
This may be a simple `memset` call, but the `page_aligned` template parameter
Expand All @@ -194,8 +194,8 @@ pages, rather than zeroing them synchronously in this call

```c++
template<bool committed>
void* reserve_aligned(size_t size) noexcept;
std::pair<void*, size_t> reserve_at_least(size_t size) noexcept;
static void* reserve_aligned(size_t size) noexcept;
static std::pair<void*, size_t> reserve_at_least(size_t size) noexcept;
```
Only one of these needs to be implemented, depending on whether the underlying
system can provide strongly aligned memory regions.
Expand Down
7 changes: 7 additions & 0 deletions src/ds/aba.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,13 @@ namespace snmalloc
Linked old;
ABA* parent;

/*
* MSVC apparently does not like the implicit constructor it creates when
* asked to interpret its input as C++20; it rejects the construction up
* in read(), above. Help it out by making the constructor explicit.
*/
Cmp(Linked old, ABA* parent) : old(old), parent(parent) {}

T* ptr()
{
return old.ptr;
Expand Down
40 changes: 40 additions & 0 deletions src/ds/concept.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#pragma once

/**
* C++20 concepts are referenced as if they were types in declarations within
* template parameters (e.g. "template<FooConcept Foo> ..."). That is, they
* take the place of the "typename"/"class" keyword on template parameters.
* If the compiler understands concepts, this macro expands as its argument;
* otherwise, it expands to the keyword "typename", so snmalloc templates that
* use concept-qualified parameters should use this to remain compatible across
* C++ versions: "template<SNMALLOC_CONCEPT(FooConcept) Foo>"
*/
#ifdef __cpp_concepts
# define SNMALLOC_CONCEPT(c) c
nwf marked this conversation as resolved.
Show resolved Hide resolved
#else
# define SNMALLOC_CONCEPT(c) typename
#endif

#ifdef __cpp_concepts
namespace snmalloc
{
/**
* C++20 concepts are more than just new syntax; there's a new support
* library specified as well. As C++20 is quite new, however, there are some
* environments, notably Clang, that understand the syntax but do not yet
* offer the library. Fortunately, alternate pronouciations are possible.
*/
# ifdef _cpp_lib_concepts
/**
* ConceptSame<T,U> is true if T and U are the same type and false otherwise.
* When specifying a concept, use ConceptSame<U> to indicate that an
* expression must evaluate precisely to the type U.
*/
template<typename T, typename U>
concept ConceptSame = std::same_as<T, U>;
# else
template<typename T, typename U>
concept ConceptSame = std::is_same<T, U>::value;
# endif
} // namespace snmalloc
#endif
27 changes: 12 additions & 15 deletions src/mem/address_space.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ namespace snmalloc
* It cannot unreserve memory, so this does not require the
* usual complexity of a buddy allocator.
*/
template<typename Pal>
class AddressSpaceManager : public Pal
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
class AddressSpaceManager
{
/**
* Stores the blocks of address space
Expand Down Expand Up @@ -160,7 +160,7 @@ namespace snmalloc
auto page_start = pointer_align_down<OS_PAGE_SIZE, char>(base);
auto page_end =
pointer_align_up<OS_PAGE_SIZE, char>(pointer_offset(base, size));
Pal::template notify_using<NoZero>(
PAL::template notify_using<NoZero>(
page_start, static_cast<size_t>(page_end - page_start));
}

Expand All @@ -178,11 +178,10 @@ namespace snmalloc
SNMALLOC_ASSERT(bits::next_pow2(size) == size);
SNMALLOC_ASSERT(size >= sizeof(void*));

if constexpr (pal_supports<AlignedAllocation, Pal>)
if constexpr (pal_supports<AlignedAllocation, PAL>)
{
if (size >= Pal::minimum_alloc_size)
return static_cast<Pal*>(this)->template reserve_aligned<committed>(
size);
if (size >= PAL::minimum_alloc_size)
return PAL::template reserve_aligned<committed>(size);
}

void* res;
Expand All @@ -194,20 +193,18 @@ namespace snmalloc
// Allocation failed ask OS for more memory
void* block;
size_t block_size;
if constexpr (pal_supports<AlignedAllocation, Pal>)
if constexpr (pal_supports<AlignedAllocation, PAL>)
{
block_size = Pal::minimum_alloc_size;
block = static_cast<Pal*>(this)->template reserve_aligned<false>(
block_size);
block_size = PAL::minimum_alloc_size;
block = PAL::template reserve_aligned<false>(block_size);
}
else
{
// Need at least 2 times the space to guarantee alignment.
// Hold lock here as a race could cause additional requests to
// the Pal, and this could lead to suprious OOM. This is
// particularly bad if the Pal gives all the memory on first call.
auto block_and_size =
static_cast<Pal*>(this)->reserve_at_least(size * 2);
// the PAL, and this could lead to suprious OOM. This is
// particularly bad if the PAL gives all the memory on first call.
auto block_and_size = PAL::reserve_at_least(size * 2);
block = block_and_size.first;
block_size = block_and_size.second;

Expand Down
12 changes: 6 additions & 6 deletions src/mem/alloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -1063,7 +1063,7 @@ namespace snmalloc
void* p = remove_cache_friendly_offset(head, sizeclass);
if constexpr (zero_mem == YesZero)
{
large_allocator.memory_provider.zero(p, sizeclass_to_size(sizeclass));
MemoryProvider::Pal::zero(p, sizeclass_to_size(sizeclass));
}
return p;
}
Expand Down Expand Up @@ -1109,8 +1109,8 @@ namespace snmalloc
SlabLink* link = sl.get_next();
slab = get_slab(link);
auto& ffl = small_fast_free_lists[sizeclass];
return slab->alloc<zero_mem>(
sl, ffl, rsize, large_allocator.memory_provider);
return slab->alloc<zero_mem, typename MemoryProvider::Pal>(
sl, ffl, rsize);
}
return small_alloc_rare<zero_mem, allow_reserve>(sizeclass, size);
}
Expand Down Expand Up @@ -1182,7 +1182,7 @@ namespace snmalloc

if constexpr (zero_mem == YesZero)
{
large_allocator.memory_provider.zero(p, sizeclass_to_size(sizeclass));
MemoryProvider::Pal::zero(p, sizeclass_to_size(sizeclass));
}
return p;
}
Expand Down Expand Up @@ -1313,7 +1313,7 @@ namespace snmalloc

if (slab != nullptr)
{
p = slab->alloc<zero_mem>(size, large_allocator.memory_provider);
p = slab->alloc<zero_mem, typename MemoryProvider::Pal>(size);

if (slab->full())
sc->pop();
Expand All @@ -1336,7 +1336,7 @@ namespace snmalloc

slab->init(public_state(), sizeclass, rsize);
chunkmap().set_slab(slab);
p = slab->alloc<zero_mem>(size, large_allocator.memory_provider);
p = slab->alloc<zero_mem, typename MemoryProvider::Pal>(size);

if (!slab->full())
sc->insert(slab);
Expand Down
22 changes: 12 additions & 10 deletions src/mem/largealloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

namespace snmalloc
{
template<class PAL>
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
class MemoryProviderStateMixin;

class Largeslab : public Baseslab
Expand All @@ -24,7 +24,7 @@ namespace snmalloc
private:
template<class a, Construction c>
friend class MPMCStack;
template<class PAL>
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
friend class MemoryProviderStateMixin;
std::atomic<Largeslab*> next;

Expand Down Expand Up @@ -56,8 +56,8 @@ namespace snmalloc
// This represents the state that the large allcoator needs to add to the
// global state of the allocator. This is currently stored in the memory
// provider, so we add this in.
template<class PAL>
class MemoryProviderStateMixin : public PalNotificationObject, public PAL
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
class MemoryProviderStateMixin : public PalNotificationObject
{
/**
* Simple flag for checking if another instance of lazy-decommit is
Expand All @@ -76,6 +76,8 @@ namespace snmalloc
std::atomic<size_t> peak_memory_used_bytes{0};

public:
using Pal = PAL;

/**
* Memory current available in large_stacks
*/
Expand Down Expand Up @@ -258,7 +260,7 @@ namespace snmalloc
p = memory_provider.template reserve<false>(large_class);
if (p == nullptr)
return nullptr;
memory_provider.template notify_using<zero_mem>(p, rsize);
MemoryProvider::Pal::template notify_using<zero_mem>(p, rsize);
}
else
{
Expand All @@ -276,19 +278,19 @@ namespace snmalloc
// The first page is already in "use" for the stack element,
// this will need zeroing for a YesZero call.
if constexpr (zero_mem == YesZero)
memory_provider.template zero<true>(p, OS_PAGE_SIZE);
MemoryProvider::Pal::template zero<true>(p, OS_PAGE_SIZE);

// Notify we are using the rest of the allocation.
// Passing zero_mem ensures the PAL provides zeroed pages if
// required.
memory_provider.template notify_using<zero_mem>(
MemoryProvider::Pal::template notify_using<zero_mem>(
pointer_offset(p, OS_PAGE_SIZE), rsize - OS_PAGE_SIZE);
}
else
{
// This is a superslab that has not been decommitted.
if constexpr (zero_mem == YesZero)
memory_provider.template zero<true>(
MemoryProvider::Pal::template zero<true>(
p, bits::align_up(size, OS_PAGE_SIZE));
else
UNUSED(size);
Expand All @@ -304,7 +306,7 @@ namespace snmalloc
if constexpr (decommit_strategy == DecommitSuperLazy)
{
static_assert(
pal_supports<LowMemoryNotification, MemoryProvider>,
pal_supports<LowMemoryNotification, typename MemoryProvider::Pal>,
"A lazy decommit strategy cannot be implemented on platforms "
"without low memory notifications");
}
Expand All @@ -316,7 +318,7 @@ namespace snmalloc
(decommit_strategy != DecommitNone) &&
(large_class != 0 || decommit_strategy == DecommitSuper))
{
memory_provider.notify_not_using(
MemoryProvider::Pal::notify_not_using(
pointer_offset(p, OS_PAGE_SIZE), rsize - OS_PAGE_SIZE);
}

Expand Down
6 changes: 3 additions & 3 deletions src/mem/mediumslab.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ namespace snmalloc
return sizeclass;
}

template<ZeroMem zero_mem, typename MemoryProvider>
void* alloc(size_t size, MemoryProvider& memory_provider)
template<ZeroMem zero_mem, SNMALLOC_CONCEPT(ConceptPAL) PAL>
void* alloc(size_t size)
{
SNMALLOC_ASSERT(!full());

Expand All @@ -86,7 +86,7 @@ namespace snmalloc
free--;

if constexpr (zero_mem == YesZero)
memory_provider.zero(p, size);
PAL::zero(p, size);
else
UNUSED(size);

Expand Down
3 changes: 2 additions & 1 deletion src/mem/pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "../ds/flaglock.h"
#include "../ds/mpmcstack.h"
#include "../pal/pal_concept.h"
#include "pooled.h"

namespace snmalloc
Expand All @@ -21,7 +22,7 @@ namespace snmalloc
{
private:
friend Pooled<T>;
template<typename TT>
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
friend class MemoryProviderStateMixin;

std::atomic_flag lock = ATOMIC_FLAG_INIT;
Expand Down
13 changes: 5 additions & 8 deletions src/mem/slab.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,9 @@ namespace snmalloc
* Returns the link as the allocation, and places the free list into the
* `fast_free_list` for further allocations.
*/
template<ZeroMem zero_mem, typename MemoryProvider>
SNMALLOC_FAST_PATH void* alloc(
SlabList& sl,
FreeListHead& fast_free_list,
size_t rsize,
MemoryProvider& memory_provider)
template<ZeroMem zero_mem, SNMALLOC_CONCEPT(ConceptPAL) PAL>
SNMALLOC_FAST_PATH void*
alloc(SlabList& sl, FreeListHead& fast_free_list, size_t rsize)
{
// Read the head from the metadata stored in the superslab.
Metaslab& meta = get_meta();
Expand Down Expand Up @@ -73,9 +70,9 @@ namespace snmalloc
if constexpr (zero_mem == YesZero)
{
if (rsize < PAGE_ALIGNED_SIZE)
memory_provider.zero(p, rsize);
PAL::zero(p, rsize);
else
memory_provider.template zero<true>(p, rsize);
PAL::template zero<true>(p, rsize);
}
else
{
Expand Down
4 changes: 3 additions & 1 deletion src/pal/pal.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "../ds/concept.h"
#include "pal_concept.h"
#include "pal_consts.h"

// If simultating OE, then we need the underlying platform
Expand Down Expand Up @@ -63,7 +65,7 @@ namespace snmalloc
/**
* Query whether the PAL supports a specific feature.
*/
template<PalFeatures F, typename PAL = Pal>
template<PalFeatures F, SNMALLOC_CONCEPT(ConceptPAL) PAL = Pal>
constexpr static bool pal_supports = (PAL::pal_features & F) == F;

// Used to keep Superslab metadata committed.
Expand Down
2 changes: 1 addition & 1 deletion src/pal/pal_bsd.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace snmalloc
* operating system to replace the pages with CoW copies of a zero page at
* any point between the call and the next write to that page.
*/
void notify_not_using(void* p, size_t size) noexcept
static void notify_not_using(void* p, size_t size) noexcept
{
SNMALLOC_ASSERT(is_aligned_block<OS::page_size>(p, size));
madvise(p, size, MADV_FREE);
Expand Down
Loading