This post originated from an RSS feed registered with .NET Buzz
by Udi Dahan.
Original Post: Asynchronous Callback R Us - part 2
Feed Title: Udi Dahan - The Software Simplist
Feed URL: http://feeds.feedburner.com/UdiDahan-TheSoftwareSimplist
Feed Description: I am a software simplist. I make this beast of architecting, analysing, designing, developing, testing, managing, deploying software systems simple.
This blog is about how I do it.
In part 1 of this topic, a different kind of API was presented for asynchronous callbacks. Pretty quick, in the comments, Ralf pointed out the problems with this approach. These problems are very typical in multi-threaded scenarios and manifest as hard to find (and reproduce) bugs.
However, in cases where we are performing "highly asynchronous work", like sending a request to another machine, where that request will be processed, and a result eventually sent back over the network, this kind of API could make it clear that we are NOT performing in-process multi-threading, and that the expectation of execution time should not hold.
And the code that enables this API (note: this is from memory):
public delegate void CompletionCallback(int errorCode);
public interface ICallbackRegistrar
{
event CompletionCallback Completed;
}
public class CallbackRegistrar : ICallbackRegistrar
{
private RegisterCallbackDelegate toCall;
public CallbackRegistrar(Delegate toCall)
{
this.toCall = toCall;
}
public event CompletionCallback Completed
{
add
{
this.toCall.DynamicInvoke(value);
}
remove { }
}
}
public ICompletionRegistrar SendMessageToServer(Message m)
{
// do work
return new CompletionRegistrar(
new delegate(CompletionCallback toCall)
{
this.messageIdToCallbackLookup[m.Id] = toCall;
}
);
}
public void HandleMessageReceivedFromServer(Message m)
{
if (m.CorrelationId != null)
{
CompletionCallback toCall;
this.messageIdToCallbackLookup.TryGetValue(m.CorrelationId, toCall);
if (toCall != null)
{
toCall(m.ErrorCode);
this.messageIdToCallbackLookup.Remove(m.CorrelationId);
}
}
}