memcache.cpp revision c58f1213e628a545081c70e26c6b67a841cff880
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/* $Id$ */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/** @file
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * IPRT - Memory Object Allocation Cache.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/*
c58f1213e628a545081c70e26c6b67a841cff880vboxsync * Copyright (C) 2006-2012 Oracle Corporation
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync *
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * available from http://www.virtualbox.org. This file is free software;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * you can redistribute it and/or modify it under the terms of the GNU
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * General Public License (GPL) as published by the Free Software
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync *
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * The contents of this file may alternatively be used under the terms
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * of the Common Development and Distribution License Version 1.0
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * VirtualBox OSE distribution, in which case the provisions of the
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * CDDL are applicable instead of those of the GPL.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync *
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * You may elect to license modified versions of this file under the
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * terms and conditions of either the GPL or the CDDL or both.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/*******************************************************************************
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync* Header Files *
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync*******************************************************************************/
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include <iprt/memcache.h>
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include "internal/iprt.h"
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include <iprt/assert.h>
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include <iprt/asm.h>
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include <iprt/critsect.h>
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include <iprt/err.h>
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include <iprt/mem.h>
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include <iprt/param.h>
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync#include "internal/magics.h"
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/*******************************************************************************
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync* Structures and Typedefs *
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync*******************************************************************************/
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/** Pointer to a cache instance. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsynctypedef struct RTMEMCACHEINT *PRTMEMCACHEINT;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/** Pointer to a cache page. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsynctypedef struct RTMEMCACHEPAGE *PRTMEMCACHEPAGE;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync/**
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * A free object.
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync *
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * @remarks This only works if the objects don't have a constructor or
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * destructor and are big enough.
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsynctypedef struct RTMEMCACHEFREEOBJ
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync{
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /** Pointer to the next free object */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync struct RTMEMCACHEFREEOBJ * volatile pNext;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync} RTMEMCACHEFREEOBJ;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync/** Pointer to a free object. */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsynctypedef RTMEMCACHEFREEOBJ *PRTMEMCACHEFREEOBJ;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/**
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * A cache page.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync *
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * This is a page of memory that we split up in to a bunch object sized chunks
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * and hand out to the cache users. The bitmap is updated in an atomic fashion
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * so that we don't have to take any locks when freeing or allocating memory.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsynctypedef struct RTMEMCACHEPAGE
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Pointer to the cache owning this page.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * This is used for validation purposes only. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync PRTMEMCACHEINT pCache;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Pointer to the next page.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * This is marked as volatile since we'll be adding new entries to the list
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * without taking any locks. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync PRTMEMCACHEPAGE volatile pNext;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Bitmap tracking allocated blocks. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync void volatile *pbmAlloc;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Bitmap tracking which blocks that has been thru the constructor. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync void volatile *pbmCtor;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Pointer to the object array.. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint8_t *pbObjects;
09e11be404479b989812ad9da85a16f3654ab320vboxsync /** The number of objects on this page. */
09e11be404479b989812ad9da85a16f3654ab320vboxsync uint32_t cObjects;
09e11be404479b989812ad9da85a16f3654ab320vboxsync
09e11be404479b989812ad9da85a16f3654ab320vboxsync /** Padding to force cFree into the next cache line. (ASSUMES CL = 64) */
09e11be404479b989812ad9da85a16f3654ab320vboxsync uint8_t abPadding[ARCH_BITS == 32 ? 64 - 6*4 : 64 - 5*8 - 4];
09e11be404479b989812ad9da85a16f3654ab320vboxsync /** The number of free objects. */
09e11be404479b989812ad9da85a16f3654ab320vboxsync int32_t volatile cFree;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync} RTMEMCACHEPAGE;
09e11be404479b989812ad9da85a16f3654ab320vboxsyncAssertCompileMemberOffset(RTMEMCACHEPAGE, cFree, 64);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/**
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Memory object cache instance.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsynctypedef struct RTMEMCACHEINT
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Magic value (RTMEMCACHE_MAGIC). */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint32_t u32Magic;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** The object size. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint32_t cbObject;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Object alignment. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint32_t cbAlignment;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** The per page object count. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint32_t cPerPage;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Number of bits in the bitmap.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * @remarks This is higher or equal to cPerPage and it is aligned such that
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * the search operation will be most efficient on x86/AMD64. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint32_t cBits;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** The maximum number of objects. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint32_t cMax;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /** Whether to the use the free list or not. */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync bool fUseFreeList;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Head of the page list. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync PRTMEMCACHEPAGE pPageHead;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Constructor callback. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync PFNMEMCACHECTOR pfnCtor;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Destructor callback. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync PFNMEMCACHEDTOR pfnDtor;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Callback argument. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync void *pvUser;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /** Critical section serializing page allocation and similar. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTCRITSECT CritSect;
09e11be404479b989812ad9da85a16f3654ab320vboxsync
09e11be404479b989812ad9da85a16f3654ab320vboxsync /** The total object count. */
09e11be404479b989812ad9da85a16f3654ab320vboxsync uint32_t volatile cTotal;
09e11be404479b989812ad9da85a16f3654ab320vboxsync /** The number of free objects. */
09e11be404479b989812ad9da85a16f3654ab320vboxsync int32_t volatile cFree;
09e11be404479b989812ad9da85a16f3654ab320vboxsync /** This may point to a page with free entries. */
09e11be404479b989812ad9da85a16f3654ab320vboxsync PRTMEMCACHEPAGE volatile pPageHint;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /** Stack of free items.
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * These are marked as used in the allocation bitmaps.
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync *
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * @todo This doesn't scale well when several threads are beating on the
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * cache. Also, it totally doesn't work when we've got a
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * constructor/destructor around or the objects are too small. */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync PRTMEMCACHEFREEOBJ volatile pFreeTop;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync} RTMEMCACHEINT;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsyncRTDECL(int) RTMemCacheCreate(PRTMEMCACHE phMemCache, size_t cbObject, size_t cbAlignment, uint32_t cMaxObjects,
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync PFNMEMCACHECTOR pfnCtor, PFNMEMCACHEDTOR pfnDtor, void *pvUser, uint32_t fFlags)
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertPtr(phMemCache);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertPtrNull(pfnCtor);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertPtrNull(pfnDtor);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync AssertReturn(!pfnDtor || pfnCtor, VERR_INVALID_PARAMETER);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertReturn(cbObject > 0, VERR_INVALID_PARAMETER);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertReturn(cbObject <= PAGE_SIZE / 8, VERR_INVALID_PARAMETER);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (cbAlignment == 0)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync if (cbObject <= 2)
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync cbAlignment = cbObject;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync else if (cbObject <= 4)
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync cbAlignment = 4;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync else if (cbObject <= 8)
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync cbAlignment = 8;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync else if (cbObject <= 16)
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync cbAlignment = 16;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync else if (cbObject <= 32)
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync cbAlignment = 32;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync else
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync cbAlignment = 64;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync else
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertReturn(!((cbAlignment - 1) & cbAlignment), VERR_NOT_POWER_OF_TWO);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertReturn(cbAlignment <= 64, VERR_OUT_OF_RANGE);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /*
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Allocate and initialize the instance memory.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTMEMCACHEINT *pThis = (RTMEMCACHEINT *)RTMemAlloc(sizeof(*pThis));
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (!pThis)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return VERR_NO_MEMORY;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync int rc = RTCritSectInit(&pThis->CritSect);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (RT_FAILURE(rc))
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTMemFree(pThis);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return rc;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pThis->u32Magic = RTMEMCACHE_MAGIC;
0aa150e34ed49f14aaa37368c2e6999ec89e5f43vboxsync pThis->cbObject = (uint32_t)RT_ALIGN_Z(cbObject, cbAlignment);
0aa150e34ed49f14aaa37368c2e6999ec89e5f43vboxsync pThis->cbAlignment = (uint32_t)cbAlignment;
0aa150e34ed49f14aaa37368c2e6999ec89e5f43vboxsync pThis->cPerPage = (uint32_t)((PAGE_SIZE - RT_ALIGN_Z(sizeof(RTMEMCACHEPAGE), cbAlignment)) / pThis->cbObject);
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync while ( RT_ALIGN_Z(sizeof(RTMEMCACHEPAGE), 8)
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync + pThis->cPerPage * pThis->cbObject
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync + RT_ALIGN(pThis->cPerPage, 64) / 8 * 2
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync > PAGE_SIZE)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pThis->cPerPage--;
09e11be404479b989812ad9da85a16f3654ab320vboxsync pThis->cBits = RT_ALIGN(pThis->cPerPage, 64);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pThis->cMax = cMaxObjects;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync pThis->fUseFreeList = cbObject >= sizeof(RTMEMCACHEFREEOBJ)
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync && !pfnCtor
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync && !pfnDtor;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pThis->pPageHead = NULL;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pThis->pfnCtor = pfnCtor;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pThis->pfnDtor = pfnDtor;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pThis->pvUser = pvUser;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync pThis->cTotal = 0;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync pThis->cFree = 0;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync pThis->pPageHint = NULL;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync pThis->pFreeTop = NULL;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync /** @todo
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync * Here is a puzzler (or maybe I'm just blind), the free list code breaks
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync * badly on my macbook pro (i7) (32-bit).
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync *
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync * I tried changing the reads from unordered to ordered to no avail. Then I
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync * tried optimizing the code with the ASMAtomicCmpXchgExPtr function to
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync * avoid some reads - no change. Inserting pause instructions did nothing
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync * (as expected). The only thing which seems to make a difference is
4bfa7b58e362a1bca0628643c352c137900bf01avboxsync * reading the pFreeTop pointer twice in the free code... This is weird or I'm
4bfa7b58e362a1bca0628643c352c137900bf01avboxsync * overlooking something..
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync *
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync * No time to figure it out, so I'm disabling the broken code paths for
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync * now. */
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync pThis->fUseFreeList = false;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync *phMemCache = pThis;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return VINF_SUCCESS;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync}
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsyncRTDECL(int) RTMemCacheDestroy(RTMEMCACHE hMemCache)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTMEMCACHEINT *pThis = hMemCache;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (!pThis)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return VINF_SUCCESS;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertReturn(pThis->u32Magic == RTMEMCACHE_MAGIC, VERR_INVALID_HANDLE);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync#ifdef RT_STRICT
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync uint32_t cFree = pThis->cFree;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync for (PRTMEMCACHEFREEOBJ pFree = pThis->pFreeTop; pFree && cFree < pThis->cTotal + 5; pFree = pFree->pNext)
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync cFree++;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync AssertMsg(cFree == pThis->cTotal, ("cFree=%u cTotal=%u\n", cFree, pThis->cTotal));
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync#endif
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /*
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Destroy it.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTMEMCACHE_MAGIC_DEAD, RTMEMCACHE_MAGIC), VERR_INVALID_HANDLE);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTCritSectDelete(&pThis->CritSect);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync while (pThis->pPageHead)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync PRTMEMCACHEPAGE pPage = pThis->pPageHead;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pThis->pPageHead = pPage->pNext;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pPage->cFree = 0;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (pThis->pfnDtor)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint32_t iObj = pPage->cObjects;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync while (iObj-- > 0)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (ASMBitTestAndClear(pPage->pbmCtor, iObj))
64babf0f32eaf36212d54af4a3ce5fe193b24825vboxsync pThis->pfnDtor(hMemCache, pPage->pbObjects + iObj * pThis->cbObject, pThis->pvUser);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
c307f0d7384bfc4d19d2290a28be89868f02f42avboxsync RTMemPageFree(pPage, PAGE_SIZE);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTMemFree(pThis);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return VINF_SUCCESS;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync}
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/**
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Grows the cache.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync *
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * @returns IPRT status code.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * @param pThis The memory cache instance.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsyncstatic int rtMemCacheGrow(RTMEMCACHEINT *pThis)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /*
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Enter the critical section here to avoid allocation races leading to
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * wasted memory (++) and make it easier to link in the new page.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTCritSectEnter(&pThis->CritSect);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync int rc = VINF_SUCCESS;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (pThis->cFree < 0)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /*
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Allocate and initialize the new page.
09e11be404479b989812ad9da85a16f3654ab320vboxsync *
09e11be404479b989812ad9da85a16f3654ab320vboxsync * We put the constructor bitmap at the lower end right after cFree.
09e11be404479b989812ad9da85a16f3654ab320vboxsync * We then push the object array to the end of the page and place the
09e11be404479b989812ad9da85a16f3654ab320vboxsync * allocation bitmap below it. The hope is to increase the chance that
09e11be404479b989812ad9da85a16f3654ab320vboxsync * the allocation bitmap is in a different cache line than cFree since
09e11be404479b989812ad9da85a16f3654ab320vboxsync * this increases performance markably when lots of threads are beating
09e11be404479b989812ad9da85a16f3654ab320vboxsync * on the cache.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync PRTMEMCACHEPAGE pPage = (PRTMEMCACHEPAGE)RTMemPageAlloc(PAGE_SIZE);
64babf0f32eaf36212d54af4a3ce5fe193b24825vboxsync if (pPage)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
64babf0f32eaf36212d54af4a3ce5fe193b24825vboxsync uint32_t const cObjects = RT_MIN(pThis->cPerPage, pThis->cMax - pThis->cTotal);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMMemZeroPage(pPage);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pPage->pCache = pThis;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pPage->pNext = NULL;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pPage->cFree = cObjects;
64babf0f32eaf36212d54af4a3ce5fe193b24825vboxsync pPage->cObjects = cObjects;
09e11be404479b989812ad9da85a16f3654ab320vboxsync uint8_t *pb = (uint8_t *)(pPage + 1);
09e11be404479b989812ad9da85a16f3654ab320vboxsync pb = RT_ALIGN_PT(pb, 8, uint8_t *);
09e11be404479b989812ad9da85a16f3654ab320vboxsync pPage->pbmCtor = pb;
09e11be404479b989812ad9da85a16f3654ab320vboxsync pb = (uint8_t *)pPage + PAGE_SIZE - pThis->cbObject * cObjects;
09e11be404479b989812ad9da85a16f3654ab320vboxsync pPage->pbObjects = pb; Assert(RT_ALIGN_P(pb, pThis->cbAlignment) == pb);
09e11be404479b989812ad9da85a16f3654ab320vboxsync pb -= pThis->cBits / 8;
09e11be404479b989812ad9da85a16f3654ab320vboxsync pb = (uint8_t *)((uintptr_t)pb & ~(uintptr_t)7);
09e11be404479b989812ad9da85a16f3654ab320vboxsync pPage->pbmAlloc = pb;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert((uintptr_t)pPage->pbmCtor + pThis->cBits / 8 <= (uintptr_t)pPage->pbmAlloc);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /* Mark the bitmap padding and any unused objects as allocated. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync for (uint32_t iBit = cObjects; iBit < pThis->cBits; iBit++)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMBitSet(pPage->pbmAlloc, iBit);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /* Make it the hint. */
c7ff622115966b69b482bd2896662e40d823b22fvboxsync ASMAtomicWritePtr(&pThis->pPageHint, pPage);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /* Link the page. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync PRTMEMCACHEPAGE pPrevPage = pThis->pPageHead;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (!pPrevPage)
c7ff622115966b69b482bd2896662e40d823b22fvboxsync ASMAtomicWritePtr(&pThis->pPageHead, pPage);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync else
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync while (pPrevPage->pNext)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync pPrevPage = pPrevPage->pNext;
c7ff622115966b69b482bd2896662e40d823b22fvboxsync ASMAtomicWritePtr(&pPrevPage->pNext, pPage);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /* Add it to the page counts. */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMAtomicAddS32(&pThis->cFree, cObjects);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMAtomicAddU32(&pThis->cTotal, cObjects);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync else
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync rc = VERR_NO_MEMORY;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTCritSectLeave(&pThis->CritSect);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return rc;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync}
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync/**
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Grabs a an object in a page.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * @returns New cFree value on success (0 or higher), -1 on failure.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * @param pPage Pointer to the page.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsyncDECL_FORCE_INLINE(int32_t) rtMemCacheGrabObj(PRTMEMCACHEPAGE pPage)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync int32_t cFreeNew = ASMAtomicDecS32(&pPage->cFree);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (cFreeNew < 0)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMAtomicIncS32(&pPage->cFree);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return -1;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return cFreeNew;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync}
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsyncRTDECL(int) RTMemCacheAllocEx(RTMEMCACHE hMemCache, void **ppvObj)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTMEMCACHEINT *pThis = hMemCache;
d273db24f1a69abac22a9c2f9c8251e16b66c47fvboxsync AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
5a7561300d631625bc381bcc85dd2087f34d0bf9vboxsync AssertReturn(pThis->u32Magic == RTMEMCACHE_MAGIC, VERR_INVALID_PARAMETER);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /*
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * Try grab a free object from the stack.
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync */
c7ff622115966b69b482bd2896662e40d823b22fvboxsync PRTMEMCACHEFREEOBJ pObj = ASMAtomicUoReadPtrT(&pThis->pFreeTop, PRTMEMCACHEFREEOBJ);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync if (pObj)
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync {
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync do
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync {
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync PRTMEMCACHEFREEOBJ pNext = ASMAtomicUoReadPtrT(&pObj->pNext, PRTMEMCACHEFREEOBJ);
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync PRTMEMCACHEFREEOBJ pObjOld;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync if (ASMAtomicCmpXchgExPtr(&pThis->pFreeTop, pNext, pObj, &pObjOld))
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync {
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync Assert(pObjOld == pObj);
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync Assert(pNext != pObjOld);
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync pObj->pNext = NULL;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync *ppvObj = pObj;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync return VINF_SUCCESS;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync }
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync pObj = pObjOld;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync ASMNopPause();
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync } while (pObj);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync }
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /*
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Try grab a free object at the cache level.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync int32_t cNewFree = ASMAtomicDecS32(&pThis->cFree);
09e11be404479b989812ad9da85a16f3654ab320vboxsync if (RT_LIKELY(cNewFree < 0))
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync uint32_t cTotal = ASMAtomicUoReadU32(&pThis->cTotal);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if ( (uint32_t)(cTotal + -cNewFree) > pThis->cMax
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync || (uint32_t)(cTotal + -cNewFree) <= cTotal)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMAtomicIncS32(&pThis->cFree);
64babf0f32eaf36212d54af4a3ce5fe193b24825vboxsync return VERR_MEM_CACHE_MAX_SIZE;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync int rc = rtMemCacheGrow(pThis);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (RT_FAILURE(rc))
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMAtomicIncS32(&pThis->cFree);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return rc;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /*
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Grab a free object at the page level.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
c7ff622115966b69b482bd2896662e40d823b22fvboxsync PRTMEMCACHEPAGE pPage = ASMAtomicReadPtrT(&pThis->pPageHint, PRTMEMCACHEPAGE);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync int32_t iObj = pPage ? rtMemCacheGrabObj(pPage) : -1;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (iObj < 0)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync for (unsigned cLoops = 0; ; cLoops++)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync for (pPage = pThis->pPageHead; pPage; pPage = pPage->pNext)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync iObj = rtMemCacheGrabObj(pPage);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (iObj >= 0)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (iObj > 0)
c7ff622115966b69b482bd2896662e40d823b22fvboxsync ASMAtomicWritePtr(&pThis->pPageHint, pPage);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync break;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (iObj >= 0)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync break;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync Assert(cLoops != 2);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync Assert(cLoops < 10);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync Assert(iObj >= 0);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync Assert((uint32_t)iObj < pThis->cMax);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /*
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Find a free object in the allocation bitmap. Use the new cFree count
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * as a hint.
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (ASMAtomicBitTestAndSet(pPage->pbmAlloc, iObj))
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync for (unsigned cLoops2 = 0;; cLoops2++)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync iObj = ASMBitFirstClear(pPage->pbmAlloc, pThis->cBits);
09e11be404479b989812ad9da85a16f3654ab320vboxsync if (RT_LIKELY(iObj >= 0))
09e11be404479b989812ad9da85a16f3654ab320vboxsync {
09e11be404479b989812ad9da85a16f3654ab320vboxsync if (!ASMAtomicBitTestAndSet(pPage->pbmAlloc, iObj))
09e11be404479b989812ad9da85a16f3654ab320vboxsync break;
09e11be404479b989812ad9da85a16f3654ab320vboxsync }
09e11be404479b989812ad9da85a16f3654ab320vboxsync else
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMMemoryFence();
64babf0f32eaf36212d54af4a3ce5fe193b24825vboxsync Assert(cLoops2 != 40);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync Assert(iObj >= 0);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync void *pvObj = &pPage->pbObjects[iObj * pThis->cbObject];
64babf0f32eaf36212d54af4a3ce5fe193b24825vboxsync Assert((uintptr_t)pvObj - (uintptr_t)pPage < PAGE_SIZE);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync /*
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync * Call the constructor?
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if ( pThis->pfnCtor
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync && !ASMAtomicBitTestAndSet(pPage->pbmCtor, iObj))
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync int rc = pThis->pfnCtor(hMemCache, pvObj, pThis->pvUser);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (RT_FAILURE(rc))
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync {
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync ASMAtomicBitClear(pPage->pbmCtor, iObj);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTMemCacheFree(pThis, pvObj);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return rc;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
64babf0f32eaf36212d54af4a3ce5fe193b24825vboxsync *ppvObj = pvObj;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return VINF_SUCCESS;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync}
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsyncRTDECL(void *) RTMemCacheAlloc(RTMEMCACHE hMemCache)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync void *pvObj;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync int rc = RTMemCacheAllocEx(hMemCache, &pvObj);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (RT_SUCCESS(rc))
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return pvObj;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return NULL;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync}
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsyncRTDECL(void) RTMemCacheFree(RTMEMCACHE hMemCache, void *pvObj)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync{
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync if (!pvObj)
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync return;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync RTMEMCACHEINT *pThis = hMemCache;
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertPtrReturnVoid(pThis);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertReturnVoid(pThis->u32Magic == RTMEMCACHE_MAGIC);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync AssertPtr(pvObj);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync Assert(RT_ALIGN_P(pvObj, pThis->cbAlignment) == pvObj);
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync if (pThis->fUseFreeList)
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync {
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync# ifdef RT_STRICT
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /* This is the same as the other branch, except it's not actually freed. */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync PRTMEMCACHEPAGE pPage = (PRTMEMCACHEPAGE)(((uintptr_t)pvObj) & ~(uintptr_t)PAGE_OFFSET_MASK);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert(pPage->pCache == pThis);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert(ASMAtomicUoReadS32(&pPage->cFree) < (int32_t)pThis->cPerPage);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync uintptr_t offObj = (uintptr_t)pvObj - (uintptr_t)pPage->pbObjects;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync uintptr_t iObj = offObj / pThis->cbObject;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert(iObj * pThis->cbObject == offObj);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert(iObj < pThis->cPerPage);
1f963f01f4f9a3848e72d4748c4f36feb715dba3vboxsync AssertReturnVoid(ASMBitTest(pPage->pbmAlloc, (int32_t)iObj));
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync# endif
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /*
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * Push it onto the free stack.
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync PRTMEMCACHEFREEOBJ pObj = (PRTMEMCACHEFREEOBJ)pvObj;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync PRTMEMCACHEFREEOBJ pNext = ASMAtomicUoReadPtrT(&pThis->pFreeTop, PRTMEMCACHEFREEOBJ);
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync PRTMEMCACHEFREEOBJ pFreeTopOld;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync pObj->pNext = pNext;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync while (!ASMAtomicCmpXchgExPtr(&pThis->pFreeTop, pObj, pNext, &pFreeTopOld))
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync {
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync pNext = pFreeTopOld;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync Assert(pNext != pObj);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync pObj->pNext = pNext;
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync ASMNopPause();
21a96ae495634bdc8bbcc6ed7d8d727a1b9430f5vboxsync }
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync }
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync else
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync {
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /* Note: Do *NOT* attempt to poison the object if we have a constructor
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync or/and destructor! */
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /*
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * Find the cache page. The page structure is at the start of the page.
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync PRTMEMCACHEPAGE pPage = (PRTMEMCACHEPAGE)(((uintptr_t)pvObj) & ~(uintptr_t)PAGE_OFFSET_MASK);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert(pPage->pCache == pThis);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert(ASMAtomicUoReadS32(&pPage->cFree) < (int32_t)pThis->cPerPage);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync /*
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync * Clear the bitmap bit and update the two object counter. Order matters!
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync */
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync uintptr_t offObj = (uintptr_t)pvObj - (uintptr_t)pPage->pbObjects;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync uintptr_t iObj = offObj / pThis->cbObject;
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert(iObj * pThis->cbObject == offObj);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync Assert(iObj < pThis->cPerPage);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync AssertReturnVoid(ASMAtomicBitTestAndClear(pPage->pbmAlloc, iObj));
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync ASMAtomicIncS32(&pPage->cFree);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync ASMAtomicIncS32(&pThis->cFree);
db2893e30b129fbadd201b8f56cd7bc1974b395dvboxsync }
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync}
66b15150e35f27f9499bb0a8399e452d6a04895dvboxsync