Re: shared DLL VS static Link, Are they different?
Giovanni Dicanio wrote:
As Joe and Doug pointed out, that library has problems with DllMain and
thread local storage, etc.
I did some stepping in the code.
The problem is that that library is a mainly-C-interface library (even if
the sources are .cpp), and there is a structure called "CvContext" in that
library.
The authors provided internal functions like icvCreateContext(),
icvDestroyContext(), and icvGetContext() - see file cxcore\src\cxerror.cpp
(where DllMain is also defined).
The problem is that this "context" management is a real mess, and if you try
to log the calls to icvCreateContext() and the calls to icvDestroyContext(),
you will see that they do not match!
I saw that inserting a custom code in icvCreateContext() and
icvDestroyContext() bodies, this custom code basically uses
OutputDebugString to trace message like "icvCreateContext - CvContext
allocated at %08x <memory address here...>").
The problem is that icvGetContext is badly designed IMHO (with some
confusing #ifdef's, too), and there is a mix of TlsGetValue(), and also
calls to icvCreateContext from icvGetContext...
There are global variables defined in that DLL, and their constructor calls
icvGetContext - you can see that using call stack:
CvType constructor calls
cvGetErrStatus calls
icvGetContext
It may happen that icvGetContext is called like shown above *before* DllMain
(because the global variables are constructed before DllMain is called).
If you put breakpoints you can see how bad and non-linear the code flow is
to get these "CvContext"s...
And if you use proper tracing, you will see that *not all* the instances of
CvContext created on the heap using malloc() are properly destroyed!
(BTW: this library is a C++ library, but uses malloc instead of new...)
In general, I think that DllMain is a poor place to do initialization and
cleanup - better providing external public functions - and if there is some
"context" state to be shared by DLL functions, better passing it explicitly
(or use a C++ library, so the "state" is inside the class body).
A couple articles about DllMain...
http://blogs.msdn.com/oleglv/archive/2003/10/24/56141.aspx
http://blogs.msdn.com/larryosterman/archive/2004/04/23/118979.aspx
HTH,
Giovanni
Thank you Giovanni.
Follow your advice, I add these code in icvCreateContext() body
//************************************************************
char msgbuf[100];
sprintf(msgbuf, "memory create here %08x \n", (void*)context);
OutputDebugString(msgbuf);
//************************************************************
add these code in icvDestroyContext() body
//************************************************************
char msgbuf[100];
sprintf(msgbuf, "memory destroyed here %08x \n", (void*)context);
OutputDebugString(msgbuf);
//************************************************************
When debug the cvcore100d.dll I found icvCreateContext() is called twice.
**First: Before DLLMain
CvType constructor calls
cvGetErrStatus calls
icvGetContext calls
icvCreateContext. ********Here!
This is before the DLLMain is entered. It seems there are some global
variable in Cvcore100d.dll, and will be initialized before DLLMain.
**Second: in DLLMain
case DLL_THREAD_ATTACH:
pContext = icvCreateContext(); ********Here!
...
But the icvDestroyContext() will be called ONCE when Exit the APP.
It's still in DLLMain
case DLL_PROCESS_DETACH:
if( g_TlsIndex != TLS_OUT_OF_INDEXES )
{
pContext = (CvContext*)TlsGetValue( g_TlsIndex );
if( pContext != NULL )
icvDestroyContext( pContext ); ********Here!
}
TlsFree( g_TlsIndex );
break;
------------------------------------------------------------------
So, I think the Memory created in global variable in DLL files will not
released, That the *CAUSE* of Memory leak. Even I download the latest
code from openCV's CVS, the problems still exit.
If I add preprocessor definition CV_DLL, there is another memory leak.
------------------------------------------------------------------
case DLL_PROCESS_DETACH:
if( g_TlsIndex != TLS_OUT_OF_INDEXES )
{
pContext = (CvContext*)TlsGetValue( g_TlsIndex );
if( pContext != NULL )
icvDestroyContext( pContext );
}
TlsFree( g_TlsIndex );
break;
-------------------------------------------------------------------
here is the code in icvGetContext()
//****************************************************************
static CvContext*
icvGetContext(void)
{
#ifdef CV_DLL
#if defined WIN32 || defined WIN64
CvContext* context;
//assert(g_TlsIndex != TLS_OUT_OF_INDEXES);
if( g_TlsIndex == TLS_OUT_OF_INDEXES )
{
g_TlsIndex = TlsAlloc();
if( g_TlsIndex == TLS_OUT_OF_INDEXES )
FatalAppExit( 0, "Only set CV_DLL for DLL usage" );
}
context = (CvContext*)TlsGetValue( g_TlsIndex );
if( !context ) ******NOTE**
{
context = icvCreateContext();
if( !context )
FatalAppExit( 0, "OpenCV. Problem to allocate memory for
TLS OpenCV context." );
TlsSetValue( g_TlsIndex, context );
}
return context;
.......
//****************************************************************
NOTE: context will always be checked in icvGetContext(), if it is NULL,
then icvCreateContext will be called, and a memory will be allocated.
Before DLL_PROCESS_ATTACH or After DLL_PROCESS_DETACH
, the TlsGetValue() will always return NULL, so, the memory will created
and never be released.
Further More, I'm not familiar with OpenCV, so, I don't know What
"CvContext" used for?