|
Neohapsis is currently accepting applications for employment. For more information, please visit our website www.neohapsis.com or email hr@neohapsis.com |
From: Dan Longley (dlongley
EXCHANGE.MICROSOFT.COM)Date: Wed Dec 19 2001 - 05:53:55 CST
First a little about COM and module refcounting:
The "module reference count" affected by LoadLibrary and FreeLibrary is
separate from a DLL-wide module reference count that you may employ for
the sake of implementing DllCanUnloadNow. (It is at a lower layer so to
speak.)
COM will call LoadLibrary when it first caches a handle to your DLL
after a call to CoGetClassObject, CoCreateInstance, etc. COM will call
FreeLibrary on your DLL after it's determined that it's done with your
DLL. It will not do a double-FreeLibrary on your DLL.
Your DLL is only unloaded when the balancing call to FreeLibrary is
made; this is independent of COM (COM may or may not be the last client
of your DLL to call FreeLibrary).
Events which may trigger COM to call FreeLibrary on your DLL include:
- an explicit call to CoFreeUnusedLibraries
- an apartment hosting and instance of an object from your DLL being
uninitialized
- process termination
The way COM's module unloading logic works is not as simple as just
calling DllCanUnloadNow, because of an inherent race condition due to
the design of the API. When COM is asked to unload a DLL, it calls
DllCanUnloadNow. A return value of S_FALSE guarantees that the module
will not be unloaded. If S_OK is returned, and COM has not already
started a timer, COM starts a 10 minute timer. If the timer was already
started and the 10 minutes have expired, COM calls FreeLibrary on the
DLL. Any calls to CoGetClassObject/CoCreateInstance, etc, reset that
timer.
Now, about your specific problem:
That 12 minute delay is likely related to the 10 minute COM class cache
unloading delay. Since your DLL is being unloaded at the end of the long
delay, that suggests that COM is the final client calling FreeLibrary,
as opposed to one of your GetProcAddress-style clients making the
unloading call to FreeLibrary.
It would be good to know what specifically caused the crash. The crash
is most likely due to an unhandled AV after your DLL was unmapped,
probably when a stranded thread attempted to fetch an instruction from
an inaccessible memory location where your DLL used to be. Next time
your app crashes, attach a debugger and look at the thread that
generated the exception. From its call stack you should be able to
distinguish whether the thread entered your module through a COM entry
point or one of your explicit GetProcAddress entry points.
If it is a thread from a COM entry point, your refcounting problem is in
the refcount you use to implement DllCanUnloadNow. (Technically it could
also be an explicit double-FreeLibrary on your part, but that seems
unlikely given the 12 minute time scale.) It is virtually impossible* to
eliminate the race condition inherent in the
DllCanUnloadNow/CoFreeUnusedLibraries architecture -- and any code your
module places after a call that decrements your module wide reference
count makes it more likely to become a victim of that race. In
particular, in any public Release methods you implement, you should not
be attempting to acquire any locks or perform any other potentially
blocking operations between the time you decrement the module refcount
and the time you return to the caller. For example, if you are using
ATL, be aware of the fact that in the case of some templates, such as
CComObject, because of the C++ destruction sequence, the module refcount
will be decremented (_Module.Unlock()) before your own class's
destructor is called. Basically, think of it this way: from the moment a
thread executes the code in your DLL to decrement the module refcount to
zero, it is in a race to get to the RET instruction to leave your DLL
before a 10 minute timer expires.
On the other hand, if the thread that AVed in your code was inside one
of your non-COM entry points, then your refcounting problem is in the
LoadLibrary/FreeLibrary refcount, most likely due to your code
accidentally calling FreeLibrary too soon. If this is the case, you
should re-evaluate your decisions about when to call FreeLibrary for the
sake of your GetProcAddress-based calls.
If you still need some more hints as to the cause of the premature
unload, try running your app with a debugger attached and a breakpoint
set for when a module unloads. (If your debugger does not support breaks
on module unload try putting a breakpoint on ntdll!ZwUnmapViewOfSection.
It will be called with the address of the module being unloaded as a
parameter.) When the debugger traps due to your DLL being unloaded, have
a look at the call stack to see what made the call to FreeLibrary --
either COM (ole32.dll) or through your own code. Also check the value of
the module refcount that you use to implement DllCanUnloadNow. And keep
an eye out for any difference between the time your DLL is unloaded and
the time the crash actually occurs.
You can also put breakpoints on kernel32!LoadLibraryA and
kernel32!FreeLibrary to monitor specific load/free activity on your DLL.
So, try and get a little information with a debugger. I'd venture a
guess that during that 12 minute wait, you'd already called FreeLibrary
from your own code, and your module refcount was zero the entire time
while you still had some code for your COM objects lined up to be
executed in response to action from the VB dialog.
Hope this helps!
D
*It's not entirely impossible to get around the race condition... you
can thunk some code onto the stack to decrement the module refcount and
RET from there :-).
> -----Original Message-----
> From: Grissom, Ed [mailto:egrissom
ZIIMAGING.COM]
> Sent: 18 December 2001 21:26
> To: DCOM
DISCUSS.MICROSOFT.COM
> Subject: [DCOM] Module Reference Count
>
>
> DCOMers -
>
> The documentation for LoadLibrary and FreeLibrary mention a "module
> reference count" that is incremented or decremented
> appropriately when these
> functions are called.
>
> How does this relate to COM reference counts and DllCanUnloadNow() ?
>
> My guess is that they are basically unrelated, except that if the COM
> reference count goes to zero, and DllCanUnloadNow() returns S_OK, then
> FreeLibrary is called by COM which decrements the "module
> reference count".
> If this "module reference count" is now zero, then the dll is
> unloaded.
>
> Is my understanding essentially correct ?
>
> If LoadLibrary is used explicitly by a client, will the DLL
> stay loaded
> after all references are Release(d) and DllCanUnloadNow()
> returns S_OK ?
>
>
>
>
> We are trying to unravel a bug and are in the midst of some
> finger-pointing
> that some clarity on this issue might resolve.
>
> Basically, we have a server DLL that not only contains
> several COM objects,
> but also some non-COM functions that are accessed thru
> LoadLibrary/GetProcAddress. All the COM objects seem to
> correctly handle
> the global DLL refcount (used by DllCanUnloadNow) as well as their own
> refcount (currently verified only by code inspection). The
> GetProcAddress
> functions do not mess with either object refcounts or the global DLL
> refcount.
>
> The problem comes about when a COM-client of the server
> described above uses
> the LoadLibrary/GetProcAddress to get and use one of the
> non-COM functions.
> The process then runs for quite a while (hours) doing mostly
> file I/O. When
> it is done, it pops up a VB dialog with the client (an
> ActiveX control)
> embedded on it. If this form sits on the users screen
> untouched for 12
> minutes, the app crashes. If the user gets to the form before the 12
> minutes are up, the process can continue and eventually completes
> successfully.
>
> If we modify the server so that DllCanUnloadNow always
> returns S_FALSE, the
> form can stay up for hours and everything works properly,
> i.e. we never see
> the problem.
>
> However, this "solution" is causing other problems, and we
> would like to
> have DllCanUnloadNow function in the proper way.
>
>
> FYI: The server is an explorer namespace extension, and the
> fact that it
> never unloads makes setup a nightmare. The first install
> goes fine, but
> after that, the dll is always in use, so it cannot be
> updated. Even after
> rebooting, explorer comes up and loads the dll before the
> "RunOnce" copying
> is done, so even a reboot does not solve the setup problem.
>
>
> Thanks for any thoughts, advice or pointers to more info...
>
>
> --
> ed grissom
> egrissom
ziimaging.com
>
> ----------------------------------------------------------------
> Users Guide http://discuss.microsoft.com/archives/mailfaq.asp
> contains important info. Save time, search the archives at
> http://discuss.microsoft.com/archives/index.html .
> To unsubscribe, mailto:DCOM-signoff-request
DISCUSS.MICROSOFT.COM
>
----------------------------------------------------------------
Users Guide http://discuss.microsoft.com/archives/mailfaq.asp
contains important info. Save time, search the archives at
http://discuss.microsoft.com/archives/index.html .
To unsubscribe, mailto:DCOM-signoff-request
DISCUSS.MICROSOFT.COM
----------------------------------------------------------------
Users Guide http://discuss.microsoft.com/archives/mailfaq.asp
contains important info. Save time, search the archives at
http://discuss.microsoft.com/archives/index.html .
To unsubscribe, mailto:DCOM-signoff-request
DISCUSS.MICROSOFT.COM
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]