Panagiotis Christopoulos Charitos 13 роки тому
батько
коміт
ea147d628b

+ 247 - 2
include/anki/util/Allocator.h

@@ -8,6 +8,7 @@
 #include <atomic>
 
 #define ANKI_DEBUG_ALLOCATORS ANKI_DEBUG
+#define ANKI_PRINT_ALLOCATOR_MESSAGES 0
 
 namespace anki {
 
@@ -87,14 +88,14 @@ public:
 	pointer allocate(size_type n, const void* = 0)
 	{
 		size_type size = n * sizeof(value_type);
-		return (pointer)AllocatorStatic::malloc(size);
+		return (pointer)detail::AllocatorStatic::malloc(size);
 	}
 
 	/// Deallocate memory
 	void deallocate(void* p, size_type n)
 	{
 		size_type size = n * sizeof(T);
-		AllocatorStatic::free(p, size);
+		detail::AllocatorStatic::free(p, size);
 	}
 
 	/// Call constructor
@@ -151,6 +152,250 @@ inline bool operator==(const Allocator<T1>&, const AnotherAllocator&)
 	return false;
 }
 
+namespace detail {
+
+/// Thread safe memory pool
+struct MemoryPool
+{
+	/// Allocated memory
+	U8* memory = nullptr;
+	/// Size of the allocated memory
+	PtrSize size = 0;
+	/// Points to the memory and more specifically to the address of the next
+	/// allocation
+	std::atomic<U8*> ptr = {nullptr};
+	/// Reference counter
+	std::atomic<I32> refCounter = {1};
+};
+
+/// Internal members for the StackAllocator. They are separate because we don't
+/// want to polute the StackAllocator template with specialized functions that
+/// take space
+class StackAllocatorInternal
+{
+protected:
+	/// The memory pool
+	detail::MemoryPool* mpool = nullptr;
+
+	/// Init the memory pool with the given size
+	void init(const PtrSize size);
+
+	/// Deinit the memory pool
+	void deinit();
+};
+
+} // end namespace detail
+
+/// Stack based allocator
+///
+/// @tparam T The type
+/// @tparam deallocationFlag If true then the allocator will try to deallocate
+///                          the memory. This is extremely important to
+///                          understand when it should be true. See notes
+/// @tparam alignmentBits Set the alighment in bits
+///
+/// @note The deallocationFlag can brake the allocator when the deallocations
+///       are not in the correct order. For example when deallocationFlag==true
+///       and the allocator is used in vector it is likely to fail
+///
+/// @note Don't ever EVER remove the double copy constructor and the double
+///       operator=. The compiler will create defaults
+template<typename T, Bool deallocationFlag = false, U32 alignmentBits = 16>
+class StackAllocator: public detail::StackAllocatorInternal
+{
+	template<typename U, Bool deallocationFlag_, U32 alignmentBits_>
+	friend class StackAllocator;
+
+public:
+	// Typedefs
+	typedef size_t size_type;
+	typedef ptrdiff_t difference_type;
+	typedef T* pointer;
+	typedef const T* const_pointer;
+	typedef T& reference;
+	typedef const T& const_reference;
+	typedef T value_type;
+
+	/// Default constructor deleted
+	StackAllocator() = delete;
+	/// Copy constructor
+	StackAllocator(const StackAllocator& b) throw()
+	{
+		*this = b;
+	}
+	/// Copy constructor
+	template<typename U>
+	StackAllocator(
+		const StackAllocator<U, deallocationFlag, alignmentBits>& b) throw()
+	{
+		*this = b;
+	}
+	/// Constuctor with size
+	StackAllocator(size_type size) throw()
+	{
+		init(size);
+	}
+
+	/// Destructor
+	~StackAllocator()
+	{
+		deinit();
+	}
+
+	/// Copy
+	StackAllocator& operator=(const StackAllocator& b)
+	{
+		mpool = b.mpool;
+		// Retain the mpool
+		++mpool->refCounter;
+		return *this;
+	}
+	/// Copy
+	template<typename U>
+	StackAllocator& operator=(
+		const StackAllocator<U, deallocationFlag, alignmentBits>& b)
+	{
+		mpool = b.mpool;
+		// Retain the mpool
+		++mpool->refCounter;
+		return *this;
+	}
+
+	/// Get the address of a reference
+	pointer address(reference x) const
+	{
+		return &x;
+	}
+	/// Get the const address of a const reference
+	const_pointer address(const_reference x) const
+	{
+		return &x;
+	}
+
+	/// Allocate memory
+	pointer allocate(size_type n, const void* hint = 0)
+	{
+		(void)hint;
+		size_type size = n * sizeof(value_type);
+		size_type alignedSize = calcAlignSize(size);
+
+		U8* out = mpool->ptr.fetch_add(alignedSize);
+
+#if ANKI_PRINT_ALLOCATOR_MESSAGES
+			std::cout << "Allocating: size: " << size
+				<< ", size after alignment: " << alignedSize
+				<< ", returned address: " << out
+				<< ", hint: " << hint <<std::endl;
+#endif
+
+		if(out + alignedSize <= mpool->memory + mpool->size)
+		{
+			// Everything ok
+		}
+		else
+		{
+			throw ANKI_EXCEPTION("Allocation failed. There is not enough room");
+		}
+
+		return (pointer)out;
+	}
+
+	/// Deallocate memory
+	void deallocate(void* p, size_type n)
+	{
+		(void)p;
+		(void)n;
+
+		if(deallocationFlag)
+		{
+			size_type alignedSize = calcAlignSize(n * sizeof(value_type));
+#if ANKI_PRINT_ALLOCATOR_MESSAGES
+			std::cout << "Deallocating: size: " << (n * sizeof(value_type))
+				<< " alignedSize: " << alignedSize
+				<< " pointer: " << p << std::endl;
+#endif
+			U8* headPtr = mpool->ptr.fetch_sub(alignedSize);
+
+			if(headPtr - alignedSize != p)
+			{
+				throw ANKI_EXCEPTION("Freeing wrong pointer. "
+					"The deallocations on StackAllocator should be in order");
+			}
+
+			ANKI_ASSERT((headPtr - alignedSize) >= mpool->memory);
+		}
+	}
+
+	/// Call constructor
+	void construct(pointer p, const T& val)
+	{
+		// Placement new
+		new ((T*)p) T(val);
+	}
+	/// Call constructor with many arguments
+	template<typename U, typename... Args>
+	void construct(U* p, Args&&... args)
+	{
+		// Placement new
+		::new((void *)p) U(std::forward<Args>(args)...);
+	}
+
+	/// Call destructor
+	void destroy(pointer p)
+	{
+		p->~T();
+	}
+	/// Call destructor
+	template<typename U>
+	void destroy(U* p)
+	{
+		p->~U();
+	}
+
+	/// Get the max allocation size
+	size_type max_size() const
+	{
+		return mpool->size;
+	}
+
+	template<typename U>
+	struct rebind
+	{
+		typedef StackAllocator<U, deallocationFlag, alignmentBits> other;
+	};
+
+	void reset()
+	{
+		mpool->ptr = mpool->memory;
+	}
+
+private:
+	/// Calculate tha align size
+	size_type calcAlignSize(size_type size)
+	{
+		return size + (size % (alignmentBits / 8));
+	}
+};
+
+/// Another allocator of the same type can deallocate from this one
+template<typename T1, typename T2, Bool deallocationFlag, U32 alignmentBits>
+inline bool operator==(
+	const StackAllocator<T1, deallocationFlag, alignmentBits>&,
+	const StackAllocator<T2, deallocationFlag, alignmentBits>&)
+{
+	return true;
+}
+
+/// Another allocator of the another type cannot deallocate from this one
+template<typename T1, typename AnotherAllocator, Bool deallocationFlag,
+	U32 alignmentBits>
+inline bool operator==(
+	const StackAllocator<T1, deallocationFlag, alignmentBits>&,
+	const AnotherAllocator&)
+{
+	return false;
+}
+
 } // end namespace anki
 
 #endif

+ 101 - 0
include/anki/util/Memory.h

@@ -0,0 +1,101 @@
+#ifndef ANKI_UTIL_MEMORY_H
+#define ANKI_UTIL_MEMORY_H
+
+#include "anki/util/Allocator.h"
+
+namespace anki {
+
+/// Function that imitates the new operator. The function allocates memory for
+/// a number of elements and calls their constructor. The interesting thing is
+/// that if the elements size is >1 then it allocates size bigger than the
+/// required. The extra chunk is a number that will be used in
+/// deleteObjectArray to identify the number of elements that were allocated
+template<typename T, typename Alloc, typename... Args>
+T* newObject(const Alloc& allocator_, const PtrSize n, Args&&... args)
+{
+	ANKI_ASSERT(n != 0);
+	T* out;
+	typename Alloc::template rebind<T>::other allocator(allocator_);
+
+	// If the number of elements is then do a simple allocaton
+	if(n == 1)
+	{
+		out = allocator.allocate(n);
+	}
+	else
+	{
+		// Allocate a memory block that includes the array size
+		typedef typename Alloc::template rebind<U8>::other CharAlloc;
+		CharAlloc charAlloc(allocator);
+		U8* mem = charAlloc.allocate(sizeof(PtrSize) + n * sizeof(T));
+
+		// Set the size of the block
+		*(PtrSize*)mem = n;
+
+		// Set the output address
+		out = (T*)(mem + sizeof(PtrSize));
+	}
+
+	// Call the constuctors
+	for(PtrSize i = 0; i < n; i++)
+	{
+		allocator.construct(&out[i], std::forward<Args>(args)...);
+	}
+
+	// Return result
+	return out;
+}
+
+/// Function that imitates the delete operator
+template<typename T, typename Alloc>
+void deleteObject(const Alloc& allocator, T* p)
+{
+	ANKI_ASSERT(p);
+
+	// Make sure the type is defined
+	typedef U8 TypeMustBeComplete[sizeof(T) ? 1 : -1];
+	(void) sizeof(TypeMustBeComplete);
+
+	// Rebind allocator because the Alloc may be of another type
+	typename Alloc::template rebind<T>::other alloc(allocator);
+
+	// Call the destructor
+	alloc.destroy(p);
+
+	// Deallocate
+	alloc.deallocate(p, 1);
+}
+
+/// Function that imitates the delete[] operator
+template<typename T, typename Alloc>
+void deleteObjectArray(const Alloc& allocator, T* p)
+{
+	ANKI_ASSERT(p);
+
+	// Make sure the type is defined
+	typedef U8 TypeMustBeComplete[sizeof(T) ? 1 : -1];
+	(void) sizeof(TypeMustBeComplete);
+
+	// Rebind allocator
+	typename Alloc::template rebind<T>::other alloc(allocator);
+
+	// Get the allocated block
+	U8* block = (U8*)(p) - sizeof(PtrSize);
+
+	// Get number of elements
+	const PtrSize n = *(PtrSize*)block;
+
+	// Call the destructors
+	for(PtrSize i = 0; i < n; i++)
+	{
+		alloc.destroy(&p[i]);
+	}
+
+	// Deallocate the block
+	typename Alloc::template rebind<U8>::other allocc(allocator);
+	allocc.deallocate(block, n * sizeof(T) + sizeof(PtrSize));
+}
+
+} // end namespace anki
+
+#endif

+ 71 - 1
src/util/Allocator.cpp

@@ -3,6 +3,10 @@
 
 namespace anki {
 
+//==============================================================================
+// Allocator                                                                   =
+//==============================================================================
+
 namespace detail {
 
 //==============================================================================
@@ -23,7 +27,7 @@ void AllocatorStatic::dump()
 //==============================================================================
 void* AllocatorStatic::malloc(PtrSize size)
 {
-	void out = std::malloc(size);
+	void* out = std::malloc(size);
 
 	if(out != nullptr)
 	{
@@ -58,4 +62,70 @@ void AllocatorStatic::free(void* p, PtrSize size)
 
 } // end namespace detail
 
+//==============================================================================
+// StackAllocatorInternal                                                      =
+//==============================================================================
+
+namespace detail {
+
+//==============================================================================
+void StackAllocatorInternal::init(const PtrSize size)
+{
+	mpool = new detail::MemoryPool;
+
+	if(mpool != nullptr)
+	{
+		mpool->memory = (U8*)::malloc(size);
+
+		if(mpool->memory != nullptr)
+		{
+			mpool->size = size;
+			mpool->ptr = mpool->memory;
+			// Memory pool's refcounter is 1
+#if ANKI_PRINT_ALLOCATOR_MESSAGES
+		std::cout << "New MemoryPool created: Address: " << mpool
+			<< ", size: " << size
+			<< ", pool address: " << mpool->memory << std::endl;
+#endif
+			return;
+		}
+	}
+
+	// Errors happened
+
+	if(mpool->memory)
+	{
+		::free(mpool->memory);
+	}
+
+	if(mpool)
+	{
+		delete mpool;
+	}
+
+	std::cerr << "Stack allocator constuctor failed. I will cannot "
+		"throw but I have to exit" << std::endl;
+	exit(0);
+}
+
+//==============================================================================
+void StackAllocatorInternal::deinit()
+{
+	int refCounter = mpool->refCounter.fetch_sub(1);
+
+	if(mpool && (refCounter - 1) == 0)
+	{
+#if ANKI_PRINT_ALLOCATOR_MESSAGES
+		std::cout << "Deleting MemoryPool " << mpool << std::endl;
+#endif
+		// It is safe to delete the memory pool
+		ANKI_ASSERT(mpool->refCounter == 0);
+
+		::free(mpool->memory);
+		delete mpool;
+	}
+}
+
+} // end namespace detail
+
 } // end namespace anki

+ 1 - 1
src/util/CMakeLists.txt

@@ -1,4 +1,4 @@
-SET(ANKI_UTIL_SOURCES Assert.cpp BinaryStream.cpp Exception.cpp Functions.cpp Scanner.cpp StringList.cpp Filesystem.cpp)
+SET(ANKI_UTIL_SOURCES Assert.cpp BinaryStream.cpp Exception.cpp Functions.cpp Scanner.cpp StringList.cpp Filesystem.cpp Allocator.cpp)
 
 IF(ANKI_PLATFORM STREQUAL "LINUX")
 	SET(ANKI_UTIL_SOURCES ${ANKI_UTIL_SOURCES} HighRezTimerPosix.cpp FilesystemPosix.cpp)