[omniORB] compiling omniORB3 on NT -- Assertion failure

Steven W. Brenneis brennes1@rjrt.com
Fri, 29 Oct 1999 15:52:43 -0400


Ji-Yong,

There is a basic flaw in one of you assumptions:

There is no requirement for heap allocated within a DLL to be freed
within the same DLL.  The only assertions in the debug runtime delete
overload are to check that the block is valid, i.e. that the no-man's
land has the proper initialization pattern, and that the heap block size
is valid, i.e. the heap pointer size value equals the stored heap block
size.  I am reasonably sure that process heap is the same for the DLL
and the process.  When a DLL is connected to a process, the operating
system simply maps the DLL's code and global variables into the process'
virtual address space. From this point onward, the process no longer is
aware that it is executing DLL or local code.  You can verify this by
using the CMemoryState MFC class and check the heap size before and
after a DLL allocates memory.

If the rule you assert was true, MFC would not function.  Many of the
MFC classes, particularly the Frame Window and its derivations, require
the user to invoke a static member function to acquire a pointer to one
of these classes.  These static member functions perform the heap
allocation within the MFC DLL.  The returned pointer may then be deleted
at the will of the programmer.

Without knowing the exact assertion failure you are getting, I can only
guess at the problem, but CrtBlock assertions are almost invariably
caused by pointer bounding problems or double deletion attempts.  In
particular, the std::string class had a nasty bug in MSVC 5.0 that would
cause this to occur on a sporadic basis.  PJ Plauger posted the fix on
his website but I am not sure whether Microsoft incorporated it in MSVC
6.0.

Steve Brenneis

Ji-Yong D. Chung wrote:
> 
>     Library Involved:  omniORB3_rtd.dll (dll version of orbcore library) and
> omniNames.exe
>     Platform: NT4.0 (SP4) MSVC++
> 
>     As I indicated in my previous email messages, I have built a debug
> versions of all the executables and dll's.  When I run my debug version of
> omniNames.exe and omniORB3, the program dies, with an assertion failure
> 
>     I wish this would be "fixed" -- as it would make future debugging
> easier.  This problem, by the way, probably will NOT show up in non-debug
> version of omniORB3 and omniNames.  I will explain later why it is desirable
> to fix this, even though the problem may not show up in the release version.
> .
> 
>     The assertion failure happens in omniNames::init(CORBA:: ...) which is
> in log.cc (see omniNames files).
> 
> void
> omniNameslog::init(CORBA::ORB_ptr the_orb, PortableServer::POA_ptr the_poa)
> {
>     ...
>     // stuff
> 
>       {
>  CORBA::Object_var ref = poa->create_reference(
>         CosNaming::NamingContext::_PD_repoId);
>  PortableServer::ObjectId_var refid = poa->reference_to_id(ref);
> 
>  putCreate(refid, logf);
>       }                                    <=== ASSERTION FAILURE #1
> 
>     // more stuff
> 
>   _omni_set_NameService(rootContext);
>   delete p;                           <==== ASSERTION FAILURE #2
> 
>     // stuff
> }
> 
>       The first assertion failure happens just after the execution of
> putCreate(refid, logf).  If one looks at the code in that "area," they are
> locally scoped within a pair of braces.
> 
>     Just after the execution of putCreate(refid, logf), as the execution
> point is about to exit the locally scoped area, MSVC debugger checks if it
> can delete   refid   .  Before it deletes the variable, however, it first
> attempts to validate the wholesomeness of the memory which it is about to
> de-allocate.  When the debugger finds that the memory pointer is not what it
> should be, it generates an assertion failure.  And the program stops
> executing.
> 
>     The assertion failure is made within MSVC delete operator (debug
> version).  This operator happens to check WHETHER the memory which is about
> to be de-allocated was originally allocated FROM the heap of the executing
> module (local heap, that is).  Note that this heap (for omniNames.exe) is
> not the same as the heap of omniORB3_rtd.dll.
> 
>     In the above code, the allocation of memory occurs inside   poa ->
> reference_to_id(...)
> procedure.  This eventually calls omniORB3_rtd.dll's memory allocation
> routines.  Therefore, the memory is from the heap of omniORB3_rtd.dll, NOT
> from the local heap.
> 
>      Later, refid will be deleted as the execution point moves outside the
> braced area.  But the deletion operators that are invoked at this point are
> LOCAL to omniNames.  The debug version of these delete operators expect the
> de-allocating memory to be from the local heap; but it is not (it is from
> omniORB3_rtd.dll's heap).  Thus the assertion failure.
> 
>     The reason why the deletion operators are local to omniNames is simple:
> omniNames, when it gets built, includes  poa.h.  This  file contains inline
> functions that allocates memory and de-allocates memory.  These inline
> function get incorporated into omniNames as  local functions.  If the
> execution of the above code actually called the delete operators from
> omniORB3_rtd.dll, since the calling delete operator's local heap is the same
> as that of omniORB3_rtd.dll, there would not be assertion failures.
> 
>     In other words, if one were to remove "inline" specifiers" from
> constructors/destructors/member functions of ObjectID_var, ObjectId_out, and
> ObjectId_var, (all inside poa.cc) the assertion error above would go away.
> Of course, poa.cc now would need to contain the definition of those
> declarations in poa.h
> 
> ----------------------------------------------------------------------------
> -------------------
> 
>     There are other memory assertion failures that occur for the same
> reasons described above.  To repeat, these assertions occur because of
> inline specifiers in header files that are present in both exporting and
> importing dll/libraries.
> 
>     It is good idea to remove the inline specifier for these functions and
> to move their definitions into implementation files.  There are two good
> reasons for doing this.  First, the efficiency gain from saving few function
> calls are generally are overshadowed by memory allocation/de-allocation
> operations.  The memory allocation and de-allocation operations are about
> 500 times more expensive than a single machine instruction (of course, a
> function call is also more expensive than a single instruction).  The point
> here is that the gain in speed is not as great as one might expect.
> 
>     The second reason is much more important: these inlined functions that
> cross the DLL boundaries make debugging just pure hell --- one dll starts
> deleting objects and stuff from another heap, and one cannot trace the
> source of error.  As you might expect, debugging omniORB3 may take months
> (or perhaps longer).  During all these debugging sessions, one really needs
> to be able to narrow down the potential source of problems.