Scalable Memory Allocator in Windows with MFC

Scalable Memory Allocator in Windows with MFC

Hi,

I have recentely changed our application over to use the TBB Scalable Memory Allocator in our Windows application, which is written in Visual Studio 2005. It has been working fine for the last couple weeks and greatly sped up some operations in our software. However,we just discovered a new crash that is related to the change over to the TBB memory allocation. FYI, I replaced the new and deleteexactly the sameway as described in the book.

The crash is to do with doing a new on a MFC CPen class and later when performing the delete on the CPen class it may crash. The crash is occuring becuase ofa mis-match using the replacement new with the 'scalable_malloc' but it is not using the replacment delete, but is instead using the windowsfree function for some reason. Other MFC classes like classes inherited from CWnd work fine however.

The code for the creation of the CPen is:

tempPen = new CPen(PS_SOLID, 1, mfcColour);

The code for the deletion is:

delete tempPen;

Lookingat the diss-assembly and steeping through the assembly I can see that the new is callingmy replacement new funtion. However, the dissassembly for the delete is as follows:

CPen::`vector deleting destructor':
783A3D84 push ebx
783A3D85 mov bl,byte ptr [esp+8]
783A3D89 test bl,2
783A3D8C push esi
783A3D8D mov esi,ecx
783A3D8F je CPen::`vector deleting destructor'+32h (783A3DB6h)
783A3D91 push edi
783A3D92 push offset CPen::~CPen (7833913Bh)
783A3D97 lea edi,[esi-4]
783A3D9A push dword ptr [edi]
783A3D9C push 8
783A3D9E push esi
783A3D9F call `eh vector destructor iterator' (783A508Ah)
783A3DA4 test bl,1
783A3DA7 je CPen::`vector deleting destructor'+2Dh (783A3DB1h)
783A3DA9 push edi
783A3DAA call dword ptr [__imp__free (782E15A8h)]
783A3DB0 pop ecx
783A3DB1 mov eax,edi
783A3DB3 pop edi
783A3DB4 jmp&nbs
p; CPen::`vector deleting destructor'+4Ch (783A3DD0h)
783A3DB6 mov dword ptr [esi],offset CPen::`vftable' (782F0160h)
783A3DBC call CGdiObject::~CGdiObject (78319F15h)
783A3DC1 test bl,1
783A3DC4 je CPen::`vector deleting destructor'+4Ah (783A3DCEh)
783A3DC6 push esi
783A3DC7 call dword ptr [__imp__free (782E15A8h)]
783A3DCD pop ecx
783A3DCE mov eax,esi
783A3DD0 pop esi
783A3DD1 pop ebx
783A3DD2 ret 4

As can be seen from this line '783A3DC7 call dword ptr [__imp__free (782E15A8h)] ' it is calling the normal free function causing a mis-match and sometimes causing an exception. I am not sure why it is not calling the replacment delete function like other parts of the code.

I was wondering if anybody else has encountered this issue and any possible work arounds. I did find this Microsoft web page which I believe is my problem:

http://support.microsoft.com/kb/q122675/

My only work around I can think of is to remove my new/delete replacements so that new and delete fall back to the generic Visual Studio new and deletes. I could fx this issue with CPen using solution 2 outlined in the link but I am worried there may be other cases that I do not know about yet.

Luckily we have our own smart pointer implementation whereour core classes for our applicationinherit from a specific base parent class. The majority of our new/deletes in the code involve these classes. So I am thinking of overloading the operator new/delete for them and only having them use the TBB Memory Allocation.

Thanks for any help,

Adam Dokter

5 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

Hi Adam,

indeed what you see is a problem in MFC. I reproduced it, and then I found a couple of links that shed some more light:

http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=3303265&SiteID=1

http://www.gamedev.net/community/forums/viewreply.asp?ID=2934245

I would choose the workaround #3 in the KB article you referenced, i.e. making a derivative class from CPen, and might be even overloading new and delete operators for this new class.

By the way, not only CPen but other CObject derivatives have the same issue as well. I experimented with CBrush and found that it does not suffer from this issue if constructed by default (i.e. with new CBrush;) but when constructed with arguments (e.g. new CBrush(0,0)), it exposes the same issue. And in the first case, destructor for CBrush was somehow instantiated in the main module (as shown by debugger) so the problem disappeared - but not in the second case.

Hi Alexey,

Thanks for the reply. Sorry, I meant I could usework around#3 in the KB article not #2 (typed the wrong number). The problem as you found also is that it is not just CPen that could cause this issue, but any other MFC class.This makes it hard to implement solution#3 as this is going into our existing product, and I would have to find each class that needs to be wrapped that we are using from MFC. And remember to do this in the future with new classes.

Ihave implemented the over-ride of new and delete on our own base class that our classes use and this is working. As these are the classes that we use the most in the system and are the ones used in different threads this has still given us the speed boost that doing a global replace of new and delete gave us but is much safer as it is only working on our classes.

Also as a note, from the KB article I supplied, it appears this issue occurs with any 3rd party DLL that you may use (not just MFC). Which makes it dangerous to do a global replace of the new and delete if using any 3rd party DLL's that exposes objects you can new and delete.

Regards,

Adam

Dok:Also as a note, from the KB article I supplied, it appears this issue occurs with any 3rd party DLL that you may use (not just MFC). Which makes it dangerous to do a global replace of the new and delete if using any 3rd party DLL's that exposes objects you can new and delete.

I think the problem is not that bad. The KB article you referenced is only relevant to really old MSVC versions (see the APPLIES TO list there) so I suppose modern versions should not have this issue. MFC, however, has a bug (as mentioned in one of the discussions I provided the links to) that it uses ::operator new to allocate objects, and free to deallocate. Here is the corresponding source code:

void* PASCAL CObject::operator new(size_t nSize)
{
#ifdef _AFX_NO_DEBUG_CRT
return ::operator new(nSize);
#else
return ::operator new(nSize, _AFX_CLIENT_BLOCK, NULL, 0);
#endif // _AFX_NO_DEBUG_CRT
}
void PASCAL CObject::operator delete(void* p)
{
#ifdef _AFX_NO_DEBUG_CRT
free(p);
#else
_free_dbg(p, _AFX_CLIENT_BLOCK);
#endif
}

For a 3rd party DLL exposing objects thatare allocated with global new and deallocated with global delete, and in the assumption thatcompiler-generated helper functions call operator delete properly as mandated by the standard, the problems may only arise if both the DLL and your application replace global new and delete. The standard explicitly says that "A C + + program shall provide at most one definition of a replaceable allocation or deallocation function. Any such function definition replaces the default version provided in the library". This is one of the reasons we do not provide replacement forms of new and delete with the TBB scalable allocator.

So if the 3rd party DLL does not provide its own replacement for global new and delete (which I think is a bad idea due to the restriction above), you should be fine using yours, assuming the compiler and linker do proper job of replacing the default operators with your version, as mandated by the standard.

Thanks, for clearing that up. The GameDev website was down two days ago soI was unable to read it back then. That with your explanation clears up what the exact issue is.

Regards,

Adam

Leave a Comment

Please sign in to add a comment. Not a member? Join today