Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / .NET Framework / Component Services / November 2006

Tip: Looking for answers? Try searching our database.

ServicedComponent.Dispose(): instance not returned to pool if passed to a server app beforehand

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Toni Arnold - 11 Oct 2006 15:21 GMT
Hi

I'm unsure whether this is a new phenomena caused by some service pack or
whatever, but I recently discovered a serious problem with a
ServicedComponent running in production in this configuration since half a
year: A pooled ServicedComponent instance correctly disposed is not returned
to the pool if it beforehand was passed to another, non-pooled
ServicedComponent as a member of a method argument object. Nulling out the
member field of the object solves the problem, but I don't really like to
solve problems that simply must not exist.

Environment: VB.NET 2003 sp1, .NET 1.1 sp1, win2k

Let me explain the situation: There is an asp.net web page "asp.dll" using a
library "lib.dll" and a server-side ServicedComponent "com.dll".
There is an asp.main() method, a lib.ModelClass and two ServicedComponent
classes, com.Worker and com.Cache:

com.dll (references lib.dll)
-------
<Assembly: ApplicationActivation(ActivationOption.Server)>

<JustInTimeActivation(False), EventTrackingEnabled(True), _
Transaction(TransactionOption.NotSupported)> _
Public Class Worker
   Inherits ServicedComponent
   Public Function Calculate(ByVal pObjCrosser As ModelClass) As ModelClass
     ' calculate data in pObjCrosser and serialize the result object
     ' back from dllhost do aspnet_wp
     Return pObjCrosser
   End Function
End Class

<ObjectPooling(MinPoolSize:=4, MaxPoolSize:=16, CreationTimeout:=10000), _
JustInTimeActivation(False), EventTrackingEnabled(True), _
Transaction(TransactionOption.NotSupported)> _
Public Class Cache
   Inherits ServicedComponent
   Protected Overrides Function CanBePooled() As Boolean
       Return True
   End Function
End Class

lib.dll (cannot reference com.dll, therefore separated ICache interface)
-------
<Serializable()>Class ModelClass
 Public WriteOnly Property ObjCache As ICache
   Set(ByVal Value As ICache)
     Me.mObjCache = ObjCache  ' <--- this assignment is the culprit!
   End Set
 End Property
End Class

asp.dll  (references both com.dll and lib.dll)
-------

Sub Main()
 ObjCrosser = New ModelClass
 Try
   ObjWorker = New Worker  ' 1: non-pooled ServicedComponent
   Try
     ObjCache = New Cache  ' 2: pooled ServicedComponent
     ObjCrosser.ObjCache = ObjCache  ' 3: assign it to an object
     ObjWorker.Calculate(ObjCrosser) ' 4: serialize it as a member to the
COM+ server
   Finally
     ObjCache.Dispose()  ' 5: deactivate, try to send back to the pool
   End Try
 Finally
   ObjWorker.Dispose()  ' 6: deactivate
 End Try

Now, when debugging, the following happens in the Component Services
Management Console. A debug Watch has been placed retrieving a value from
ObjCache.

1:
- com.dll gets started
- 4 Cache objects are now in Pool (MinPoolSize), but not Activated yet.
- Worker gets Activated, but is not In Call yet.

2:
- 1 Cache object is taken from the Pool, it is Activated now.
- Watch now retrieves a value from the active object.

3:
- ObjCrosser has now the Cache object as a private member.
 Nothing new happens on the COM+ server, as expected.

4:
- ObjCrosser is serialized to the COM+ Server, method call is too short
 to show up in Activated, but Call Time is about 30ms.
 Nothing else happens on the COM+ server, as expected.

5:
- Watch throws a System.ObjectDisposedException, as expected.

Until now, anything has worked exactly as expected, but now:
- ObjCached, while evidentially disposed, is still Activated and *not*
returned to the pool!

6:
- ObjWorker is no more Activated, as expected.

Now the page terminates, but the Cache object remains Activated until an
idle shutdown event happens. Consequence: Once the MaxPoolSize limit has
been hit (here after 16 page requests without an idle shutdown), the server
consecutively responds with "System.Runtime.InteropServices.COMException
COM+ activation failed because the activation could not be completed in the
specified amount of time." - until the COM+ application is restarted.

The Main() method may look awful in this example. In fact, the ModelClass
and the Worker are heavyweight model tier classes, the former requiring the
asp.net context to be instantiated, the latter requiring such an instance to
do the real work required to create the web page. The current method call
layout is the result of performance optimizing by minimizing the number of
cross-COM+-boundary method calls which just are too expensive in inner
loops: the Worker class (causing thousands of queries to the Cache class)
has been moved from the aspnet_wp process to the dllhost. The "evil" Cache
member in ObjCrosser of course is not used within the Calculate() call, it
just happens accidentally to be there (in fact, the member is overwritten
with one instantiated within the Worker), but needed in the snipped away
code before and after. By the way: Exchanging the order of
instantiate/dispose of the two ServicedComponents doesn't change the
described behaviour.

Of course the actual leak is trivial to elimiate (after spending endless
hours on debugging): I just have to add the line
ObjCrosser.ObjCache = Nothing
between steps 3 and 4. But my main problem is that

1) Pooled objects are by design a limited resource (a *hard* limit!).
2) Try/Finally is therefore required to guarantee that they get disposed in
*any* (yes, *any*!) case.
3) But a successfull call to .Dispose() *demonstrably* does *not* dispose
the object in all cases.

So I have two questions:

A) Is the described behaviour by design and documented somewhere, or is that
a (known?) bug?
B) Are there other known cases where ServicedComponent.Dispose() actually
doesn't to the job it is *absolutely required* to do?
Bryan Phillips - 29 Oct 2006 21:02 GMT
If setting the variable to null releases it back to the pool, then the
dispose method is not calling Marshal.ReleaseCOMObject on itself like it
does for proxies.  Try adding the call to Marshal.ReleaseCOMObject right
after you call dispose.

Bryan Phillips
MCSD, MCDBA, MCSE
Blog:  http://bphillips76.spaces.live.com

> Hi
>
[quoted text clipped - 139 lines]
> B) Are there other known cases where ServicedComponent.Dispose() actually
> doesn't to the job it is *absolutely required* to do?
Toni Arnold - 01 Nov 2006 14:11 GMT
> If setting the variable to null releases it back to the pool, then the
> dispose method is not calling Marshal.ReleaseCOMObject on itself like it
> does for proxies.  Try adding the call to Marshal.ReleaseCOMObject right
> after you call dispose.

Before and after calling .Dispose(), a pooled ServicedComponent is equally
instantiated on the client side as a
{System.Runtime.Remoting.Proxies.__TransparentProxy}. According to the
Marshal.ReleaseComObject documentation [1], it throws a "Specified cast is
not valid." exception when trying to manually maintain* the reference
counter for a pooled ServicedComponent, as a TransparentProxy is not a
reference to an intermediate COM object itself, which seems to be required
by the Marshal.ReleaseComObject(ByVal o As Object).
*A note in [1] says "To ensure that the runtime callable wrapper and the
original COM object are released, construct a loop from which you call this
method until the returned reference count reaches zero." At least nobody
asks me to construct a trivial assembler program to ensure that the MMU
releases the individual memory pages used by the COM+ machinery ;-)

The Marshal Class documentation [2] describes it as a tool to handle the
conversion between managed and unmanaged resources. I'm aware of the fact
that the COM+ machinery is not written in .NET and therefore using a
ServicedComponent involves execution of unmanaged C++ operating system
code - but I thought that a) the .NET ServicedComponent tools should
transparently hide that fact from the application programmer (using only
managed .NET code) and b) that the .Dispose() method is exactly the tool we
need here: It is documented in [3] as "Releases all resources used by the
ServicedComponent". And this usually works regardless of the reference
counter within the client CLR - except in my case.

In my pre-.NET-COM+ book [4], pg. 258, I found this interesting, but a
little vague statement: "A proxy could also be created when an interface
pointer is passed from one context to another via some interface method
call. Once again, COM+ will take care of creating the proxy." In my case, I
think I was passing a pointer to a proxy instance, not to an IDL interface.
In any case, COM+ seems unaskedly to take care of incrementing the reference
counter, but not of decrementing it again.
Of course, as described on the same page, it makes no sense to pass that
pointer to a proxy e.g. from the aspnet_wp context to the dllhost context,
which is what I unintentionally was doing before setting the variable to
null, as "a proxy is geared to swim in a specific sea", and it does not work
outside of the context it was instantiated.

By the way: There was a mistake in my fist posting: an idle shutdown event
never happens, as the stuck ServicedComponent counts as activated, therefore
the situation was even worse than I thought.

So perhaps somebody has a good ServicedComponent COM+ book at hand and could
send me a pointer to that mystic chapter where my problem is described in
detail?

Thanks!

[1]
.NET Framework Class Library
Marshal.ReleaseComObject Method  [Visual Basic]

[2]
.NET Framework Class Library
Marshal Class  [Visual Basic]

[3]
NET Framework Class Library
ServicedComponent.Dispose Method  [Visual Basic]

[4]
Pradeep Tapadiya: COM+ Programming. Prentice Hall, 2001

Rate this thread:







Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.