* Factor out explicit Config type Instead of using snmalloc::Alloc::Config, expose snmalloc::Config, which is then used to derive the allocator type. * Move globalalloc to front end. * Remove unneed template parameter from global snmalloc functions. * Remove SNMALLOC_PASS_THROUGH VeronaRT now has an abstraction layer which can easily replace the allocator. Having such a complex integration still in snmalloc does not make sense. * Take some global functions off of local alloc. * Drop comparison overloads on atomic Capptr. Performing a comparison on two atomic ptr is a complex operation, and should not be implicit. The memory model order and such things needs to be considered by the caller. * Remove function_ref and use templates The implementation prefers to use templates over the function_ref. This now only exists in the Pal for a currently unused feature. * Removing function_ref reduces stl needs. * Remove use of __is_convertible to support older g++ * Inline function that is only used once. * Remove unused function * Restrict ThreadAlloc usage to globalalloc This commit introduces various inline functions on snmalloc:: that perform allocation/deallocation using the thread local allocator. They remove all usage from a particular test. * Move cheri checks to own file. * Refactor is_owned checks. * Move alloc_size and check_size to globalalloc. * Minor simplification of dealloc path * Fix up is_owned to take a config * Improve usage of scoped allocator. * Handle Config_ in globalalloc.
201 lines
4.4 KiB
C++
201 lines
4.4 KiB
C++
/**
|
|
* This file demonstrates how the snmalloc library could be implemented to
|
|
* provide a miracle pointer like feature. This is not a hardened
|
|
* implementation and is purely for illustrative purposes.
|
|
*
|
|
* Do not use as is.
|
|
*/
|
|
|
|
#ifdef SNMALLOC_THREAD_SANITIZER_ENABLED
|
|
int main()
|
|
{
|
|
return 0;
|
|
}
|
|
#else
|
|
|
|
# include "test/setup.h"
|
|
|
|
# include <atomic>
|
|
# include <iostream>
|
|
# include <memory>
|
|
# include <snmalloc/backend/globalconfig.h>
|
|
# include <snmalloc/snmalloc_core.h>
|
|
|
|
namespace snmalloc
|
|
{
|
|
// Instantiate the allocator with a client meta data provider that uses an
|
|
// atomic size_t to store the reference count.
|
|
using Config = snmalloc::StandardConfigClientMeta<
|
|
ArrayClientMetaDataProvider<std::atomic<size_t>>>;
|
|
}
|
|
|
|
# define SNMALLOC_PROVIDE_OWN_CONFIG
|
|
# include <snmalloc/snmalloc.h>
|
|
|
|
SNMALLOC_SLOW_PATH void error(std::string msg)
|
|
{
|
|
std::cout << msg << std::endl;
|
|
abort();
|
|
}
|
|
|
|
SNMALLOC_FAST_PATH_INLINE void check(bool b, std::string msg)
|
|
{
|
|
if (SNMALLOC_UNLIKELY(!b))
|
|
error(msg);
|
|
}
|
|
|
|
namespace snmalloc::miracle
|
|
{
|
|
// snmalloc meta-data representation
|
|
// * 2n + 1: Represents an object that has not been deallocated with n
|
|
// additional references to it
|
|
// * 2n : Represents a deallocated object that
|
|
// has n additional references to it
|
|
|
|
inline void* malloc(size_t size)
|
|
{
|
|
auto p = snmalloc::libc::malloc(size);
|
|
if (SNMALLOC_UNLIKELY(p == nullptr))
|
|
return nullptr;
|
|
|
|
snmalloc::get_client_meta_data(p) = 1;
|
|
return p;
|
|
}
|
|
|
|
inline void free(void* ptr)
|
|
{
|
|
if (ptr == nullptr)
|
|
return;
|
|
|
|
// TODO could build a check into this that it is the start of the object?
|
|
auto previous = snmalloc::get_client_meta_data(ptr).fetch_add((size_t)-1);
|
|
|
|
if (SNMALLOC_LIKELY(previous == 1))
|
|
{
|
|
std::cout << "Freeing " << ptr << std::endl;
|
|
snmalloc::libc::free(ptr);
|
|
return;
|
|
}
|
|
|
|
check((previous & 1) == 1, "Double free detected");
|
|
|
|
// We have additional references to this object.
|
|
// We should not free it.
|
|
// TOOD this assumes this is not an internal pointer.
|
|
memset(ptr, 0, snmalloc::libc::malloc_usable_size(ptr));
|
|
}
|
|
|
|
inline void acquire(void* p)
|
|
{
|
|
auto previous = snmalloc::get_client_meta_data(p).fetch_add((size_t)2);
|
|
|
|
// Can we take new pointers to a deallocated object?
|
|
check((previous & 1) == 1, "Acquiring a deallocated object");
|
|
}
|
|
|
|
inline void release(void* p)
|
|
{
|
|
auto previous = snmalloc::get_client_meta_data(p).fetch_add((size_t)-2);
|
|
|
|
if (previous > 2)
|
|
return;
|
|
|
|
check(previous == 2, "Releasing an object with insufficient references");
|
|
|
|
std::cout << "Freeing from release " << p << std::endl;
|
|
snmalloc::libc::free(p);
|
|
}
|
|
|
|
/**
|
|
* This class can be used to replace a raw pointer. It will automatically use
|
|
* the underlying backup reference counting design from the miracle pointer
|
|
* docs.
|
|
*/
|
|
template<typename T>
|
|
class raw_ptr
|
|
{
|
|
T* p;
|
|
|
|
public:
|
|
raw_ptr() : p(nullptr) {}
|
|
|
|
raw_ptr(T* p) : p(p)
|
|
{
|
|
snmalloc::miracle::acquire(p);
|
|
}
|
|
|
|
T& operator*()
|
|
{
|
|
return *p;
|
|
}
|
|
|
|
~raw_ptr()
|
|
{
|
|
if (p == nullptr)
|
|
return;
|
|
snmalloc::miracle::release(p);
|
|
}
|
|
|
|
raw_ptr(const raw_ptr& rp) : p(rp.p)
|
|
{
|
|
snmalloc::miracle::acquire(p);
|
|
}
|
|
|
|
raw_ptr& operator=(const raw_ptr& other)
|
|
{
|
|
p = other.p;
|
|
snmalloc::miracle::acquire(other.p);
|
|
return *this;
|
|
}
|
|
|
|
raw_ptr(raw_ptr&& other) : p(other.p)
|
|
{
|
|
other.p = nullptr;
|
|
}
|
|
|
|
raw_ptr& operator=(raw_ptr&& other)
|
|
{
|
|
p = other.p;
|
|
other.p = nullptr;
|
|
return *this;
|
|
}
|
|
};
|
|
} // namespace snmalloc::miracle
|
|
|
|
/**
|
|
* Overload new and delete to use the "miracle pointer" implementation.
|
|
*/
|
|
void* operator new(size_t size)
|
|
{
|
|
return snmalloc::miracle::malloc(size);
|
|
}
|
|
|
|
void operator delete(void* p)
|
|
{
|
|
snmalloc::miracle::free(p);
|
|
}
|
|
|
|
void operator delete(void* p, size_t)
|
|
{
|
|
snmalloc::miracle::free(p);
|
|
}
|
|
|
|
int main()
|
|
{
|
|
snmalloc::miracle::raw_ptr<int> p;
|
|
{
|
|
auto up1 = std::make_unique<int>(41);
|
|
auto up = std::make_unique<int>(42);
|
|
auto up2 = std::make_unique<int>(40);
|
|
auto up3 = std::make_unique<int>(39);
|
|
p = up.get();
|
|
check(*p == 42, "Failed to set p");
|
|
}
|
|
// Still safe to access here. The unique_ptr has been destroyed, but the
|
|
// raw_ptr has kept the memory live.
|
|
// Current implementation zeros the memory when the unique_ptr is destroyed.
|
|
check(*p == 0, "Failed to keep memory live");
|
|
return 0;
|
|
}
|
|
#endif
|