Monday, November 16, 2009

Slow file deletion on Windows 7 RTM

The picture below says it all:



Of course, it didn't actually take 23 hours. It actually took about 45 minutes - that's around 13 items per second. More than half of which was taken up by calculating how long it was going to take (estimating the quality of the calculation is left to the reader).

Now I understand the need for transactional semantics during a delete, the necessity of shared filed locking, and the possibility of file system watchers getting involved in their own idiosyncratic ways.

But 13 items per second? On a 10,000 RPM drive? Seriously, guys.

Friday, November 13, 2009

The WeakEventHandler

When your Observer class hooks an event on an Observable, it implicitly creates a strong reference from Observable to Observer. That means the Observer won't be collected until the Observable is. That means memory leaks at best; latent bugs due to out-of-date observers at worst. So I've written a little WeakEventHandler class that behaves like a weak delegate: it allows the Observer to be collected before the Observable is. Best of all, the syntax is almost identical to the original.

Instead of writing

observable.Event += new EventHandler<MyEventArgs>(this.HandleEvent);

you'd write

observable.Event += new WeakEventHandler<MyEventArgs>(this.HandleEvent);

and the delegate behaves as if it were weak. However there is one downside: the proxy object that is created to pass the event to the observer hangs around. This can cause problems if the event is attached to by a great many short-lived observers. Fortunately there is a solution:

observable.Event += new WeakEventHandler<MyEventArgs>(this.HandleEvent,
handler => observable.Event -= handler);

The implementation of WeakEventHandler is given below.

delegate void EventDetacher<TArgs>(EventHandler<TArgs> handler)
where TArgs : EventArgs;


public class WeakEventHandler <TArgs>
where TArgs : EventArgs
{
MethodInfo method;
WeakReference target;
EventDetacher<TArgs> detacher;


public WeakEventHandler(EventHandler<TArgs> realHandler, EventDetacher<TArgs> detacher)
{
this.method = realHandler.Method;
this.target = new WeakReference(realHandler.Target);
this.detacher = detacher;
}


public WeakEventHandler(EventHandler<TArgs> realHandler)
{
this.method = realHandler.Method;
this.target = new WeakReference(realHandler.Target);
this.detacher = null;
}


private void HandleEvent(object sender, TArgs args)
{
object realObject = target.Target;
if (realObject == null)
{
if (detacher != null)
detacher(this);
}
else
{
method.Invoke(realObject, new object[] { sender, args });
}
}

public static implicit operator EventHandler<TArgs>(WeakEventHandler<TArgs> proxy)
{
return proxy.HandleEvent;
}
}