nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: PublicHeader: OVR_Kernel.h nuclear@0: Filename : OVR_Allocator.h nuclear@0: Content : Installable memory allocator nuclear@0: Created : September 19, 2012 nuclear@0: Notes : nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #ifndef OVR_Allocator_h nuclear@0: #define OVR_Allocator_h nuclear@0: nuclear@0: #include "OVR_Types.h" nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: nuclear@0: // ***** Disable template-unfriendly MS VC++ warnings nuclear@0: #if defined(OVR_CC_MSVC) nuclear@0: // Pragma to prevent long name warnings in in VC++ nuclear@0: #pragma warning(disable : 4503) nuclear@0: #pragma warning(disable : 4786) nuclear@0: // In MSVC 7.1, warning about placement new POD default initializer nuclear@0: #pragma warning(disable : 4345) nuclear@0: #endif nuclear@0: nuclear@0: // Un-define new so that placement constructors work nuclear@0: #undef new nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** Placement new overrides nuclear@0: nuclear@0: // Calls constructor on own memory created with "new(ptr) type" nuclear@0: #ifndef __PLACEMENT_NEW_INLINE nuclear@0: #define __PLACEMENT_NEW_INLINE nuclear@0: nuclear@0: # if defined(OVR_CC_MWERKS) || defined(OVR_CC_BORLAND) || defined(OVR_CC_GNU) nuclear@0: # include nuclear@0: # else nuclear@0: // Useful on MSVC nuclear@0: OVR_FORCE_INLINE void* operator new (size_t n, void *ptr) { OVR_UNUSED(n); return ptr; } nuclear@0: OVR_FORCE_INLINE void operator delete (void *, void *) { } nuclear@0: # endif nuclear@0: nuclear@0: #endif // __PLACEMENT_NEW_INLINE nuclear@0: nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------ nuclear@0: // ***** Macros to redefine class new/delete operators nuclear@0: nuclear@0: // Types specifically declared to allow disambiguation of address in nuclear@0: // class member operator new. nuclear@0: nuclear@0: #define OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, check_delete) \ nuclear@0: void* operator new(size_t sz) \ nuclear@0: { void *p = OVR_ALLOC_DEBUG(sz, __FILE__, __LINE__); return p; } \ nuclear@0: void* operator new(size_t sz, const char* file, int line) \ nuclear@0: { void* p = OVR_ALLOC_DEBUG(sz, file, line); OVR_UNUSED2(file, line); return p; } \ nuclear@0: void operator delete(void *p) \ nuclear@0: { check_delete(class_name, p); OVR_FREE(p); } \ nuclear@0: void operator delete(void *p, const char*, int) \ nuclear@0: { check_delete(class_name, p); OVR_FREE(p); } nuclear@0: nuclear@0: #define OVR_MEMORY_DEFINE_PLACEMENT_NEW \ nuclear@0: void* operator new (size_t n, void *ptr) { OVR_UNUSED(n); return ptr; } \ nuclear@0: void operator delete (void *ptr, void *ptr2) { OVR_UNUSED2(ptr,ptr2); } nuclear@0: nuclear@0: nuclear@0: #define OVR_MEMORY_CHECK_DELETE_NONE(class_name, p) nuclear@0: nuclear@0: // Redefined all delete/new operators in a class without custom memory initialization nuclear@0: #define OVR_MEMORY_REDEFINE_NEW(class_name) \ nuclear@0: OVR_MEMORY_REDEFINE_NEW_IMPL(class_name, OVR_MEMORY_CHECK_DELETE_NONE) nuclear@0: nuclear@0: nuclear@0: namespace OVR { nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** Construct / Destruct nuclear@0: nuclear@0: // Construct/Destruct functions are useful when new is redefined, as they can nuclear@0: // be called instead of placement new constructors. nuclear@0: nuclear@0: nuclear@0: template nuclear@0: OVR_FORCE_INLINE T* Construct(void *p) nuclear@0: { nuclear@0: return ::new(p) T(); nuclear@0: } nuclear@0: nuclear@0: template nuclear@0: OVR_FORCE_INLINE T* Construct(void *p, const T& source) nuclear@0: { nuclear@0: return ::new(p) T(source); nuclear@0: } nuclear@0: nuclear@0: // Same as above, but allows for a different type of constructor. nuclear@0: template nuclear@0: OVR_FORCE_INLINE T* ConstructAlt(void *p, const S& source) nuclear@0: { nuclear@0: return ::new(p) T(source); nuclear@0: } nuclear@0: nuclear@0: template nuclear@0: OVR_FORCE_INLINE T* ConstructAlt(void *p, const S1& src1, const S2& src2) nuclear@0: { nuclear@0: return ::new(p) T(src1, src2); nuclear@0: } nuclear@0: nuclear@0: template nuclear@0: OVR_FORCE_INLINE void ConstructArray(void *p, size_t count) nuclear@0: { nuclear@0: uint8_t *pdata = (uint8_t*)p; nuclear@0: for (size_t i=0; i< count; ++i, pdata += sizeof(T)) nuclear@0: { nuclear@0: Construct(pdata); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: template nuclear@0: OVR_FORCE_INLINE void ConstructArray(void *p, size_t count, const T& source) nuclear@0: { nuclear@0: uint8_t *pdata = (uint8_t*)p; nuclear@0: for (size_t i=0; i< count; ++i, pdata += sizeof(T)) nuclear@0: { nuclear@0: Construct(pdata, source); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: template nuclear@0: OVR_FORCE_INLINE void Destruct(T *pobj) nuclear@0: { nuclear@0: pobj->~T(); nuclear@0: OVR_UNUSED1(pobj); // Fix incorrect 'unused variable' MSVC warning. nuclear@0: } nuclear@0: nuclear@0: template nuclear@0: OVR_FORCE_INLINE void DestructArray(T *pobj, size_t count) nuclear@0: { nuclear@0: for (size_t i=0; i~T(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //----------------------------------------------------------------------------------- nuclear@0: // ***** Allocator nuclear@0: nuclear@0: // Allocator defines a memory allocation interface that developers can override nuclear@0: // to to provide memory for OVR; an instance of this class is typically created on nuclear@0: // application startup and passed into System or OVR::System constructor. nuclear@0: // nuclear@0: // nuclear@0: // Users implementing this interface must provide three functions: Alloc, Free, nuclear@0: // and Realloc. Implementations of these functions must honor the requested alignment. nuclear@0: // Although arbitrary alignment requests are possible, requested alignment will nuclear@0: // typically be small, such as 16 bytes or less. nuclear@0: nuclear@0: class Allocator nuclear@0: { nuclear@0: friend class System; nuclear@0: public: nuclear@0: virtual ~Allocator(){} nuclear@0: nuclear@0: // *** Standard Alignment Alloc/Free nuclear@0: nuclear@0: // Allocate memory of specified size with default alignment. nuclear@0: // Alloc of size==0 will allocate a tiny block & return a valid pointer; nuclear@0: // this makes it suitable for new operator. nuclear@0: virtual void* Alloc(size_t size) = 0; nuclear@0: // Same as Alloc, but provides an option of passing debug data. nuclear@0: virtual void* AllocDebug(size_t size, const char* file, unsigned line) nuclear@0: { OVR_UNUSED2(file, line); return Alloc(size); } nuclear@0: nuclear@0: // Reallocate memory block to a new size, copying data if necessary. Returns the pointer to nuclear@0: // new memory block, which may be the same as original pointer. Will return 0 if reallocation nuclear@0: // failed, in which case previous memory is still valid. nuclear@0: // Realloc to decrease size will never fail. nuclear@0: // Realloc of pointer == 0 is equivalent to Alloc nuclear@0: // Realloc to size == 0, shrinks to the minimal size, pointer remains valid and requires Free(). nuclear@0: virtual void* Realloc(void* p, size_t newSize) = 0; nuclear@0: nuclear@0: // Frees memory allocated by Alloc/Realloc. nuclear@0: // Free of null pointer is valid and will do nothing. nuclear@0: virtual void Free(void *p) = 0; nuclear@0: nuclear@0: nuclear@0: // *** Standard Alignment Alloc/Free nuclear@0: nuclear@0: // Allocate memory of specified alignment. nuclear@0: // Memory allocated with AllocAligned MUST be freed with FreeAligned. nuclear@0: // Default implementation will delegate to Alloc/Free after doing rounding. nuclear@0: virtual void* AllocAligned(size_t size, size_t align); nuclear@0: // Frees memory allocated with AllocAligned. nuclear@0: virtual void FreeAligned(void* p); nuclear@0: nuclear@0: // Returns the pointer to the current globally installed Allocator instance. nuclear@0: // This pointer is used for most of the memory allocations. nuclear@0: static Allocator* GetInstance() { return pInstance; } nuclear@0: nuclear@0: nuclear@0: protected: nuclear@0: // onSystemShutdown is called on the allocator during System::Shutdown. nuclear@0: // At this point, all allocations should've been freed. nuclear@0: virtual void onSystemShutdown() { } nuclear@0: nuclear@0: public: nuclear@0: static void setInstance(Allocator* palloc) nuclear@0: { nuclear@0: OVR_ASSERT((pInstance == 0) || (palloc == 0)); nuclear@0: pInstance = palloc; nuclear@0: } nuclear@0: nuclear@0: private: nuclear@0: nuclear@0: static Allocator* pInstance; nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------ nuclear@0: // ***** Allocator_SingletonSupport nuclear@0: nuclear@0: // Allocator_SingletonSupport is a Allocator wrapper class that implements nuclear@0: // the InitSystemSingleton static function, used to create a global singleton nuclear@0: // used for the OVR::System default argument initialization. nuclear@0: // nuclear@0: // End users implementing custom Allocator interface don't need to make use of this base nuclear@0: // class; they can just create an instance of their own class on stack and pass it to System. nuclear@0: nuclear@0: template nuclear@0: class Allocator_SingletonSupport : public Allocator nuclear@0: { nuclear@0: struct AllocContainer nuclear@0: { nuclear@0: size_t Data[(sizeof(D) + sizeof(size_t)-1) / sizeof(size_t)]; nuclear@0: bool Initialized; nuclear@0: AllocContainer() : Initialized(0) { } nuclear@0: }; nuclear@0: nuclear@0: AllocContainer* pContainer; nuclear@0: nuclear@0: public: nuclear@0: Allocator_SingletonSupport() : pContainer(0) { } nuclear@0: nuclear@0: // Creates a singleton instance of this Allocator class used nuclear@0: // on OVR_DEFAULT_ALLOCATOR during System initialization. nuclear@0: static D* InitSystemSingleton() nuclear@0: { nuclear@0: static AllocContainer Container; nuclear@0: OVR_ASSERT(Container.Initialized == false); nuclear@0: nuclear@0: Allocator_SingletonSupport *presult = Construct((void*)Container.Data); nuclear@0: presult->pContainer = &Container; nuclear@0: Container.Initialized = true; nuclear@0: return (D*)presult; nuclear@0: } nuclear@0: nuclear@0: protected: nuclear@0: virtual void onSystemShutdown() nuclear@0: { nuclear@0: Allocator::onSystemShutdown(); nuclear@0: if (pContainer) nuclear@0: { nuclear@0: pContainer->Initialized = false; nuclear@0: Destruct((D*)this); nuclear@0: pContainer = 0; nuclear@0: } nuclear@0: } nuclear@0: }; nuclear@0: nuclear@0: //------------------------------------------------------------------------ nuclear@0: // ***** Default Allocator nuclear@0: nuclear@0: // This allocator is created and used if no other allocator is installed. nuclear@0: // Default allocator delegates to system malloc. nuclear@0: nuclear@0: class DefaultAllocator : public Allocator_SingletonSupport nuclear@0: { nuclear@0: public: nuclear@0: virtual void* Alloc(size_t size); nuclear@0: virtual void* AllocDebug(size_t size, const char* file, unsigned line); nuclear@0: virtual void* Realloc(void* p, size_t newSize); nuclear@0: virtual void Free(void *p); nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------ nuclear@0: // ***** Memory Allocation Macros nuclear@0: nuclear@0: // These macros should be used for global allocation. In the future, these nuclear@0: // macros will allows allocation to be extended with debug file/line information nuclear@0: // if necessary. nuclear@0: nuclear@0: #define OVR_REALLOC(p,s) OVR::Allocator::GetInstance()->Realloc((p),(s)) nuclear@0: #define OVR_FREE(p) OVR::Allocator::GetInstance()->Free((p)) nuclear@0: #define OVR_ALLOC_ALIGNED(s,a) OVR::Allocator::GetInstance()->AllocAligned((s),(a)) nuclear@0: #define OVR_FREE_ALIGNED(p) OVR::Allocator::GetInstance()->FreeAligned((p)) nuclear@0: nuclear@0: #ifdef OVR_BUILD_DEBUG nuclear@0: #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->AllocDebug((s), __FILE__, __LINE__) nuclear@0: #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->AllocDebug((s), f, l) nuclear@0: #else nuclear@0: #define OVR_ALLOC(s) OVR::Allocator::GetInstance()->Alloc((s)) nuclear@0: #define OVR_ALLOC_DEBUG(s,f,l) OVR::Allocator::GetInstance()->Alloc((s)) nuclear@0: #endif nuclear@0: nuclear@0: //------------------------------------------------------------------------ nuclear@0: nuclear@0: // Base class that overrides the new and delete operators. nuclear@0: // Deriving from this class, even as a multiple base, incurs no space overhead. nuclear@0: class NewOverrideBase nuclear@0: { nuclear@0: public: nuclear@0: nuclear@0: // Redefine all new & delete operators. nuclear@0: OVR_MEMORY_REDEFINE_NEW(NewOverrideBase) nuclear@0: }; nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------ nuclear@0: // ***** Mapped memory allocation nuclear@0: // nuclear@0: // Equates to VirtualAlloc/VirtualFree on Windows, mmap/munmap on Unix. nuclear@0: // These are useful for when you need system-supplied memory pages. nuclear@0: // These are also useful for when you need to allocate memory in a way nuclear@0: // that doesn't affect the application heap. nuclear@0: nuclear@0: void* MMapAlloc(size_t size); nuclear@0: void MMapFree(void* memory, size_t size); nuclear@0: nuclear@0: nuclear@0: } // OVR nuclear@0: nuclear@0: nuclear@0: // Redefine operator 'new' if necessary. nuclear@0: #if defined(OVR_DEFINE_NEW) nuclear@0: #define new OVR_DEFINE_NEW nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: #endif // OVR_Memory