設計を再考した後、水田からの入力により、このようなものを思いつきましたが、それが正しいかどうか疑問に思います。実行すると問題ないようです...アイデアは、事前に割り当てられたオブジェクトが次から継承されることです:
struct Node
{
void* pool;
};
このようにして、割り当てられたすべてのオブジェクトに、後で解放するためにそのプールへのポインターを注入します。次に、次のようになります。
template<class T, int thesize>
struct MemPool
{
T* getNext();
void free(T* ptr);
struct ThreadLocalMemPool
{
T* getNextTL();
void freeTL();
int size;
vector<T*> buffer;
vector<int> freeList;
int freeListIdx;
int bufferIdx;
ThreadLocalMemPool* nextTlPool; //within a thread's context a linked list
};
int size;
threadlocal ThreadLocalMemPool* tlPool; //one of these per thread
};
つまり、基本的に私が言うMemPool<Cat, 100>
と、スレッドごとにスレッドgetNexts
ローカルのmempoolをインスタンス化するmempoolを提供します。サイズは、モジュロを簡単にするために内部で最も近い 2 のべき乗に丸められます (簡単にするために省略します)。は各スレッドにローカルであるためgetNext()
、ロックは必要ありません。次のように、解放部分にアトミックを使用しようとしています。
T* ThreadLocalMemPool::getNextTL()
{
int iHead = ++bufferIdx % size;
int iTail = freeListIdx % size;
if (iHead != iTail) // If head reaches tail, the free list is empty.
{
int & idx = freeList[iHead];
while (idx == DIRTY) {}
return buffer[idx];
}
else
{
bufferIdx--; //we will recheck next time
if (nextTLPool)
return nextTLPool->getNextTL();
else
//set nextTLPool to a new ThreadLocalMemPool and return getNextTL() from it..
}
}
void ThreadLocalMemPool::free(T* ptr)
{
//the outer struct handles calling this in the right ThreadLocalMemPool
//we compute the index in the pool from which this pool came from by subtracting from
//its address the address of the first pointer in this guys buffer
int idx = computeAsInComment(ptr);
int oldListIdx = atomic_increment_returns_old_value(freeListIdx);
freeList[oldListIdx % size] = idx;
}
さて、アイデアは、割り当てた以上に解放することはできないため(正しい使用法を想定しています)、freeListIdx
常にプール内の後ろをたどるということです。bufferIdx
free への呼び出しは、バッファ インデックスをフリー リストに返す順序を同期し、getNext はサイクル バックするときにこれを取得します。私はそれについて少し考えてきましたが、論理に意味的に間違っているものは何も見当たりません。