| Servergeek |
| Mickey Williams' weblog |
|
My C# BookMicrosoft Press PageOn Amazon Racing LinksSlalomSkateboarder.comNCDSA slalom page 3dm Ick Sticks Pocket Pistols .NETScott GuthrieRob Howard .NET Notes BradA LonghornScobleChris Sells ServicesDon BoxChristian Weyer All Things Distributed Ingo Rammer Tim Ewald BlawgsBag and BaggageSCOTUS Blog EconArg MaxCapital Spectator OtherWilliam Gibson |
Friday, April 25, 2003
More on the MSDN GC ArticlePosted 6:10 PMThinking about this a bit more... Perhaps if I show a more elaborate example. Consider classes X and Y, where X implements IDisposable and Y does not:
class X: IDisposable {
Y _y = new _Y();
public Y Y {
get { return _y; }
set { _y = value;}
}
...
}
class Y {
Z _z;
public Z Z {
get { return _z; }
set { _z = value;}
}
}
Instances of X (and Y) are created: X _x = new X(); Time passes, and in recognition of long and dedicated service, _x and _y move into generation 1 in the heap. The _x and _y instances are dreaming of promotion to generation 2, when _y aquires a reference to _z, an instance of class Z: _z.Y.Z = new Z(); Shortly after _y aquires a reference to _z, _x finds itself without portfolio, and becomes untraceable. At this point, I believe the following things are true:
Since object _y (in gen 1) was written to, all references from _y are considered roots. Extending the example, this could be a graph of objects, not just _z. (I'm also assuming that other object references adjacent to the object ref known as _y also become root-equivalent here - an unknown number since the number of references affected depends on the granularity of the data structure that tracks writes into higher generations.) As far as possible optimizations, clearly an object undergoing finalization can't attempt to use managed objects. These objects may have been finalized, and may be hollowed-out zombies of objects that are incapable of fulfilling any sort of contracted behavior. And just as clearly, it's a type-safety/security issue to leave a reference dangling. But would it be possible to detect (using the previous example) for_y's reference to _z to be altered in some way so that the runtime could safely free the object previously known as _z? Would the cost be small enough to justify given that you would be able to free objects earlier? Until today I assumed so, and I believed that this was done - the MSDN article by Rico explains why this is not the case. But this anchoring of unused references (_y causing _z to remain in-heap) is not unique to obects requiring finalization. Yes, a finalized object does persist longer than a non-finalized object, but that's a known side-effect of finalization. It seems to me that non-finalized object will also tend to anchor younger objects, albeit for a shorter period of time. So what does this mean for you and me? The write barrier is a good place to avoid.
More on MSDN GC ArticlePosted 12:56 PMThinking about this a bit more... and maybe it's me that's buggy. I can see how the finalizer will tend to lock some objects, but I have a few questions. Bottom line: I agree with the perf issue described in the article, but still don't like the Dispose pattern example. Large post coming later today. Thursday, April 24, 2003
Event Handling Strategies - Client ExceptionsPosted 12:53 PMSo here's one of those problems that you run into when building distributed concurrent systems. Almost everyone I work with (me included) writes great code most of the time. Unfortunately, the odd case where we do something boneheaded can, on occasion, cause much grief - and trouble can come from unexpected sources - consider the humble .NET Framework event. Let's say that you create a component that exposes a public event. Given a sufficient number of clients wiring up event handling delegates, eventually one or more of these clients will allow unhandled exceptions to leak back into your component. This is a potential problem, since the typical naive pattern for invoking an event delegate chain is simply:
class Producer
{
public event EventHandler Faucet;
public void RaiseEvent()
{
if(Faucet != null)
Faucet(this, EventArgs.Empty);
}
}
If an exception flows back into this code it won't be handled, and your code will fall down and go boom. Aha! you say. I'm no bumpkin! I've wrapped my code in a catch-all block, so that I'm immune to bad behavior to clients, like so:
public void RaiseEvent()
{
try
{
if(Faucet != null)
Faucet(this, EventArgs.Empty);
}
catch
{
// Handle exception here or just eat it.
}
}
Unfortunately, while this code protects you, it doesn't protect any other (presumably well-behaved) clients that expect reliable event notification. If a client (incorrectly) throws an exception back to your component, pain does not flow to the offending party. Rather, other subscribers to the event will occasionally miss their event notifications. This is unfortunate, because although these clients are probably planning on doing evil things in the future, and may have even performed evil deeds recently, they are completely innocent of the current crime. In order to ensure that all subscribers receive events even in the presence of bad behavior by other subscribers, you must catch exceptions separately for each invocation through the delegate chain. A boilerplate looks something like this:
public void RaiseEvent()
{
if(Faucet != null)
{
// Pack arguments for event
object[] args = new object[] {
args[0] = this,
args[1] = EventArgs.Empty
};
Delegate [] targets = Faucet.GetInvocationList();
foreach(Delegate d in targets)
{
try
{
d.DynamicInvoke(args);
}
catch
{
// Handle exception
}
}
}
}
MSDN Article BugsPosted 6:57 AMI missed this article when it was initially published last week, but MSDN has an introductory article on GC Basics and performance. Pretty good in some places, but there are a couple of errors that detract from the article. The bit about finalization and performance is incorrect in one aspect. Although an object that requires finalization will lock any objects that it refers to, it will only do so while it is queued or it is being finalized. These are the only times that the object is root traceable. Consider:
The article also improperly implements the Dispose pattern. Here's the fragment from the article:
// Naive implementation - don't use!!
class X: IDisposable
{
public X(
)
{
initialize resources
}
~X()
{
release resources
}
public void Dispose()
{
// this is the same as calling ~X()
Finalize();
// no need to finalize later
System.GC.SuppressFinalize(this);
}
};
Even ignoring the closing semi-colon for the class, this code doesn't properly separate the two disposal scenarios:
Here's the basic boilerplate for the Dispose pattern:
public class MyResource: IDisposable
{
bool _disposed = false;
public void Dispose()
{
InternalDispose(true);
}
~MyResource()
{
InternalDispose(false);
}
private void InternalDispose(bool disposing)
{
if(disposing)
{
// Call Dispose on managed objects owned by this object
_disposed = true;
GC.SuppressFinalize(this);
}
// Free unmanaged resources
}
}
The purpose of the _disposed field is to detect the case where a client attempts to use an object that's already been disposed, viz:
public void f()
{
if(_disposed)
throw new ObjectDisposedException("A pithy comment");
// Do MyResource.f() ...
}
Wednesday, April 23, 2003
Werner's Blogging EuroRotorPosted 9:01 PMWerner Vogels is blogging EuroRotor in Pisa. He covers a talk by Luca Cardelli on Polyphonic C#, which I've been drooling over for quite a while. The MSR page has some basic information including a feature chart, and a promise of a compiler someday. Hmm, if you work at MS do you get to sample this kind of stuff, or do you need to wait like everybody else... Tuesday, April 22, 2003
Microsoft Smartphone Dev KitPosted 4:36 PMOh yeah, definitely wanting one of these. I wonder if I can convince my daughters to buy me a license for Father's Day? No, wait - that's too far away. Monday, April 21, 2003
Dave Winer SmackdownPosted 6:26 AMLove this quote from Mark Pilgrim: Oh, but announce a new but invalid RSS feed, and hes all over that like Napalm on a fresh wound. I really couldn't stand using Radio, for reasons layed out quite well by Werner (I'm a very happy Blogger Pro user now). It looks like there may be a wee bit of pent-up anger directed towards Dave Winer. Dave's of the tribe that consistently blames Microsoft for bugs and lack of adherence to standards, while producing a product that is, frankly, subpar. The unique thing about this kind of train wreck is that you can follow the links as it unfolded, even if you weren't there when it happened. Updated: Just realized I didn't refer to the original post, which was from Dave.
|