#include "tbbmalloc_internal.h"
#include <new>
namespace rml {
namespace internal {
struct BackRefBlock : public BlockI {
BackRefBlock *nextForUse; FreeObject *bumpPtr; FreeObject *freeList;
BackRefBlock *nextRawMemBlock;
std::atomic<int> allocatedCount; BackRefIdx::main_t myNum; MallocMutex blockMutex;
std::atomic<bool> addedToForUse;
BackRefBlock(const BackRefBlock *blockToUse, intptr_t num) :
nextForUse(NULL), bumpPtr((FreeObject*)((uintptr_t)blockToUse + slabSize - sizeof(void*))),
freeList(NULL), nextRawMemBlock(NULL), allocatedCount(0), myNum(num),
addedToForUse(false) {
memset(&blockMutex, 0, sizeof(MallocMutex));
MALLOC_ASSERT(!(num >> CHAR_BIT*sizeof(BackRefIdx::main_t)),
"index in BackRefMain must fit to BackRefIdx::main");
}
void zeroSet() { memset(this+1, 0, BackRefBlock::bytes-sizeof(BackRefBlock)); }
static const int bytes = slabSize;
};
static const int BR_MAX_CNT = (BackRefBlock::bytes-sizeof(BackRefBlock))/sizeof(void*);
struct BackRefMain {
static const size_t bytes = sizeof(uintptr_t)>4? 256*1024 : 8*1024;
static const int dataSz;
static const int leaves = 4;
static const size_t mainSize = BackRefMain::bytes+leaves*BackRefBlock::bytes;
static const size_t blockSpaceSize = 64*1024;
Backend *backend;
std::atomic<BackRefBlock*> active; std::atomic<BackRefBlock*> listForUse; BackRefBlock *allRawMemBlocks;
std::atomic <intptr_t> lastUsed; bool rawMemUsed;
MallocMutex requestNewSpaceMutex;
BackRefBlock *backRefBl[1];
BackRefBlock *findFreeBlock();
void addToForUseList(BackRefBlock *bl);
void initEmptyBackRefBlock(BackRefBlock *newBl);
bool requestNewSpace();
};
const int BackRefMain::dataSz
= 1+(BackRefMain::bytes-sizeof(BackRefMain))/sizeof(BackRefBlock*);
static MallocMutex mainMutex;
static std::atomic<BackRefMain*> backRefMain;
bool initBackRefMain(Backend *backend)
{
bool rawMemUsed;
BackRefMain *main =
(BackRefMain*)backend->getBackRefSpace(BackRefMain::mainSize,
&rawMemUsed);
if (! main)
return false;
main->backend = backend;
main->listForUse.store(nullptr, std::memory_order_relaxed);
main->allRawMemBlocks = nullptr;
main->rawMemUsed = rawMemUsed;
main->lastUsed = -1;
memset(&main->requestNewSpaceMutex, 0, sizeof(MallocMutex));
for (int i=0; i<BackRefMain::leaves; i++) {
BackRefBlock *bl = (BackRefBlock*)((uintptr_t)main + BackRefMain::bytes + i*BackRefBlock::bytes);
bl->zeroSet();
main->initEmptyBackRefBlock(bl);
if (i)
main->addToForUseList(bl);
else main->active.store(bl, std::memory_order_relaxed);
}
backRefMain.store(main, std::memory_order_release);
return true;
}
#if __TBB_SOURCE_DIRECTLY_INCLUDED
void destroyBackRefMain(Backend *backend)
{
if (backRefMain.load(std::memory_order_acquire)) { for (BackRefBlock *curr = backRefMain.load(std::memory_order_relaxed)->allRawMemBlocks; curr; ) {
BackRefBlock *next = curr->nextRawMemBlock;
backend->putBackRefSpace(curr, BackRefMain::blockSpaceSize,
true);
curr = next;
}
backend->putBackRefSpace(backRefMain.load(std::memory_order_relaxed), BackRefMain::mainSize,
backRefMain.load(std::memory_order_relaxed)->rawMemUsed);
}
}
#endif
void BackRefMain::addToForUseList(BackRefBlock *bl)
{
bl->nextForUse = listForUse.load(std::memory_order_relaxed);
listForUse.store(bl, std::memory_order_relaxed);
bl->addedToForUse.store(true, std::memory_order_relaxed);
}
void BackRefMain::initEmptyBackRefBlock(BackRefBlock *newBl)
{
intptr_t nextLU = lastUsed+1;
new (newBl) BackRefBlock(newBl, nextLU);
MALLOC_ASSERT(nextLU < dataSz, NULL);
backRefBl[nextLU] = newBl;
lastUsed.store(nextLU, std::memory_order_release);
}
bool BackRefMain::requestNewSpace()
{
bool isRawMemUsed;
static_assert(!(blockSpaceSize % BackRefBlock::bytes),
"Must request space for whole number of blocks.");
if (backRefMain.load(std::memory_order_relaxed)->dataSz <= lastUsed + 1) return false;
MallocMutex::scoped_lock newSpaceLock(requestNewSpaceMutex);
if (listForUse.load(std::memory_order_relaxed)) return true;
BackRefBlock *newBl = (BackRefBlock*)backend->getBackRefSpace(blockSpaceSize, &isRawMemUsed);
if (!newBl) return false;
for (BackRefBlock *bl = newBl; (uintptr_t)bl < (uintptr_t)newBl + blockSpaceSize;
bl = (BackRefBlock*)((uintptr_t)bl + BackRefBlock::bytes)) {
bl->zeroSet();
}
MallocMutex::scoped_lock lock(mainMutex);
const size_t numOfUnusedIdxs = backRefMain.load(std::memory_order_relaxed)->dataSz - lastUsed - 1;
if (numOfUnusedIdxs <= 0) { backend->putBackRefSpace(newBl, blockSpaceSize, isRawMemUsed);
return false;
}
int blocksToUse = min(numOfUnusedIdxs, blockSpaceSize / BackRefBlock::bytes);
if (isRawMemUsed) {
newBl->nextRawMemBlock = backRefMain.load(std::memory_order_relaxed)->allRawMemBlocks;
backRefMain.load(std::memory_order_relaxed)->allRawMemBlocks = newBl;
}
for (BackRefBlock *bl = newBl; blocksToUse>0; bl = (BackRefBlock*)((uintptr_t)bl + BackRefBlock::bytes), blocksToUse--) {
initEmptyBackRefBlock(bl);
if (active.load(std::memory_order_relaxed)->allocatedCount.load(std::memory_order_relaxed) == BR_MAX_CNT) {
active.store(bl, std::memory_order_release); } else {
addToForUseList(bl);
}
}
return true;
}
BackRefBlock *BackRefMain::findFreeBlock()
{
BackRefBlock* active_block = active.load(std::memory_order_acquire);
MALLOC_ASSERT(active_block, ASSERT_TEXT);
if (active_block->allocatedCount.load(std::memory_order_relaxed) < BR_MAX_CNT)
return active_block;
if (listForUse.load(std::memory_order_relaxed)) { MallocMutex::scoped_lock lock(mainMutex);
if (active_block->allocatedCount.load(std::memory_order_relaxed) == BR_MAX_CNT) {
active_block = listForUse.load(std::memory_order_relaxed);
if (active_block) {
active.store(active_block, std::memory_order_release);
listForUse.store(active_block->nextForUse, std::memory_order_relaxed);
MALLOC_ASSERT(active_block->addedToForUse.load(std::memory_order_relaxed), ASSERT_TEXT);
active_block->addedToForUse.store(false, std::memory_order_relaxed);
}
}
} else if (!requestNewSpace())
return NULL;
return active.load(std::memory_order_acquire); }
void *getBackRef(BackRefIdx backRefIdx)
{
if (!(backRefMain.load(std::memory_order_acquire))
|| backRefIdx.getMain() > (backRefMain.load(std::memory_order_relaxed)->lastUsed.load(std::memory_order_acquire))
|| backRefIdx.getOffset() >= BR_MAX_CNT)
{
return NULL;
}
std::atomic<void*>& backRefEntry = *(std::atomic<void*>*)(
(uintptr_t)backRefMain.load(std::memory_order_relaxed)->backRefBl[backRefIdx.getMain()]
+ sizeof(BackRefBlock) + backRefIdx.getOffset() * sizeof(std::atomic<void*>)
);
return backRefEntry.load(std::memory_order_relaxed);
}
void setBackRef(BackRefIdx backRefIdx, void *newPtr)
{
MALLOC_ASSERT(backRefIdx.getMain()<=backRefMain.load(std::memory_order_relaxed)->lastUsed.load(std::memory_order_relaxed)
&& backRefIdx.getOffset()<BR_MAX_CNT, ASSERT_TEXT);
((std::atomic<void*>*)((uintptr_t)backRefMain.load(std::memory_order_relaxed)->backRefBl[backRefIdx.getMain()]
+ sizeof(BackRefBlock) + backRefIdx.getOffset() * sizeof(void*)))->store(newPtr, std::memory_order_relaxed);
}
BackRefIdx BackRefIdx::newBackRef(bool largeObj)
{
BackRefBlock *blockToUse;
void **toUse;
BackRefIdx res;
bool lastBlockFirstUsed = false;
do {
MALLOC_ASSERT(backRefMain.load(std::memory_order_relaxed), ASSERT_TEXT);
blockToUse = backRefMain.load(std::memory_order_relaxed)->findFreeBlock();
if (!blockToUse)
return BackRefIdx();
toUse = NULL;
{ MallocMutex::scoped_lock lock(blockToUse->blockMutex);
if (blockToUse->freeList) {
toUse = (void**)blockToUse->freeList;
blockToUse->freeList = blockToUse->freeList->next;
MALLOC_ASSERT(!blockToUse->freeList ||
((uintptr_t)blockToUse->freeList>=(uintptr_t)blockToUse
&& (uintptr_t)blockToUse->freeList <
(uintptr_t)blockToUse + slabSize), ASSERT_TEXT);
} else if (blockToUse->allocatedCount.load(std::memory_order_relaxed) < BR_MAX_CNT) {
toUse = (void**)blockToUse->bumpPtr;
blockToUse->bumpPtr =
(FreeObject*)((uintptr_t)blockToUse->bumpPtr - sizeof(void*));
if (blockToUse->allocatedCount.load(std::memory_order_relaxed) == BR_MAX_CNT-1) {
MALLOC_ASSERT((uintptr_t)blockToUse->bumpPtr
< (uintptr_t)blockToUse+sizeof(BackRefBlock),
ASSERT_TEXT);
blockToUse->bumpPtr = NULL;
}
}
if (toUse) {
if (!blockToUse->allocatedCount.load(std::memory_order_relaxed) &&
!backRefMain.load(std::memory_order_relaxed)->listForUse.load(std::memory_order_relaxed)) {
lastBlockFirstUsed = true;
}
blockToUse->allocatedCount.store(blockToUse->allocatedCount.load(std::memory_order_relaxed) + 1, std::memory_order_relaxed);
}
} } while (!toUse);
if (lastBlockFirstUsed)
backRefMain.load(std::memory_order_relaxed)->requestNewSpace();
res.main = blockToUse->myNum;
uintptr_t offset =
((uintptr_t)toUse - ((uintptr_t)blockToUse + sizeof(BackRefBlock)))/sizeof(void*);
MALLOC_ASSERT(!(offset >> 15), ASSERT_TEXT);
res.offset = offset;
if (largeObj) res.largeObj = largeObj;
return res;
}
void removeBackRef(BackRefIdx backRefIdx)
{
MALLOC_ASSERT(!backRefIdx.isInvalid(), ASSERT_TEXT);
MALLOC_ASSERT(backRefIdx.getMain()<=backRefMain.load(std::memory_order_relaxed)->lastUsed.load(std::memory_order_relaxed)
&& backRefIdx.getOffset()<BR_MAX_CNT, ASSERT_TEXT);
BackRefBlock *currBlock = backRefMain.load(std::memory_order_relaxed)->backRefBl[backRefIdx.getMain()];
std::atomic<void*>& backRefEntry = *(std::atomic<void*>*)((uintptr_t)currBlock + sizeof(BackRefBlock)
+ backRefIdx.getOffset()*sizeof(std::atomic<void*>));
MALLOC_ASSERT(((uintptr_t)&backRefEntry >(uintptr_t)currBlock &&
(uintptr_t)&backRefEntry <(uintptr_t)currBlock + slabSize), ASSERT_TEXT);
{
MallocMutex::scoped_lock lock(currBlock->blockMutex);
backRefEntry.store(currBlock->freeList, std::memory_order_relaxed);
#if MALLOC_DEBUG
uintptr_t backRefEntryValue = (uintptr_t)backRefEntry.load(std::memory_order_relaxed);
MALLOC_ASSERT(!backRefEntryValue ||
(backRefEntryValue > (uintptr_t)currBlock
&& backRefEntryValue < (uintptr_t)currBlock + slabSize), ASSERT_TEXT);
#endif
currBlock->freeList = (FreeObject*)&backRefEntry;
currBlock->allocatedCount.store(currBlock->allocatedCount.load(std::memory_order_relaxed)-1, std::memory_order_relaxed);
}
if (!currBlock->addedToForUse.load(std::memory_order_relaxed) &&
currBlock!=backRefMain.load(std::memory_order_relaxed)->active.load(std::memory_order_relaxed)) {
MallocMutex::scoped_lock lock(mainMutex);
if (!currBlock->addedToForUse.load(std::memory_order_relaxed) &&
currBlock!=backRefMain.load(std::memory_order_relaxed)->active.load(std::memory_order_relaxed))
backRefMain.load(std::memory_order_relaxed)->addToForUseList(currBlock);
}
}
} }