diff options
Diffstat (limited to 'src/utilcode/memorypool.cpp')
-rw-r--r-- | src/utilcode/memorypool.cpp | 317 |
1 files changed, 317 insertions, 0 deletions
diff --git a/src/utilcode/memorypool.cpp b/src/utilcode/memorypool.cpp new file mode 100644 index 0000000000..1db8291e6a --- /dev/null +++ b/src/utilcode/memorypool.cpp @@ -0,0 +1,317 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "stdafx.h" + +#include "memorypool.h" +#include "ex.h" + +size_t MemoryPool::GetSize() +{ + LIMITED_METHOD_CONTRACT; + size_t retval=0; + + Block *block = m_blocks; + while (block != NULL) + { + retval+=(BYTE*)block->elementsEnd-(BYTE*)block->elements; + block = block->next; + } + return retval; +} + +#ifndef DACCESS_COMPILE + +BOOL MemoryPool::AddBlock(SIZE_T elementCount) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT(return FALSE;); + } CONTRACTL_END; + + // + // Check for arithmetic overflow + // + S_SIZE_T cbBlockSize = S_SIZE_T(elementCount) * S_SIZE_T(m_elementSize); + S_SIZE_T cbAllocSize = S_SIZE_T(sizeof(Block)) + cbBlockSize; + if (cbBlockSize.IsOverflow() || cbAllocSize.IsOverflow()) + return FALSE; + + // + // Allocate the new block. + // + + Block *block = (Block *) new (nothrow) BYTE [cbAllocSize.Value()]; + + if (block == NULL) + return FALSE; + + // + // Chain all elements together for the free list + // + + _ASSERTE(m_freeList == NULL); + Element **prev = &m_freeList; + + Element *e = block->elements; + Element *eEnd = (Element *) ((BYTE*) block->elements + elementCount*m_elementSize); + while (e < eEnd) + { + *prev = e; + prev = &e->next; +#if _DEBUG + DeadBeef(e); +#endif + e = (Element*) ((BYTE*)e + m_elementSize); + } + + *prev = NULL; + + // + // Initialize the other block fields & link the block into the block list + // + + block->elementsEnd = e; + block->next = m_blocks; + m_blocks = block; + + return TRUE; +} + +void MemoryPool::DeadBeef(Element *element) +{ +#if _DEBUG + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + int *i = &element->deadBeef; + int *iEnd = (int*) ((BYTE*)element + m_elementSize); + while (i < iEnd) + *i++ = (int) 0xdeadbeef; +#else + LIMITED_METHOD_CONTRACT; +#endif +} + +MemoryPool::MemoryPool(SIZE_T elementSize, SIZE_T initGrowth, SIZE_T initCount) + : m_elementSize(elementSize), + m_growCount(initGrowth), + m_blocks(NULL), + m_freeList(NULL) +{ + CONTRACTL { + if (initCount) THROWS; else NOTHROW; + GC_NOTRIGGER; + } CONTRACTL_END; + + _ASSERTE(elementSize >= sizeof(Element)); + _ASSERTE((elementSize & ((sizeof(PVOID)-1))) == 0); + + if (initCount > 0) + AddBlock(initCount); +} + +MemoryPool::~MemoryPool() +{ + LIMITED_METHOD_CONTRACT; + Block *block = m_blocks; + while (block != NULL) + { + Block *next = block->next; + delete [] (BYTE*) block; + block = next; + } +} + +BOOL MemoryPool::IsElement(void *element) +{ + LIMITED_METHOD_CONTRACT; + + Block *block = m_blocks; + while (block != NULL) + { + if (element >= block->elements && + element < block->elementsEnd ) + { + return ((BYTE *)element - (BYTE*)block->elements) % m_elementSize == 0; + } + block = block->next; + } + + return FALSE; +} + +BOOL MemoryPool::IsAllocatedElement(void *element) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + + if (!IsElement(element)) + return FALSE; + + // + // Now, make sure the element isn't + // in the free list. + // + +#if _DEBUG + // + // In a debug build, all objects on the free list + // will be marked with deadbeef. This means that + // if the object is not deadbeef, it's not on the + // free list. + // + // This check will give us decent performance in + // a debug build for FreeElement, since we + // always expect to return TRUE in that case. + // + + if (((Element*)element)->deadBeef != (int) 0xdeadBeef) + return TRUE; +#endif + + Element *f = m_freeList; + while (f != NULL) + { + if (f == element) + return FALSE; + f = f->next; + } + +#if _DEBUG + // + // We should never get here in a debug build, because + // all free elements should be deadbeefed. + // + _ASSERTE(!"Unreachable"); +#endif + + return TRUE; +} + +void *MemoryPool::AllocateElement() +{ + CONTRACTL { + THROWS; + GC_NOTRIGGER; + } CONTRACTL_END; + + void *element = AllocateElementNoThrow(); + if (element == NULL) + ThrowOutOfMemory(); + + return element; +} + +void *MemoryPool::AllocateElementNoThrow() +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + INJECT_FAULT( return FALSE; ); + } CONTRACTL_END; + + void *element = m_freeList; + + if (element == NULL) + { + if (!AddBlock(m_growCount)) + return NULL; + + m_growCount *= 2; + element = m_freeList; + } + + // if we come there means that addblock succeeded and m_freelist isn't null anymore + PREFIX_ASSUME(m_freeList!= NULL); + m_freeList = m_freeList->next; + + return element; +} + +void MemoryPool::FreeElement(void *element) +{ + CONTRACTL { + NOTHROW; + GC_NOTRIGGER; + CANNOT_TAKE_LOCK; + } CONTRACTL_END; + +#if _DEBUG // don't want to do this assert in a non-debug build; it is expensive + _ASSERTE(IsAllocatedElement(element)); +#endif + + Element *e = (Element *) element; + +#if _DEBUG + DeadBeef(e); +#endif + + e->next = m_freeList; + m_freeList = e; +} + +void MemoryPool::FreeAllElements() +{ + LIMITED_METHOD_CONTRACT; + + Block *block = m_blocks; + while (block != NULL) + { + Block *next = block->next; + delete [] block; + block = next; + } + + m_freeList = NULL; + m_blocks = NULL; +} + + + +MemoryPool::Iterator::Iterator(MemoryPool *pool) +{ + LIMITED_METHOD_CONTRACT; + + // + // Warning! This only works if you haven't freed + // any elements. + // + + m_next = pool->m_blocks; + m_e = NULL; + m_eEnd = NULL; + m_end = (BYTE*) pool->m_freeList; + m_size = pool->m_elementSize; +} + +BOOL MemoryPool::Iterator::Next() +{ + LIMITED_METHOD_CONTRACT; + + if (m_e == m_eEnd + || (m_e == m_end && m_end != NULL)) + { + if (m_next == NULL) + return FALSE; + m_e = (BYTE*) m_next->elements; + m_eEnd = (BYTE*) m_next->elementsEnd; + m_next = m_next->next; + if (m_e == m_end) + return FALSE; + } + + m_e += m_size; + + return TRUE; +} + +#endif |