Files
snmalloc/src/backend/commonconfig.h
David Chisnall 51e75bca89 Add memcpy with bounds checks.
The memcpy implementation is not completely stupid but is almost
certainly not as good as a carefully tuned and optimised one.

Building snmalloc with FreeBSD's libc memcpy + jemalloc and with this,
each 10 times, does not show a statistically significant performance
difference at 95% confidence.  The snmalloc version has very slightly
lower median and worst-case times.  This is in no way a sensible
benchmark, but it serves as a smoke test for significant performance
regressions.

The CI self-host job now uses the checked memcpy.

This also fixes an off-by-one error in the external bounds.  This is
triggered by ninja, so we will see breakage in CI if it is reintroduced.

In debug builds, we provide a verbose error containing the address of
the allocation, the base and bounds of the allocation, and a backtrace.

The backtrace was broken by the CI cleanup moving the BACKTRACE_HEADER
macro into the SNMALLOC_ namespace.  This is also fixed.

The test involves hijacking `abort`, which doesn't work everywhere.  It
also requires `backtrace` to work in configurations where stack traces
are enabled.  This is disabled in QEMU because `backtrace` appears to
crash reliably in QEMU user mode.

For now, in the -checks build configurations, we are hitting a slow path
in the pagemap on accesses so that the pages that are `PROT_NONE` don't
cause crashes.  These need to be made read-only, but this requires a PAL
change.
2021-09-16 13:53:13 +01:00

141 lines
4.9 KiB
C++

#pragma once
#include "../ds/defines.h"
#include "../mem/remotecache.h"
namespace snmalloc
{
// Forward reference to thread local cleanup.
void register_clean_up();
/**
* Options for a specific snmalloc configuration. Every globals object must
* have one `constexpr` instance of this class called `Options`. This should
* be constructed to explicitly override any of the defaults. A
* configuration that does not need to override anything would simply declare
* this as a field of the global object:
*
* ```c++
* constexpr static snmalloc::Flags Options{};
* ```
*
* A global configuration that wished to use out-of-line message queues but
* accept the defaults for everything else would instead do this:
*
* ```c++
* constexpr static snmalloc::Flags Options{.IsQueueInline = false};
* ```
*
* To maintain backwards source compatibility in future versions, any new
* option added here should have its default set to be whatever snmalloc was
* doing before the new option was added.
*/
struct Flags
{
/**
* Should allocators have inline message queues? If this is true then
* the `CoreAllocator` is responsible for allocating the
* `RemoteAllocator` that contains its message queue. If this is false
* then the `RemoteAllocator` must be separately allocated and provided
* to the `CoreAllocator` before it is used.
*
* Setting this to `false` currently requires also setting
* `LocalAllocSupportsLazyInit` to false so that the `CoreAllocator` can
* be provided to the `LocalAllocator` fully initialised but in the
* future it may be possible to allocate the `RemoteAllocator` via
* `alloc_meta_data` or a similar API in the back end.
*/
bool IsQueueInline = true;
/**
* Does the `CoreAllocator` own a `Backend::LocalState` object? If this is
* true then the `CoreAllocator` is responsible for allocating and
* deallocating a local state object, otherwise the surrounding code is
* responsible for creating it.
*
* Use cases that set this to false will probably also need to set
* `LocalAllocSupportsLazyInit` to false so that they can provide the local
* state explicitly during allocator creation.
*/
bool CoreAllocOwnsLocalState = true;
/**
* Are `CoreAllocator` allocated by the pool allocator? If not then the
* code embedding this snmalloc configuration is responsible for allocating
* `CoreAllocator` instances.
*
* Users setting this flag must also set `LocalAllocSupportsLazyInit` to
* false currently because there is no alternative mechanism for allocating
* core allocators. This may change in future versions.
*/
bool CoreAllocIsPoolAllocated = true;
/**
* Do `LocalAllocator` instances in this configuration support lazy
* initialisation? If so, then the first exit from a fast path will
* trigger allocation of a `CoreAllocator` and associated state. If not
* then the code embedding this configuration of snmalloc is responsible
* for allocating core allocators.
*/
bool LocalAllocSupportsLazyInit = true;
};
/**
* Class containing definitions that are likely to be used by all except for
* the most unusual back-end implementations. This can be subclassed as a
* convenience for back-end implementers, but is not required.
*/
class CommonConfig
{
public:
/**
* Special remote that should never be used as a real remote.
* This is used to initialise allocators that should always hit the
* remote path for deallocation. Hence moving a branch off the critical
* path.
*/
SNMALLOC_REQUIRE_CONSTINIT
inline static RemoteAllocator unused_remote;
/**
* Special remote that is used in meta-data for large allocations.
*
* nullptr is considered a large allocations for this purpose to move
* of the critical path.
*
* Bottom bits of the remote pointer are used for a sizeclass, we need
* size bits to represent the non-large sizeclasses, we can then get
* the large sizeclass by having the fake large_remote considerably
* more aligned.
*/
SNMALLOC_REQUIRE_CONSTINIT
inline static constexpr RemoteAllocator* fake_large_remote{nullptr};
static_assert(
&unused_remote != fake_large_remote,
"Compilation should ensure these are different");
};
/**
* SFINAE helper. Matched only if `T` implements `is_initialised`. Calls
* it if it exists.
*/
template<typename T>
SNMALLOC_FAST_PATH auto call_is_initialised(T*, int)
-> decltype(T::is_initialised())
{
return T::is_initialised();
}
/**
* SFINAE helper. Matched only if `T` does not implement `is_initialised`.
* Unconditionally returns true if invoked.
*/
template<typename T>
SNMALLOC_FAST_PATH auto call_is_initialised(T*, long)
{
return true;
}
} // namespace snmalloc