| |
 Monday, March 21, 2005
Just a quick reminder, as Brad and Kit mentioned, a bunch of the CLR and BCL team will be at the Chili's in the Bellevue Crossroads mall tomorrow (Tue, 3/22). Starts at roughly 5:30pm... I'm hoping there will be more customers and partners than Microsoft employees!
 Sunday, March 20, 2005
I was just working on some early work on the STG->IL compiler I mentioned last week. Perhaps my brain is just shifting a little too much into functional mode, but I designed my entire AST to avoid any fields for almost all nodes. Things that would ordinarily call for fields are just pairs (or higher order combinations of pairs, such as triples and lists). I don't know if this is good, bad, or whatever, but it's certainly interesting.
For example, given the nonterminals:
prog : binds binds : var1 = lf1; ...; varn = lfn (where n >= 1) lf : varsa \u varsb -> expr
Well, prog just derives from binds. Binds is simply a list of pairs of vars and lfs (lambda forms). Vars are just symbols (which themselves are just strings, but alas I can't derive from string in the Framework since it's sealed, so here I do need a field unless I choose to just use List<char>), and lambda forms are just quintuples of other stuff. All of these use a marker interface IAstNode to indicate that they're an AST node rather than cluttering up their inheritence hierarchy.
For example:
interface IAstNode {} class Program : IAstNode, BindingList {} class BindingList : IAstNode, List<Binding> {} class Binding : Pair<Variable, LambdaForm> {} class Quintuple<A,B,C,D> : Pair<A,Pair<B,Pair<C,D>>> class LambdaForm : Quintuple<VariableList, UpdateFlag, VariableList, Expression> {} ...and so on...
Why do I find this so much more beautiful than the dirty, messy, more verbose field-based approach? I mean, everybody always talks about the is-a thing when preaching OOP... But if a program is-a list of bindings, and a binding is-a blah why not reflect that in the type relationships?
Anyhow, this isn't thought through too much. But it certainly seems clearer. For example, we do a lot of work in the framework to make strings seem like lists of characters... Why fake it? E.g. class String : List<char> { /*string-specific functions*/ }. Surely string is conceptually closer to having a list of characters being its direct supertype than plain old vanilla object. (Although on second thought the realities of performance and such probably limit the extent to which we could do this.) I guess it's the old aggregation versus inheritence argument. Blegh.
But it's really nice that I can program like this if I wish. Being able to compose powerful abstractions through nothing but inheritence and polymorphism is a great feature of and a testament to the CTS.
BTW: How did we all survive w/out generics pre-2.0?
 Friday, March 18, 2005
I recently sent this out to an internal audience. I saw no reason not to share it with external folks, too... Although most of it probably won't be of interest, maybe somebody out there will get something useful from it.
Atomicity & Asynchronous Exception Failures
We often get asked how Framework developers should write atomic paired operations that are reliable in the face of asynchronous exceptions, the canonical example being thread aborts. These operations might be the acquisition and release of a lock or the allocation and deallocation of some unmanaged resource, for example. Not dealing with the risk that an exception might interrupt these (hence breaking atomicity) might cause undesirable reliability, or possibly even security, problems for you. A lot of people wonder how paranoid they should be when coding defensively against these scenarios.
This email is intended to clarify today’s state of affairs (as of Whidbey Beta2), provide guidance to those writing reusable managed code, and to shed a little insight into our thinking around where we might go in the future. To be entirely transparent, the top priority is first to convince you that, in almost all cases, you don’t need to and shouldn’t be writing the paranoid code necessary to deal with such problems. Only after that’s out of the way will we discuss how to do what you originally might have thought you wanted to do (although, hopefully by then you’ll have been convinced otherwise).
This is meant to compliment Chris Brumme’s recent email “Exceptions, Security & Frameworks,” available here. In particular, the thread abort section #9. A lot of this will make its way into the Design Guidelines document in a more easily consumable form over the coming weeks.
A quick overview
We consider it the responsibility of Framework code to guarantee cleanup of state which spans AppDomains. It is also the responsibility of Framework code to block threads in a manner in which the CLR can take control of them. (For instance, use WaitHandle.WaitOne rather than a P/Invoke to WaitForSingleObject.) And it is also the responsibility of Framework code to tolerate AppDomain unloads. This includes tolerating a certain level of inconsistency, as seen by Finalize methods and AppDomain.DomainUnload events during an unload. There are some things Framework code isn’t responsible for, though. It is entirely up to the host to deal with and contain possible inconsistent or corrupt state inside an AD that occurs as a result of raising asynchronous exceptions on some opaque executing thread. Further, it is the responsibility of code which consumes an API to recover deterministically as needed from operations that might fail, not that of the API itself. This last paragraph is dense and perhaps the core message of this email, so you might want to go back and reread it.
Let’s examine some implications of this, using thread aborts as a running example. Asynchronous thread aborts that are part of an AD unload are acceptable, and Framework code needs to tolerate them. Random aborts that have nothing to do with an unload are not fine, and the Framework generally need not remain consistent when they occur. The host is making a decision that the risk of inconsistency is less than the value of injecting these kinds of aborts and then continuing to run. SQL Server can make this decision because it monitors whether the threads it aborts are modifying shared state at the same time.
Asynchronous exceptions are tricky since they manifest as exceptions originating at seemingly any instruction in code running on a thread, essentially introducing nondeterministic races all over the place. By asynchronous, this just means that the target of a thread abort is different than the thread actually asking for the abort, either through the Thread.Abort() API or as part of the AppDomain unload process. Although saying “any instruction” is a bit of an overstatement. We don’t process thread aborts if you’re executing inside a Constrained Execution Region (CER), finally block, catch block, .cctor, or while running inside unmanaged code. If an abort is requested while inside one of these regions, we process it as soon as you exit (assuming you’re not nested inside another).
Synchronous thread aborts (e.g. Thread.CurrentThread.Abort()) aren’t a concern at all since the result is analogous to manually throwing a new ThreadAbortException exception (with the exception that they get re-raised after catch blocks). This is completely deterministic and happens at well defined points in your code; thus, it doesn’t carry the same risk of corruption as asynchronous aborts and won’t be discussed further.
Atomic pairs
If you have a piece of code that introduces a state change, makes some side effect, or acquires or allocates a resource, for example, there will usually be a paired operation intended to roll back the operation. One normally has a desire to make these two things atomic. That is, if one occurs, the other is also guaranteed to occur. If you open a file, you normally need to make sure you close it. After you acquire a lock on an object, you probably want to release it when you’re done so that you avoid deadlocks. And further, the soonest you can do this the better. Deterministic (or eager) action is usually preferable to nondeterministic (or late). While these things are reasonably simple to achieve about 99.x% of the time, achieving that extra 0.(1-0.x)% is extraordinarily difficult, and indeed seldom justifies the complexity and difficulty of trying to become resilient.
As a simple example, all of this means that given trivial code like this:
using (Foo x = new Foo()) { // … }
It can fail in nontrivial ways. For example, if a ThreadAbortException were raised sometime between the invocation of Foo’s constructor and the assignment to x, then Dispose() will never get called on the instance that got created. This is because at the end of the scope, x is still null (since it was never assigned a value). Assuming some resources got allocated in Foo’s constructor, it’s the responsibility of Foo’s finalizer to clean up after itself at this point. It should also be noted this problem can also occur if Foo’s constructor throws an exception, which is a particularly good reason to avoid throwing exceptions from constructors and to instead prefer acquisition of resources inside discrete methods.
Now, hopefully Foo was written to have a finalizer that will eventually clean up such resources. Indeed, if Foo has a public constructor, then the author of Foo can have no guarantees that Foo is only used in the context of a ‘using’ statement. In other words, Foo would have no guarantees that its IDisposable interface is ever called on any of its instances. If we’re talking about publicly constructable objects, the only guarantees worth considering are the guarantees made to the client code.
While this does mean that whatever Foo introduced might take a little while to roll back, since we’re processing a thread abort it’s safe for Framework code to assume that we are unloading an AppDomain and therefore will be executing finalizers shortly anyhow, which means this is perfectly acceptable most of the time. (We talk about why this is so further below.) Here’s where the paranoia starts. What if surrounding code assumes that Foo will always have been disposed of once control escapes the using statement? In this case, this assumption doesn’t hold. If the surrounding code is privy to the internal details of what state Foo changes and how (such as knowing it creates a particular file on disk and cleans it up during Dispose, for example), it might be written against a false set of invariants. This might cause failures to ripple up the call stack.
Thankfully, as is the case with infrastructure exceptions like thread abort, the exception will be forced to re-raise at the tail end of any catch blocks. So long as catch blocks are written without such assumptions, at least non-catch code won’t execute and see surprising state. And so long as any state which spans a single AppDomain is cleaned up in finalizers, such failures don’t seem quite so bad.
Some simple dos and don’ts
There are some simple things you can do to make your code more robust without stepping over the paranoia threshold. As I stated above, most people writing Framework code shouldn’t even care about most of what appears after this section.
1. We don’t expect most of the Framework to be willing or able to recover fully from asynchronous exception. In fact, the sole responsibility of code executing during a thread abort or an AD unload is to fix up corruption to state that spans AppDomains. By span, this just means that the management and lifetime of that state is orthogonal to that of the AppDomain executing code which is manipulating it. It’s safe to assume that, if code is subject to a thread abort, it will be shortly followed by an AD unload. Your goal should be to make “shortly” as short as possible, namely by reducing the amount of work you do as a result of these events. This includes finally blocks, finalization, and AD unload event handlers.
This guidance immediately rules out having to worry about protecting state which is entirely isolated inside an AD, such as managed object monitors, for example. This is true, of course, unless doing so would subject your code to possible security holes, in which case you might have to worry at least a little about this. A malicious person who knows you end up violating a bunch of invariants because you didn’t write code to deal with a thread abort might use this knowledge to find new and interesting exploits. If you’re doing thread impersonation or some other scary security elevation, you likely want to guarantee (via ExecutionContext.Run) that it gets reverted before passing control back up the stack, even in the face of thread aborts. Fortunately, aborting a thread does demand privileges not granted to most partial trust callers, but this does not rule out some bug exposing a reproducible way to provoke aborts. Not likely, but not impossible either.
2. Most of the time, you can (and should) rely on lazy cleanup to prevent leaks. Eager cleanup is useful, but you should always use a finalizer to guarantee that important state gets rolled back and that resources get reclaimed. Better yet, use SafeHandle[http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx], especially for cross-AD state. SafeHandle uses critical finalization to ensure execution even in the face of rude thread aborts and unloads. Regular finalizers won’t get a chance to run in such situations. True, lazy cleanup can lead to undesirable intermediary situations, namely while the abort is getting propagated and other cleanup code executed, but it’s certainly better than letting a leak past the AD unload entirely. The benefit of forcing eager cleanup in the face of the rare occurrences described in this paper is not worth the significant cost you would have to incur. Try not to write code that depends too intimately on eager cleanup having taken place, especially inside other cleanup code.
3. Don’t use finally blocks to intentionally delay asynchronous exceptions. While a clever observation, writing your code entirely inside finally blocks to avoid an asynchronous from being injected between a block of paired operations is a horribly bad practice. This is especially true of long running sections of code, especially those which have a blocking operation thrown into the mix. Doing this holds up processing of thread aborts and can hang AD unloads. This will result in a poor application experience for those who rely on your code.
For example, consider this snippet:
ReaderWriterLock rwl = /*…*/; bool taken = false; try { try {} finally { rwl.AcquireWriterLock(-1); taken = true; } // do some work } finally { if (taken) rwl.ReleaseWriterLock(); }
Yes, this prevents a normal asynchronous thread abort from occurring between the lock acquisition and entrance into the try block, but it also introduces the significant unintended consequences cited above. Moreover, if your code blocks upon trying to acquire a resource, we wouldn’t be able to abort it until it unblocks itself. If it’s in a deadlock or long-running acquisition, this would be pretty horrible.
4. Start paired operations inside a try block when possible. This might deviate from guidance given in the past, but nonetheless helps to eliminate the window between an acquisition and entrance into the try block. While this window can seem tiny (e.g. acquiring a lock right before entering a try), the JIT is free to inject as much code inside of it as it sees fit. This will undoubtedly be a small quantity, but nonetheless increases the window’s duration and hence likelihood of being interrupted. With the window eliminated entirely, however, it means that you can be assured your finally block will run during an abort. It does complicate matters slightly, as now you can’t be certain whether the operation succeeded or not, and therefore whether you have any state rollback to perform in your finally.
There are a few ways to deal with this, but in general your finally block should be resilient to failure. This might mean trying an operation and swallowing a resulting exception (like releasing a lock that never got successfully acquired), or using an out parameter in the acquisition method. Unless you use a CER or finally block to do the acquisition, you can’t be assured that you ever completed an assignment or operation fully. But in (hopefully) most cases, you won’t need to know. And this is usually more attractive than worrying whether your finally block will get a chance to run.
5. Always use the ‘using’ and ‘lock’ statements in C# as appropriate. If you’re familiar with the code that gets generated for these, you might notice a discrepancy between #4 and #5. This is true. Both of these constructs expand to code which does the acquisition right before entering the try block, which as we established in the opening section, could lead to missed deterministic cleanup. But as we’ve also already established, most of this shouldn’t be a concern to you by now. In fact, the ‘lock’ statement has a hack to ensure that, at least in the absence of a rude abort or rude AD unload, the unlock will always occur. (Unfortunately, because it’s a JIT hack, it’s not guaranteed to happen across all platforms.) By using these constructs, we can optimize your code for free in the future if and when we reassess how better to make the code they emit (or how the JIT responds to such code, as in the case of ‘lock’) more reliable in the face of asynchronous failures.
6. Never initiate asynchronous thread aborts in the Framework. This is perhaps too strongly worded, but most code should never try to abort an opaque thread. Only sophisticated hosts who are prepared to deal with the ensuing corruption and inconsistent state inside an AD should ever attempt to do this, and even then I emphasize the word “attempt.” If you’re doing this, know that we are recommending Framework code to be written with the assumption that an abort will be followed shortly by an unload. If you don’t abide by this policy, code probably won’t react as you might have hoped. There’s an MDA in Whidbey that catches this. We expect people writing applications to make this mistake, but please, please, please just don’t do it from Framework code.
7. Use CERs sparingly. CERs can be used to patch up known problems and to protect state that spans ADs, but unless you know for sure that one of these applies to you, don’t even consider using one. You need to execute under particularly bad circumstances, and truthfully doing it right is rocket science. Low level infrastructure code will use CERs more and more in the future, but for most of the code that builds on top of this infrastructure, CERs aren’t necessary.
Stress failures, complexities
The picture of the world depicted above is a bit naive. For example, it suggests that orphaning an object lock is acceptable during an AD unload. While in theory this is a fine thing to live with, there’s a set of code which will run during an unload that might make assumptions about what invariants have or have not been kept in place. This is one of the reasons why it’s so important to reduce the complexity and quantity of code that runs in such situations, and in particular to reduce as many inter-dependencies as possible. And if an asynchronous exception was raised and the AD isn’t actually being unloaded, then certainly a whole host of things are sure to be wrong within the AD’s boundaries afterwards. Most of these problems become magnified when examined under high stress loads.
Orphaned monitors are an example of a perfectly isolated resource that can still cause problems. If an AD is getting unloaded and a) this involves multiple threads and b) their cleanup paths attempt any lock acquisition whatsoever, there is a real risk of deadlock due to a lock that never got released. But again, this is by and large an application problem, not one for the Framework to be concerned about. Most of the Framework is written not to be thread-safe... at least not to access precise shared locks across thread boundaries anyhow. The criticality of a deadlock, however, is precisely why we added special code to recognize the ‘lock’ pattern—this guarantees lock release in the face of asynchronous exceptions (although not during rude aborts and rude unloads).
It’s likely that inside hosts which aggressively perturb code with thread aborts, such as ASP.NET for example, innocent Framework code might fail in new and interesting ways. We’re not suggesting that we shouldn’t evolve to deal with these situations as they arise, simply that the cases are so sporadic and difficult to predict that developers shouldn’t proactively seek to fix problems that might not exist. Aggressive stress testing should uncover most of these problems.
Future direction
Of course, this section is highly speculative. The topics raised in this paper are something that we (the CLR team) intend to look very closely at in the Orcas timeframe. It’s likely that we’ll dream up some great new solutions. It’s also likely that any such solutions will require a combination of runtime, language, and Framework support to get right.
For example, we’ve begun to adopt the pattern of providing out parameters to confirm acquisition of a resource, where the acquisition occurs inside a protected region. This is mostly to avoid the difficulties explained in #3 and #4 above. For example, the Monitor.ReliableEnter method does just this (which is only internally available, so you’re out of luck if you’re not shipping inside mscorlib). It executes inside a CER and sets a bit to indicate that you’ve successfully taken the lock. This alleviates concern about #4 above causing problems. For example, one could imagine the C# ‘lock’ keyword emitted code like this in the future:
bool took = false; try { Monitor.ReliableEnter(foo, out took); // code inside synchronized block } finally { if (took) Monitor.Exit(foo); }
So long as you abide by rule #5 above, you will get the benefits of whatever innovation we do for free, and this doesn’t rely on any JIT hackery. We will certainly look for more places to introduce such a pattern—and even make it public, too.
Dispose is unfortunately more difficult to solve. The goal here would be to first make sure the construction of an object and assignment to a local variable is atomic, and second to ensure there’s no window between the assignment and entrance into the try block. Without running constructors inside a protected region, however, there’s no obvious great solution. We certainly wouldn’t advise anybody to execute constructors inside a finally block, for example. But at the same time, we know we need to figure this one out. Doing resource allocations inside a protected factory method is one option, for instance, but only one that makes sense in a situation where you’d feel comfortable holding up an AD unload in order to protect state.
This guidance was creates mostly in response to constant feedback and questions we receive on the topic. A lot of this will make its way into the Design Guidelines in the form of more prescriptive guidance. This paper was largely an exploratory exercise resulting from conversations and meetings on the topic. In particular, I’d like to thank Chris Brumme, WeiWen Liu, Brian Grunkemeyer, Anthony Moore, and Dave Fetterman for their helpful feedback and suggestions, and indeed pushing the direction of the core message and points in the above text.
 Saturday, March 12, 2005
I’m in the process of writing up several articles/whitepapers that I intend to start publishing over the coming weeks.
- Dispose, Finalization & Resource Management Design Guidelines: I’ve been iteratively working on these updates over the past couple months and just submitted the whole thing to our core review group to solicit feedback, comments, and the like. It covers the recent unification work we did around the Dispose pattern in the Framework, along with general guidance on writing correct resource management code. For a bit of history around this, check out here and here. The end result turned out to be a bit longer and larger than I had originally intended, but I think I was able to lay it out in an intuitive and easily consumable way.
- Writing Atomic Abort-Safe Code: A lot of people writing code for the Framework lose a bunch of sleep over writing the most reliable code possible. However, our guidance on how to do this in the face of asynchronous thread aborts hasn’t been clear in the past, especially when writing paired operations (e.g. acquire/release semantics). So a bunch of us got together, discussed it, and decided we need to come up with some clear guidelines. I’m writing up a brief article and following it up with a DG update and some FxCop rule proposals. Priority #1: convince most people that they needn’t worry about these things. Only then will the painful details of abort semantics, AD unloads, CERs, critical finalizers, finallys, and the like be presented.
- Threading Security Best Practices Design Guidelines: During the recent Whidbey security push, we produced a lot of great content around how to write secure multi-threaded code. Unfortunately, for obvious reasons much of this must remain inside MS. But some of the more general guidance is being incorporated into the DG. This includes, for example, avoiding publicly-accessible locks, never accepting ReaderWriterLock LockCookies from untrusted sources, and avoiding message pumping inside a synchronized region. Hopefully this will help users of the Framework to write more robust and secure code, too.
- Concurrency & Parallelism: These topics are more personal research interests of mine, but nonetheless somewhat related to my job as the threading PM. I walk through a number of aspects about concurrency, parallelism (yes there’s a difference), and the nature of each. Specifically with regards to parallelism, I discuss the intrinsic algorithmic properties which are conducive to parallel execution, some theoretical math which demonstrates that, with the right multi-x (where x is core|proc) and task management architectures, the future looks promising. Check out this butterfly sorter. I recently had an interesting conversation with its author, Satnam Singh, and we seem to agree on many things. This particular example was designed and implemented in specialized hardware, but there’s no reason why we can’t write such things generically in software to take advantage of the underlying hardware support. I present some interesting evidence and conclusions to support this assertion.
- Compiling Scheme to IL with Sencha: Again, personal research area. I need to write up some findings based on my compiler work, what I consider to be the unique features of Sencha, and mostly just capture knowledge so I have something to refer back to. I’m thinking more seriously about another compiler effort, and have chatted with the GHC and MSR folks a bit about it. Basically, I intend to create a STG textual format and a corresponding STG-to-IL compiler, based on Simon’s Spineless Tagless G-Machine paper. I believe this could then be used easily as a backend to the GHC compiler. Very ambitious project with a limited user audience, but it seems to have a lot of interesting facets to it. Such software could be used for parallel research on the CLR in the future.
 Saturday, March 05, 2005
 Friday, March 04, 2005
Advanced Topics in Types and Programming Languages edited by Benjamin C. Pierce |
 |
9 of 10. This book builds on one of my favorite reads from last year, Types and Programming Languages. It contains a collection of essays on topics ranging from precise type analysis, lower level type systems (e.g. a typed assembly language), reasoning about programs, and ML type inferencing. I've not yet completed it, but it is generally very well written. I highly recommend both of these books. |
Free Culture: The Nature and Future of Creativity by Lawrence Lessig |
 |
8 of 10. Fascinating read by one of the most knowledgeable and provocative experts on the topic of both historical and modern intellectual property. This book takes a careful look (without too much lawyer speak) at the impacts IP law have had on culture as a whole, and examines the impacts on the future of ideas and the ability to act out on such creativity. Lawrence has a blog over at http://www.lessig.org/blog/, and has a great collection of presentations over at the IT Conversations website. |
Mind Hacks by Tom Stafford, Matt Webb |
 |
7 of 10. This is a fun book. While some of the topics covered aren't necessarily hacks, the author's do a great job of discussing some interesting facets of how the brain functions. While coverage doesn't go very deep in any one area, the book provides plenty of (mostly web) references to follow up on if you end up wanting more details. The prose is very computer/geekish which just adds to the reading pleasure. |
 Monday, February 28, 2005
Via the Daily ACK, the University of Washington has their Computer Science & Engineering colloquium series available online.
One gem in this series is "Google: A Behind-the-Scenes Look." The presenter discusses a little bit about culture, a tad about their algorithms, data centers, trends, ongoing research, and so on. Each of the topics could have a dedicated video in its own right. Still a great breadth-first overview.
I've always loved data. It seems to me that all of the creative and elegant software out there has arisen directly as a result of humans needing to deal with and categorize information. No data? Not much to do, now, is there? Sure, sure, ... people often cook up some hypothesis or artificial goal in order to get motivated, but processing information is so ingrained in what we are and do that it truly seems to be the reason for human existence.
Alright, maybe that was too preachy/zen-like/whatever. Just watch the damned video. :)
Based on a recent question from Brad, I decided to write up a little post on the topic of event handlers. Specifically, that objects registered to receive events are rooted, meaning that they won't be eligible for GC even if you drop them on the floor. Through some trickery with weak references, you can work around this (if you need to).
So, what's the issue?
Once you register an object as a sink for events in C#, the source object generating the events will keep a reference to it. When the source becomes unreachable, your sink will also become unreachable (assuming that's the only live reference to the sink). An alternative way to make your sink unreachable is to manually unregister it, asking that the source give up its reference. But this means that until either one of these things happen, your object won't be eligible for garbage collection. This probably won’t be surprising for most folks, but for finalizable objects which own resources it does mean you need to be very careful to unregister your sink when it needs to die. Even for objects which don't hold on to such resources, you might be building up a list of unused objects after a while, akin to stashing away references in a collection and forgetting to prune it when you're done.
For example, given source and sink set of types:
class Source
{
public event EventHandler<EventArgs> Tick;
public void OnTick()
{
EventHandler<EventArgs> t = Tick;
if (t != null)
t(this, new EventArgs());
}
}
interface ISink
{
void HandleTick(object sender, EventArgs e);
}
And client code which defines a sink implementation:
class MySink : ISink, IDisposable
{
private IntPtr someHandle;
static int counter;
int id = counter++;
public MySink()
{
// create someHandle
}
~MySink()
{
// clean up someHandle
Console.WriteLine("!{0} finalized", id);
}
public void Dispose()
{
// clean up someHandle
GC.SuppressFinalize(this);
Console.WriteLine("~{0} disposed", id);
}
public void HandleTick(object sender, EventArgs e)
{
// use someHandle
Console.WriteLine("{0}: Received", id);
}
}
The owner of an instance of MySink might not realize what consequences registering the sink for messages will have, namely that it will cause it to become rooted. For example, this simple code does the trick:
Source src = /*...*/;
MySink sk = new MySink();
src.Tick += sk.HandleTick;
Given that this type owns unmanaged resources, the user really needs to remember to unregister it when they’re done with it.
And this is a problem because…?
This actually seems like it's the desired/intended behavior for somebody registering for events in most cases. Frequently, a sink object will be created, registered for an event, and then its reference dropped and left on its own to process the incoming messages. I.e. its only purpose in life is to respond to events. It might seem surprising that somebody would register an object and forget to ever unregister it, but I suspect this happens all the time. This is one of those subtle bugs which could manifest over time in a long running application (e.g. a web app), where unmanaged memory just continues to rise and rise until ASP.NET recycles itself. Especially if there’s some AppDomain-wide event registration and routing mechanism.
So, for those situations where we don’t want the event source to keep us alive, what are we to do?
Enter “Weak Event Handlers”
We have this nifty feature called weak references (exposed by the WeakReference class), enabling you to hold on to a reference without keeping it pinned from GC collection. If there are no strong references to an object and the GC runs, it'll sweep these up. This means that the underlying object referred to by the weak reference won't be valid any longer. (For those from the Java camp, be careful—one of the first mistakes I made when moving to the CLR was thinking that weak references == soft references. Soft references are only collected as a last resort, making them perfect for caching. Weak references are always collected if no strong references exist at the time of a GC, so you might have to think carefully about using them. This is one great reason to avoid GC.Collect() in production applications (among many others).)
We can go ahead and use this feature to wrap up our event handler delegates. Then we just hand the wrapper instead of the raw delegate off to event registration, thus preventing the event source from keeping our object alive. So, let’s get started.
First we introduce a general purpose WeakEventHandler<T> class:
class WeakEventHandler<T> where T : EventArgs
{
private WeakReference weakRef;
private EventHandler<T> handler;
public WeakEventHandler(EventHandler<T> d)
{
weakRef = new WeakReference(d);
handler = new EventHandler<T>(Invoke);
}
public EventHandler<T> Handler
{
get { return handler; }
}
private void Invoke(object sender, T e)
{
EventHandler<T> d = (EventHandler<T>)weakRef.Target;
if (d != null)
d(sender, e);
else
// hmm! what to do here?
}
}
So now somebody can construct a weak event handler, and register that instead. Once the target of the delegate loses its roots, it’s eligible for GC. For example, the three line snippet from above becomes:
Source src = /*...*/;
MySink sk = new MySink();
src.Tick += new WeakEventHandler<EventArgs>(sk.HandleTick).Handler;
(Note that I initially wanted to subclass Delegate to make a WeakDelegate type, and simply override the internal storage in order to make a general purpose “weak delegate.” Unfortunately, Delegate and MulticastDelegate are treated specially by the CLI (see Partition II), meaning that I couldn’t do this straightforwardly. I’m going to look into it further, but I’m not sure exactly what this would entail.)
But now won't my WeakEventHandler stay alive?
Yep, it does. This is admittedly leaps and bounds better than keeping a finalizable object rooted, but we can probably clean things up a little. Notice the “// hmm! what to do here?” line above?—we can actually put some code in here that intelligently unregisters the weak handler from the source. It requires a bit of generalization, but here’s a shot at it. (Note that this approach requires an event to be triggered to initiate the cleanup, so for events with particularly long delays in between instances, this might not be a great solution.)
First, let’s update our WeakEventHandler<T> class to accept an unregister delegate. This will be invoked in cases where our underlying object has been collected, and hence the weak wrapper should be removed from the event source’s registration list; from this point forward, our previous code just ignores all future requests to fire the event anyhow:
delegate void UnregisterHandler<T>(WeakEventHandler<T> handler) where T : EventArgs;
class WeakEventHandler<T> where T : EventArgs
{
private WeakReference weakRef;
private EventHandler<T> handler;
private UnregisterHandler<T> unregister;
public WeakEventHandler(EventHandler<T> d) : this(d, null)
{
}
public WeakEventHandler(EventHandler<T> d, UnregisterHandler<T> u)
{
weakRef = new WeakReference(d);
handler = new EventHandler<T>(Invoke);
unregister = u;
}
public EventHandler<T> Handler
{
get { return handler; }
}
private void Invoke(object sender, T e)
{
EventHandler<T> d = (EventHandler<T>)weakRef.Target;
if (d != null)
d(sender, e);
else if (unregister != null)
unregister(this);
}
}
Then, we just update our source to have an Unregister method. As a matter of fact, now that we have this weak handler goop, let’s encapsulate it inside a new Register method. This means that (unfortunately) we won’t be using the C# syntax for event registration, but compared with having users grok the weak handler mechanism, I personally find it much more palatable:
class Source
{
public event EventHandler<EventArgs> Tick;
public void OnTick()
{
EventHandler<EventArgs> t = Tick;
if (t != null)
t(this, new EventArgs());
}
public void Register(ISink sk)
{
Tick += new WeakEventHandler<EventArgs>(sk.HandleTick, Unregister).Handler;
}
private void Unregister(WeakEventHandler<EventArgs> eh)
{
Tick -= eh.Handler;
}
}
Now the three lines of code change to:
Source src = /*...*/;
MySink sk = new MySink();
src.Register(sk);
Should I actually do this?
Just because you can use this doesn't mean you should. :) It seems like a reasonable strategy to make sure you don't shoot yourself in the foot too badly, but if you're relying on weak wrappers to do the unregistration and cleanup for you, it seems like all you're really doing is working around bugs. This is a bit like forgetting to call Dispose on an object and having a finalizer kick in just to make sure you don't leak. Further, you probably shouldn't use this if all you do is register for an event and drop the reference. In this case, the GC could kick in as early as the nanosecond after you drop the reference, meaning your registration was useless.
With that said, I think it's perfectly reasonable to use this to debug and detect bugs in your code. For example, if you fired an assert instead of unregistering the weak handler, you might be able to detect where in your program you forget to unregister sink objects. Just some thoughts. I'd love to hear any feedback on where you might find this useful.
Testing, Testing, 1, 2, 3...
This little snippet of test code demonstrates the whole thing. Here’s the full source in case anybody wants to play around with it without having to scour this post to pull together all the code.
class Program
{
static void Main()
{
Program p = new Program();
p.Execute();
}
private Source src = new Source();
public void Execute()
{
for (int j = 0; j < 1000; j++)
{
for (int i = 0; i < 1000; i++)
{
src.Register(new MySink());
}
src.OnTick();
GC.Collect();
}
}
}
 Tuesday, February 22, 2005
I was hacking ever so briefly on my compiler today, and found a pretty subtle bug. Basically, free variables weren't being detected correctly which whacked my closure conversion process--resulting in IL which didn't quite do what it should have. I'm not sure that anybody would find the debugging exercise interesting, but humor me... ok? ;)
I have this simple test case:
(define (mkadder n) (lambda (x) (+ x n))) (define z0 (mkadder 5)) (define z1 (mkadder 10)) (write "Should be 55: ") (write (z0 50)) (newline) (write "Should be 60: ") (write (z1 50)) (newline)
Nothing terribly groundbreaking. I have a small # of these simple tests (~50) which aren't really tests at all. I just compile them, stash away the IL, and review and accept (or debug) any changes to the IL when I make compiler changes. Ideally, I'd have some asserts in there, but this seems to work fine for now.
As you probably noticed above, the (lambda (x) (+ x n)) contains a free variable, n, which is bound by its enclosing mkadder lambda. I represent bound closures through a pretty popular closure conversion technique.
It's pretty simple: Any lambdas with no free variables are emitted as static methods; other lambdas, however, get emitted as classes containing fields to hold the bindings of their free variables. These classes also have a constructor which requires all such bindings to be supplied, and an Apply method that takes whatever # of args the lambda needs to execute (this is the meat of the lambda).
E.g. mkadder is a static method since it doesn't contain free variables:
public static Func1<double,double> mkadder(double n);
However, the anonymous lambda inside gets its own class:
public class _lambda0 : Closure<float> { private float n; public _lambda0(float n); public float Apply(float x); }
(A smart cookie might notice that we have a subtle issue here--that n actually gets blitted around. So when mkadder passes n to _lambda0's constructor, if _lambda0 ends up mutating it, we're not sharing the same reference, and hence mkadder wouldn't see _lambda0's updates. This is a problem since this is the correct semantics. To solve this problem, I try to detect any mutation and lift the variable out into a shared static reference; by detect, I look for either a) mutation for sure (e.g. set!), or b) can't tell (e.g. passing it off to yet another method). But I digress...)
Before calling Apply, an instance of the closure class must be constructed, at which time the values for free variables are provided, fields set, and free variables bound. (Another popular technique is to pass free variables as additional arguments to the method. I considered this, but this way seems a bit nicer for interop scenarios from OO languages (i.e. C#). It does mean that I have to construct simple objects and throw them away almost immediately, though, so once I get to the perf tuning stage I might change my mind. I could always go with both, and make the instance method just a shortcut to the static which passes the field values as the free variable arguments. This sacrifices code size, though. Never perfect, is it?)
Anyhow, I detect free variables and pick the strategy by doing a depth first traversal of my AST. During this traversal, I accumulate variable references, and for special nodes, such as a lambda which binds parameters, or a define or let which creates bindings for a precise scope, I delete variables from the ongoing free variable list. This works nicely if your traversal code works properly. :) When it doesn't, bad things happen.
I represent things like the operands to a procedure call as an IList<AstNode>. Unfortunately, my traversal code uses reflection to get at an AST node's children, and looks for both nodes directly and IEnumerable types (which contain lists of nodes). But alas, IList<T> doesn't implement the old-style IEnumerable (although it does implement IEnumerable<T>). It makes sense--doing so would require anybody implementing a new generics-style list to implement the old-style one, too. I wanted to say typeof(IEnumerable<*>).IsAssignableFrom(foo), but of course you can't easily express such things in C#... (although through some hard-to-follow reflection code you most certainly can). I could have done IEnumerable<AstNode>, but unfortunately I have nodes which use further derived types as the type argument, so this wouldn't have worked... (although using co-/contra-variant support in IL might have worked... I prefer to remain in C#-world for now).
This meant the free variable n wasn't even being picked up! Which in turn meant that my lambda was fully closed and could be emitted as a static method. Ouch. Obviously, this caused my program to fail, but the scary thing is how subtly: it compiled and even passed verification. How I emitted valid IL when I should have been missing a variable reference is beyond me at the moment. This surely will uncover a couple bugs which happen in strange edge cases like this. Lesson learned #1: When in doubt, fail loudly. Lesson learned #2: Get better test coverage. This is such a simple case, I'm surprised I just found it now.
Anyhow... What did I do to fix this? Pretty simple: I just changed my AstNodes to use List<T>. The BCL team's done the work to implement both IEnumerable and IEnumerable<T> on List<T>. I know I'll always have objects in there, so I'm not worried about the boxing overhead this would have normally incurred (although I do end up doing a lot of runtime type checking--something I need to do anyhow).
And like magic, it works! Yaay.
 Monday, February 21, 2005
I wonder if anybody out there in the community has given any substantial thought to how Avalon fits in on the web. Right after PDC'03, I started work on something which ultimately got dropped and never completed. It was intended to be a web application framework that enabled Avalon and Indigo to hook into some web eventing mechanism (i.e. ASP.NET or something similar), to allow for rich web-based UIs.
Basically, you could wire up certain UI events to result in Indigo messages to a web endpoint. This endpoint could have some defined "page" lifecycle, or you might be able to hijack ASP.NET in some fashion to reuse the existing familiar model. Certain types of messages could result in a full client page reload, i.e. the message returned could contain a segment of XAML which would then be rendered in response. You could even envision page reactions which didn't require a full page refresh, sort of a JavaScript-ish thing, for example updating a single field, moving a UI widget, and so on.
It seems you could do this without Indigo (just use raw HTTP and fully take advantage of ASP.NET... you'd of course need to get it to generate proper XAML instead of HTML), but there are so many benefits that Indigo buys you that I think it's worth the extra investment. Getting things to work just right on the client, including dynamic page compilation and tuning the eventing model to have the right level of chattiness so as not to make your UI super sluggish, seems like these would be two of the most difficult challenges. Still lots of potential.
So: any thoughts on this whole thing?
|
|
Me
Joe  is an architect and developer on a systems incubation project at Microsoft.
Recent
Search
Browse
Disclaimer:
The content of this site are my own personal opinions and do
not represent my employer's view in anyway.
© 2013, Joe Duffy
|
|