diff options
Diffstat (limited to 'src/VBox/Runtime/generic/mempool-generic.cpp')
-rw-r--r-- | src/VBox/Runtime/generic/mempool-generic.cpp | 410 |
1 files changed, 410 insertions, 0 deletions
diff --git a/src/VBox/Runtime/generic/mempool-generic.cpp b/src/VBox/Runtime/generic/mempool-generic.cpp new file mode 100644 index 00000000..466ff180 --- /dev/null +++ b/src/VBox/Runtime/generic/mempool-generic.cpp @@ -0,0 +1,410 @@ +/* $Id: mempool-generic.cpp $ */ +/** @file + * IPRT - Memory Allocation Pool. + */ + +/* + * Copyright (C) 2009-2020 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/mempool.h> +#include "internal/iprt.h" + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/errcore.h> +#include <iprt/mem.h> +#include <iprt/spinlock.h> +#include <iprt/string.h> + +#include "internal/magics.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** Pointer to a memory pool instance. */ +typedef struct RTMEMPOOLINT *PRTMEMPOOLINT; +/** Pointer to a memory pool entry. */ +typedef struct RTMEMPOOLENTRY *PRTMEMPOOLENTRY; + +/** + * Memory pool entry. + */ +typedef struct RTMEMPOOLENTRY +{ + /** Pointer to the pool */ + PRTMEMPOOLINT pMemPool; + /** Pointer to the next entry. */ + PRTMEMPOOLENTRY volatile pNext; + /** Pointer to the previous entry. */ + PRTMEMPOOLENTRY volatile pPrev; + /** The number of references to the pool entry. */ + uint32_t volatile cRefs; +} RTMEMPOOLENTRY; + + +/** + * Memory pool instance data. + */ +typedef struct RTMEMPOOLINT +{ + /** Magic number (RTMEMPOOL_MAGIC). */ + uint32_t u32Magic; + /** Spinlock protecting the pool entry list updates. */ + RTSPINLOCK hSpinLock; + /** Head entry pointer. */ + PRTMEMPOOLENTRY volatile pHead; + /** The number of entries in the pool (for statistical purposes). */ + uint32_t volatile cEntries; + /** User data associated with the pool. */ + void *pvUser; + /** The pool name. (variable length) */ + char szName[8]; +} RTMEMPOOLINT; + + +/********************************************************************************************************************************* +* Defined Constants And Macros * +*********************************************************************************************************************************/ +/** Validates a memory pool handle, translating RTMEMPOOL_DEFAULT when found, + * and returns rc if not valid. */ +#define RTMEMPOOL_VALID_RETURN_RC(pMemPool, rc) \ + do { \ + if (pMemPool == RTMEMPOOL_DEFAULT) \ + pMemPool = &g_rtMemPoolDefault; \ + else \ + { \ + AssertPtrReturn((pMemPool), (rc)); \ + AssertReturn((pMemPool)->u32Magic == RTMEMPOOL_MAGIC, (rc)); \ + } \ + } while (0) + +/** Validates a memory pool entry and returns rc if not valid. */ +#define RTMEMPOOL_VALID_ENTRY_RETURN_RC(pEntry, rc) \ + do { \ + AssertPtrReturn(pEntry, (rc)); \ + AssertPtrNullReturn((pEntry)->pMemPool, (rc)); \ + Assert((pEntry)->cRefs < UINT32_MAX / 2); \ + AssertReturn((pEntry)->pMemPool->u32Magic == RTMEMPOOL_MAGIC, (rc)); \ + } while (0) + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The */ +static RTMEMPOOLINT g_rtMemPoolDefault = +{ + /* .u32Magic = */ RTMEMPOOL_MAGIC, + /* .hSpinLock = */ NIL_RTSPINLOCK, + /* .pHead = */ NULL, + /* .cEntries = */ 0, + /* .pvUser = */ NULL, + /* .szName = */ "default" +}; + + + +RTDECL(int) RTMemPoolCreate(PRTMEMPOOL phMemPool, const char *pszName) +{ + AssertPtr(phMemPool); + AssertPtr(pszName); + Assert(*pszName); + + size_t cchName = strlen(pszName); + PRTMEMPOOLINT pMemPool = (PRTMEMPOOLINT)RTMemAlloc(RT_UOFFSETOF_DYN(RTMEMPOOLINT, szName[cchName + 1])); + if (!pMemPool) + return VERR_NO_MEMORY; + int rc = RTSpinlockCreate(&pMemPool->hSpinLock, RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE, "RTMemPoolCreate"); + if (RT_SUCCESS(rc)) + { + pMemPool->u32Magic = RTMEMPOOL_MAGIC; + pMemPool->pHead = NULL; + pMemPool->cEntries = 0; + pMemPool->pvUser = NULL; + memcpy(pMemPool->szName, pszName, cchName); + *phMemPool = pMemPool; + return VINF_SUCCESS; + } + RTMemFree(pMemPool); + return rc; +} +RT_EXPORT_SYMBOL(RTMemPoolCreate); + + +RTDECL(int) RTMemPoolDestroy(RTMEMPOOL hMemPool) +{ + if (hMemPool == NIL_RTMEMPOOL) + return VINF_SUCCESS; + PRTMEMPOOLINT pMemPool = hMemPool; + RTMEMPOOL_VALID_RETURN_RC(pMemPool, VERR_INVALID_HANDLE); + if (pMemPool == &g_rtMemPoolDefault) + return VINF_SUCCESS; + + /* + * Invalidate the handle and free all associated resources. + */ + ASMAtomicWriteU32(&pMemPool->u32Magic, RTMEMPOOL_MAGIC_DEAD); + + int rc = RTSpinlockDestroy(pMemPool->hSpinLock); AssertRC(rc); + pMemPool->hSpinLock = NIL_RTSPINLOCK; + + PRTMEMPOOLENTRY pEntry = pMemPool->pHead; + pMemPool->pHead = NULL; + while (pEntry) + { + PRTMEMPOOLENTRY pFree = pEntry; + Assert(pFree->cRefs > 0 && pFree->cRefs < UINT32_MAX / 2); + pEntry = pEntry->pNext; + + pFree->pMemPool = NULL; + pFree->pNext = NULL; + pFree->pPrev = NULL; + pFree->cRefs = UINT32_MAX - 3; + RTMemFree(pFree); + } + + RTMemFree(pMemPool); + + return VINF_SUCCESS; +} +RT_EXPORT_SYMBOL(RTMemPoolDestroy); + + +DECLINLINE(void) rtMemPoolInitAndLink(PRTMEMPOOLINT pMemPool, PRTMEMPOOLENTRY pEntry) +{ + pEntry->pMemPool = pMemPool; + pEntry->pNext = NULL; + pEntry->pPrev = NULL; + pEntry->cRefs = 1; + + if (pMemPool->hSpinLock != NIL_RTSPINLOCK) + { + RTSpinlockAcquire(pMemPool->hSpinLock); + + PRTMEMPOOLENTRY pHead = pMemPool->pHead; + pEntry->pNext = pHead; + if (pHead) + pHead->pPrev = pEntry; + pMemPool->pHead = pEntry; + + RTSpinlockRelease(pMemPool->hSpinLock); + } + + ASMAtomicIncU32(&pMemPool->cEntries); +} + + +DECLINLINE(void) rtMemPoolUnlink(PRTMEMPOOLENTRY pEntry) +{ + PRTMEMPOOLINT pMemPool = pEntry->pMemPool; + if (pMemPool->hSpinLock != NIL_RTSPINLOCK) + { + RTSpinlockAcquire(pMemPool->hSpinLock); + + PRTMEMPOOLENTRY pNext = pEntry->pNext; + PRTMEMPOOLENTRY pPrev = pEntry->pPrev; + if (pNext) + pNext->pPrev = pPrev; + if (pPrev) + pPrev->pNext = pNext; + else + pMemPool->pHead = pNext; + pEntry->pMemPool = NULL; + + RTSpinlockRelease(pMemPool->hSpinLock); + } + else + pEntry->pMemPool = NULL; + + ASMAtomicDecU32(&pMemPool->cEntries); +} + + +RTDECL(void *) RTMemPoolAlloc(RTMEMPOOL hMemPool, size_t cb) RT_NO_THROW_DEF +{ + PRTMEMPOOLINT pMemPool = hMemPool; + RTMEMPOOL_VALID_RETURN_RC(pMemPool, NULL); + + PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemAlloc(cb + sizeof(*pEntry)); + if (!pEntry) + return NULL; + rtMemPoolInitAndLink(pMemPool, pEntry); + + return pEntry + 1; +} +RT_EXPORT_SYMBOL(RTMemPoolAlloc); + + +RTDECL(void *) RTMemPoolAllocZ(RTMEMPOOL hMemPool, size_t cb) RT_NO_THROW_DEF +{ + PRTMEMPOOLINT pMemPool = hMemPool; + RTMEMPOOL_VALID_RETURN_RC(pMemPool, NULL); + + PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemAllocZ(cb + sizeof(*pEntry)); + if (!pEntry) + return NULL; + rtMemPoolInitAndLink(pMemPool, pEntry); + + return pEntry + 1; +} +RT_EXPORT_SYMBOL(RTMemPoolAllocZ); + + +RTDECL(void *) RTMemPoolDup(RTMEMPOOL hMemPool, const void *pvSrc, size_t cb) RT_NO_THROW_DEF +{ + PRTMEMPOOLINT pMemPool = hMemPool; + RTMEMPOOL_VALID_RETURN_RC(pMemPool, NULL); + + PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemAlloc(cb + sizeof(*pEntry)); + if (!pEntry) + return NULL; + memcpy(pEntry + 1, pvSrc, cb); + rtMemPoolInitAndLink(pMemPool, pEntry); + + return pEntry + 1; +} +RT_EXPORT_SYMBOL(RTMemPoolDup); + + +RTDECL(void *) RTMemPoolDupEx(RTMEMPOOL hMemPool, const void *pvSrc, size_t cbSrc, size_t cbExtra) RT_NO_THROW_DEF +{ + PRTMEMPOOLINT pMemPool = hMemPool; + RTMEMPOOL_VALID_RETURN_RC(pMemPool, NULL); + + PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemAlloc(cbSrc + cbExtra + sizeof(*pEntry)); + if (!pEntry) + return NULL; + memcpy(pEntry + 1, pvSrc, cbSrc); + memset((uint8_t *)(pEntry + 1) + cbSrc, '\0', cbExtra); + rtMemPoolInitAndLink(pMemPool, pEntry); + + return pEntry + 1; +} +RT_EXPORT_SYMBOL(RTMemPoolDupEx); + + + +RTDECL(void *) RTMemPoolRealloc(RTMEMPOOL hMemPool, void *pvOld, size_t cbNew) RT_NO_THROW_DEF +{ + /* + * Fend off the odd cases. + */ + if (!cbNew) + { + RTMemPoolRelease(hMemPool, pvOld); + return NULL; + } + + if (!pvOld) + return RTMemPoolAlloc(hMemPool, cbNew); + + /* + * Real realloc. + */ + PRTMEMPOOLINT pNewMemPool = hMemPool; + RTMEMPOOL_VALID_RETURN_RC(pNewMemPool, NULL); + + PRTMEMPOOLENTRY pOldEntry = (PRTMEMPOOLENTRY)pvOld - 1; + RTMEMPOOL_VALID_ENTRY_RETURN_RC(pOldEntry, NULL); + PRTMEMPOOLINT pOldMemPool = pOldEntry->pMemPool; + AssertReturn(pOldEntry->cRefs == 1, NULL); + + /* + * Unlink it from the current pool and try reallocate it. + */ + rtMemPoolUnlink(pOldEntry); + + PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)RTMemRealloc(pOldEntry, cbNew + sizeof(*pEntry)); + if (!pEntry) + { + rtMemPoolInitAndLink(pOldMemPool, pOldEntry); + return NULL; + } + rtMemPoolInitAndLink(pNewMemPool, pEntry); + + return pEntry + 1; +} +RT_EXPORT_SYMBOL(RTMemPoolRealloc); + + +RTDECL(void) RTMemPoolFree(RTMEMPOOL hMemPool, void *pv) RT_NO_THROW_DEF +{ + RTMemPoolRelease(hMemPool, pv); +} +RT_EXPORT_SYMBOL(RTMemPoolFree); + + +RTDECL(uint32_t) RTMemPoolRetain(void *pv) RT_NO_THROW_DEF +{ + PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)pv - 1; + RTMEMPOOL_VALID_ENTRY_RETURN_RC(pEntry, UINT32_MAX); + + uint32_t cRefs = ASMAtomicIncU32(&pEntry->cRefs); + Assert(cRefs < UINT32_MAX / 2); + + return cRefs; +} +RT_EXPORT_SYMBOL(RTMemPoolRetain); + + +RTDECL(uint32_t) RTMemPoolRelease(RTMEMPOOL hMemPool, void *pv) RT_NO_THROW_DEF +{ + if (!pv) + return 0; + + PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)pv - 1; + RTMEMPOOL_VALID_ENTRY_RETURN_RC(pEntry, UINT32_MAX); + Assert( hMemPool == NIL_RTMEMPOOL + || hMemPool == pEntry->pMemPool + || (hMemPool == RTMEMPOOL_DEFAULT && pEntry->pMemPool == &g_rtMemPoolDefault)); RT_NOREF_PV(hMemPool); + AssertReturn(pEntry->cRefs > 0, UINT32_MAX); + + uint32_t cRefs = ASMAtomicDecU32(&pEntry->cRefs); + Assert(cRefs < UINT32_MAX / 2); + if (!cRefs) + { + rtMemPoolUnlink(pEntry); + pEntry->cRefs = UINT32_MAX - 2; + RTMemFree(pEntry); + } + + return cRefs; +} +RT_EXPORT_SYMBOL(RTMemPoolRelease); + + +RTDECL(uint32_t) RTMemPoolRefCount(void *pv) RT_NO_THROW_DEF +{ + PRTMEMPOOLENTRY pEntry = (PRTMEMPOOLENTRY)pv - 1; + RTMEMPOOL_VALID_ENTRY_RETURN_RC(pEntry, UINT32_MAX); + + uint32_t cRefs = ASMAtomicReadU32(&pEntry->cRefs); + Assert(cRefs < UINT32_MAX / 2); + + return cRefs; +} +RT_EXPORT_SYMBOL(RTMemPoolRefCount); + |