BREWのメモリ管理(3)
なぜ、前回のプログラムでエラーが発生したのかというと、
void* operator new( size_t size ){ return CApplet::getMemoryManager()->operator_new( size ); } void operator delete( void* ptr ){ CApplet::getMemoryManager()->operator_delete( ptr ); }
この部分で、CAppletに依存している部分があるからです。
自分の場合、IModuleとIAppletについては、AEEModGen.cやAEEAppGen.cを使わずに自分で実装しているので、CAppletが完成する前にnewを使っているのです。
そのため、CApplet::getApplet()を使用しても、不正なポインタしか返さないため、newをすると、アクセスバイオレーションを起こしてしまいます。
これをどうするか、しばらく考えてみました。
まず思いついたのが、CAppletが完成するまでは、newを使用せずにMALLOCで確保する方法。
CAppletを生成するまでにMALLOCで確保したメモリが、FREEで確実に解放されるという保証さえ出来ればいけると思います。
そして、CAppletを生成するまでにMALLOCで確保する物は、IModuleを実装したクラス(以下CModule)、IAppletを実装したクラス(以下CApplet)、IDisplayインスタンスぐらいなので、メインフレーム側では、わざとやらない限り解放することはないと思います。
なので、この方法でうまくいくと思います。
……しかし、自分は火の中に飛び込んでしまいました(;´Д`)
なんか、IDisplayインスタンスは仕方ないとして、CModuleやCAppletがoperator new以外で確保されていることが、気持ち悪く感じたのです。
なので、何とかCModuleやCAppletも何とかしてoperator newで確保できないかと考えてみました。
CModuleの生成は、BREWのエントリーポイントであるAEEMod_Loadが呼び出す、AEEStaticMod_Newで行われています。
つまり、ここでCModuleを生成するより前にMemoryManagerを生成すればいいのです。
そして、CModuleを解放した後に、このMemoryManagerを解放すればいいのです。
しかし、そこで問題が発生します。
このMemoryManagerを指しているポインタをどこに保持しておくか、という問題です。
BREWはグローバル変数が使えないため、このポインタをどこにも保持できないのです。
一応、自己書き換えをすれば出来るのですが、それは最終手段です。
もし、今後メモリ管理が厳しくなり、メモリ保護が掛けられたりすると、非常に困るからです。
いろいろと考えていると、ふと思いつきました。
CModuleの領域にくっつけてやろう、と。
つまり、CModuleを生成するときに、memory poolからCModuleの領域+MemoryManagerポインタの領域を確保して、余分な領域にMemoryManagerへのポインタを入れてやればいいのです。
そして、CAppletを生成するときも、IModuleへのポインタが渡されるので、そこからMemoryManagerへのポインタを取得すれば、CAppletもmemory poolから生成することが出来ます。
あと、もしかしたらMALLOC()で確保したメモリでnewをしたい場合があると思うので、それ用のtemplateも作っておきます。
これをプログラムにまとめると、
// placement new void* operator new( size_t size , void* ptr ){ return ptr; } template< class T > class MemoryAllocator{ public: // 引数なし static T* create(){ void* p = MALLOC( sizeof( T ) ); if( p == null ) return null; return new( p ) T; } // 引数ひとつ template< class V1 > static T* create( V1 v1 ){ void* p = MALLOC( sizeof( T ) ); if( p == null ) return null; return new( p ) T( v1 ); } // 引数ふたつ template< class V1 , class V2 > static T* create( V1 v1 , V2 v2 ){ void* p = MALLOC( sizeof( T ) ); if( p == null ) return null; return new( p ) T( v1 , v2 ); } // 引数みっつ template< class V1 , class V2 , class V3 > static T* create( V1 v1 , V2 v2 , V3 v3 ){ void* p = MALLOC( sizeof( T ) ); if( p == null ) return null; return new( p ) T( v1 , v2 , v3 ); } // 後は必要に応じて追加汁 // 解放 static void destroy( T* ptr ){ if( ptr != null ){ ptr->~T(); FREE( ptr ); } } };
IMemoryManager* getMemoryManager( IModule* module ){ return *(IMemoryManager**))*1(; } // CModule生成部分 int CModule::StaticMod_New( int16 nSize , IShell* pIShell , void* ph , IModule** ppMod , PFNMODCREATEINST pfnMC , PFNFREEMODDATA pfnMF ){ ... // ちょっとした処理 IMemoryManager* pMemoryManager; pMemoryManager = MemoryAllocator< IMemoryManager >::create(); if( pMemoryManager == null ) return ENOMEMORY; if( pMemoryManager->init() != 0 ) return ENOMEMORY; uint32 memsize = sizeof( IMemoryManager* ); uint32 modsize = sizeof( CModule ); CModule* pModule = (CModule*)pMemoryManager->operator_new( memsize + modsize ); if( pModule != null ){ // placement new pModule = new( (void*)(((char*)pModule) + memsize) ) CModule; if( pModule->init( pIShell , pfnMC , pfnMF ) ){ // メモリマネージャをIModuleにくっつける *(IMemoryManager**)(((char*)pModule) - memsize) = pMemoryManager; *ppMod = (IModule*)pModule; return SUCCESS; } } return ENOMEMORY; } // CModule解体部分 uint32 CModule::Release( IModule* pThis ){ CModule* pModule = (CModule*)pThis; if ( --(pModule->_nRef) == 0 ){ if( pModule->_pfnModFreeData ){ pModule->_pfnModFreeData( pThis ); } IMemoryManager* manager = getMemoryManager( pModule ); // CModuleをoperator_deleteで解放 pModule->~CModule(); manager->operator_delete( (void*))*2( ); // 最後はMemoryManagerを解放して終わり MemoryAllocator< IMemoryManager >::destroy( manager ); return 0; } return pModule->_nRef; }
// CApplet生成部分 extern "C" int AEEClsCreateInstance( AEECLSID ClsId , IShell* pIShell , IModule* pIModule , void** ppObj ){ ... // ちょっとした処理 CApplet* pApplet = getMemoryManager( pIModule )->operator_new( sizeof( CApplet ) ); if( pApplet == null ) return ENOMEMORY; // placement new pApplet = new( pApplet ) CApplet( pIModule , pIShell ); ... // ちょっとした処理 } // CApplet解体部分 uint32 CApplet::Release( IApplet* pThis ){ CApplet* pApplet = (CApplet*)pThis; if ( --(pApplet->_nRef) == 0 ) { // デストラクタを呼び出して、operator_deleteで解放 IModule* pModule = pApplet->_pIModule; pApplet->~CApplet(); getMemoryManager( pModule )->operator_delete( pApplet ); //CAppletが解放された為、これ以降、deleteを使用してはいけない return 0; } return pApplet->_nRef; }
このようなります。
これで、CModuleとCAppletがmemory poolから確保されたメモリになり、メモリの確保が好きなように出来るようになります。
ただ、IShell_CreateInstanceによって生成されるインスタンスは、MemoryManagerから領域を取得するわけではないため、NULLチェックをする必要があります。
この問題については後日考えます。
で、最後に1つだけ。
これ、まだコンパイルしてません(;´Д`)
BREWで遊んでると、横から作業を割り振られたので、BREWを触る時間が無くなってしまったんですよ(;つД`)
うまくいったらまた報告します。