Reduce dependence on C++ runtime

If the external thread statics are used, then
we don't need to include some C++ runtime
concepts. This refactoring moves some global initialization under
conditional compilation.
This commit is contained in:
Matthew Parkinson
2020-02-26 17:55:29 +00:00
parent 3775a625a4
commit 9f53ec0ef8
10 changed files with 114 additions and 36 deletions

View File

@@ -38,7 +38,7 @@ namespace snmalloc
static AllocPool* make() noexcept
{
return make(default_memory_provider);
return make(default_memory_provider());
}
Alloc* acquire()

View File

@@ -58,8 +58,39 @@ namespace snmalloc
class MemoryProviderStateMixin : public PAL
{
std::atomic_flag lock = ATOMIC_FLAG_INIT;
void* bump;
size_t remaining;
void* bump = 0;
size_t remaining = 0;
public:
/**
* Stack of large allocations that have been returned for reuse.
*/
ModArray<NUM_LARGE_CLASSES, MPMCStack<Largeslab, RequiresInit>> large_stack;
static MemoryProviderStateMixin<PAL>* make() noexcept
{
// Temporary storage to start the allocator in.
MemoryProviderStateMixin<PAL> local;
// Allocate permanent storage for the allocator usung temporary allocator
MemoryProviderStateMixin<PAL>* allocated
= local.alloc_chunk<MemoryProviderStateMixin<PAL>, 1>();
// Put temporary allocator we have used, into the permanent storage.
// memcpy is safe as this is entirely single threaded.
memcpy(allocated, &local, sizeof(MemoryProviderStateMixin<PAL>));
return allocated;
}
private:
/**
* The last time we saw a low memory notification.
*/
std::atomic<uint64_t> last_low_memory_epoch = 0;
std::atomic_flag lazy_decommit_guard = {};
void new_block()
{
@@ -71,11 +102,6 @@ namespace snmalloc
remaining = SUPERSLAB_SIZE;
}
/**
* The last time we saw a low memory notification.
*/
std::atomic<uint64_t> last_low_memory_epoch = 0;
std::atomic_flag lazy_decommit_guard;
SNMALLOC_SLOW_PATH void lazy_decommit()
{
// If another thread is try to do lazy decommit, let it continue. If
@@ -141,11 +167,6 @@ namespace snmalloc
}
public:
/**
* Stack of large allocations that have been returned for reuse.
*/
ModArray<NUM_LARGE_CLASSES, MPMCStack<Largeslab, PreZeroed>> large_stack;
/**
* Primitive allocator for structure that are required before
* the allocator can be running.
@@ -422,5 +443,8 @@ namespace snmalloc
* The memory provider that will be used if no other provider is explicitly
* passed as an argument.
*/
inline GlobalVirtual default_memory_provider;
inline GlobalVirtual& default_memory_provider()
{
return *(Singleton<GlobalVirtual*, GlobalVirtual::make>::get());
}
} // namespace snmalloc

View File

@@ -153,7 +153,7 @@ namespace snmalloc
if (e->compare_exchange_strong(
value, LOCKED_ENTRY, std::memory_order_relaxed))
{
auto& v = default_memory_provider;
auto& v = default_memory_provider();
value = v.alloc_chunk<PagemapEntry, OS_PAGE_SIZE>();
e->store(value, std::memory_order_release);
}

View File

@@ -41,7 +41,7 @@ namespace snmalloc
static Pool* make() noexcept
{
return Pool::make(default_memory_provider);
return Pool::make(default_memory_provider());
}
template<typename... Args>

View File

@@ -11,16 +11,6 @@ extern "C" void _malloc_thread_cleanup();
namespace snmalloc
{
/**
* A global fake allocator object. This never allocates memory and, as a
* result, never owns any slabs. On the slow paths, where it would fetch
* slabs to allocate from, it will discover that it is the placeholder and
* replace itself with the thread-local allocator, allocating one if
* required. This avoids a branch on the fast path.
*/
inline Alloc GlobalPlaceHolder(
default_memory_provider, SNMALLOC_DEFAULT_CHUNKMAP(), nullptr, true);
#ifdef SNMALLOC_EXTERNAL_THREAD_ALLOC
/**
* Version of the `ThreadAlloc` interface that does no management of thread
@@ -39,15 +29,39 @@ namespace snmalloc
public:
static SNMALLOC_FAST_PATH Alloc* get_noncachable()
{
return (Alloc*&)ThreadAllocUntyped::get();
return (Alloc*)ThreadAllocUntyped::get();
}
static SNMALLOC_FAST_PATH Alloc* get()
{
return (Alloc*&)ThreadAllocUntyped::get();
return (Alloc*)ThreadAllocUntyped::get();
}
};
#endif
/**
* Function passed as a template parameter to `Allocator` to allow lazy
* replacement. In this case we are assuming the underlying external thread
* alloc is performing initialization, so this is not required, and just
* always returns nullptr to specify no new allocator is required.
*/
SNMALLOC_FAST_PATH void* lazy_replacement(void* existing)
{
UNUSED(existing);
return nullptr;
}
using ThreadAlloc = ThreadAllocUntypedWrapper;
#else
/**
* A global fake allocator object. This never allocates memory and, as a
* result, never owns any slabs. On the slow paths, where it would fetch
* slabs to allocate from, it will discover that it is the placeholder and
* replace itself with the thread-local allocator, allocating one if
* required. This avoids a branch on the fast path.
*/
inline GlobalVirtual dummy_memory_provider;
inline Alloc GlobalPlaceHolder(
dummy_memory_provider, SNMALLOC_DEFAULT_CHUNKMAP(), nullptr, true);
/**
* Common aspects of thread local allocator. Subclasses handle how releasing
@@ -196,8 +210,6 @@ namespace snmalloc
ThreadAllocLibcCleanup::inner_release();
}
using ThreadAlloc = ThreadAllocLibcCleanup;
#elif defined(SNMALLOC_EXTERNAL_THREAD_ALLOC)
using ThreadAlloc = ThreadAllocUntypedWrapper;
#else
using ThreadAlloc = ThreadAllocThreadDestructor;
#endif
@@ -233,5 +245,5 @@ namespace snmalloc
}
return lazy_replacement_slow();
}
#endif
} // namespace snmalloc