アセンブラを使わないallocaの独自実装 (win32用)

そう。allocaの独自実装を考えていたことがあった。だが、C言語のみで実装するのはメモリハンドル管理の面でとても面倒だと思っていたが、STLを使うと以下のように書けるとRBtreeのC言語実装を知る*1一年ほど前から思っていた。
なんか、STLのオブジェクトをグローバルな領域に置くのがOOP的に美しくないと当時は思っており、この方法でソフトウェアを実装することは無かったが、今となってはもうどうでもよい。


さて、何故、今更alloca()なのかというと
http://d.hatena.ne.jp/y-hamigaki/20060807
vorbisencとalloca() にて
alloca()の実装が各コンパイラ間によって違うという事実を知ったからだ。
それから昔考えていた事(もしくは書いていたこと)を思い出した。*2
しかし、GetCurrentThreadId()の使い方が間違っていないか心配である。
GetCurrentThreadId()の戻り値が各スレッド毎にユニークであるという前提に以下のプログラムは書かれているからだ。


以下の実装は本家のalloca()のようにスコープアウト時にメモリを解放しないので沢山のスレッドから大量のメモリ確保命令が来てしまうとメモリスワップが発生して使い物にならなくなるだろう。


#include <windows.h>
#include "dkc_alloca.h"
#include <map>



struct ALLOCA_DATA{
void *pData;
size_t size;
};

typedef std::map<DWORD,ALLOCA_DATA> MAP_T;


static MAP_T gMap;

static bool setAllocaData(ALLOCA_DATA *p,size_t asize){
void *pt = malloc(asize);
if(NULL==pt) return false;
p->pData = pt;
p->size = asize;
return true;
}
static bool resetAllocaData(ALLOCA_DATA *p,size_t asize){
void *pt = realloc(p->pData,asize);
if(NULL==pt) return false;
p->pData = pt;
p->size = asize;
return true;
}
static bool freeAllocaData(ALLOCA_DATA *p){
if(NULL==p || !p->pData) return false;
free(p->pData);
}
void *dkc_alloca(size_t size){
ALLOCA_DATA data;
DWORD id = GetCurrentThreadId();
MAP_T::iterator it = gMap.find(id);
if(it == gMap.end())
{
if(false==setAllocaData(&data,size))
return NULL;
gMap.insert(MAP_T::value_type(id,data));
}else{
if(false==resetAllocaData(&( (*it).second),size))
return NULL;
}
return data.pData;
}

int dkc_alloca_free()
{
DWORD id = GetCurrentThreadId();
MAP_T::iterator it = gMap.find(id);
if(it == gMap.end())
{
return -1;
}
//以下 synchronizedのはずだが
//Current threadからしか呼ばれないので要らない
freeAllocaData(&(*it).second);
gMap.erase(it);

/*
もしくは
void *p = (*it).second.pData;
gMap.erase(it);
free(p);か?
*/

return 0;
}
void dkc_alloca_all_free()
{
//以下 synchronized 省略するが^^;
MAP_T::iterator it = gMap.begin();
for(;it != gMap.end();it++)
{
freeAllocaData(&(*it).second);
}
gMap.clear();
}


#define TEST_DKC_ALLOCA
#ifdef TEST_DKC_ALLOCA

//dkutil_c使います。
#include <dkutil_c/dkc.h>
int main(){
dkcCheckMemoryLeak(TRUE);
//atexitで登録してあげる
atexit(dkc_alloca_all_free);

//後はご自由に
dkc_alloca(12);


dkc_alloca(1024);

//メモリリークしないなり^^

}

#endif

追記:注意:上記のプログラムには問題が多数含まれていますので実際に使用しないで下さい。

*1:http://d.hatena.ne.jp/studiokingyo/20050321
修正版は http://d.hatena.ne.jp/studiokingyo/20051206

*2:確か、このアイディアは某ブログに掲載されていた覚えがあるのだが・・・