What is ContextDelegate?
ContextDelegate is an abstraction that simplifies the delivery of callbacks into any event driven, single-threaded context. A context delegate can be described as a delegate with a built-in affinity to the “context” in which it is created. When such a delegate is invoked, the target method is invoked asynchronously in the original context in which the delegate was created. When the callback-recipient context is a UI context (e.g. WinForms, WebForms), the callback is dispatched on a UI thread within event boundaries. When the callback-recipient context has no special requirements, the callback is dispatched on any available thread (possibly the calling thread).
Why is ContextDelegate Needed?
Fundamentally, a client application is an event processing engine. Application code is typically comprised of visual and non-visual objects that collectively execute in an event driven single-threaded context. In more complex client applications, the roles of the visual and non-visual objects are formalized using a Model-View-Controller design pattern.
At any moment, only one event is being processed by the client application. Most Frameworks deliver system level asynchronous activity (e.g. key press, mouse click or move events) as simple events that are serially processed by the client application. No user interface object ever needs to allow for the possibility of multiple system events racing with each other on multiple threads.
With the advent of multi-threaded client applications, it becomes necessary for the application to deal with non-system asynchronous events (e.g. arrival of data from a server, completion of a long calculation). Historically, it has been left to the application developer to arrange for these events to be safely processed by the same context (i.e. thread) that processes system events. Most resulting ad-hoc solutions involved writing a complex and error-prone mix of thread-safe and single-threaded logic. This in turn, ultimately destroyed the original simplicity and predictability of the event-driven, single-threaded UI model.
ContextDelegate provides a transparent way to deliver any application generated asynchronous event with the same contract as system level events. This allows these events to be handled with the same simplicity as system level events (e.g. keyboard clicks).
What is a Practical Example?
A typical pattern for publish/subscribe client applications involves a set of one or more threads that subscribe to published updates from a publisher and invoke callbacks for processing of those updates. The callbacks must be executed on the client (i.e. UI) thread. The callbacks must be delivered in a manner that maintains responsiveness of the user interface, even when the frequency of updates is high.
ContextDelegate handles the necessary plumbing to make this possible with a minimum of effort by the application developer.
While there are several types involved in the facility, only the
ContextDelegate class is used by the client. The multithreaded service is unaware that it is dealing with a context-sensitive callback. A typical usage idiom involves the following events.
- The client code creates a “regular” .NET delegate.
- The client code calls the
static ContextDelegate.Create() method to create a context-sensitive delegate from the regular .NET delegate that is passed as the only parameter. A
Delegate type is returned from the
Create() method. The returned delegate refers to the original delegate and implies a strong reference to the target object.
- The client code “passes” the returned “context-sensitive” delegate returned from the
Create() method to a multithreaded service.
- The multithreaded service invokes the “context-sensitive” delegate.
- The context-sensitive delegate invokes the original delegate in the context specified by the callback creator.
If the target method for the delegate is decorated with the
[ThreadNeutral] attribute, the
ContextDelegate.Create() method does not perform any special marshalling. This makes it possible for the
ContextDelegate facility to transparently support context-sensitive and context-insensitive observers.
There are classes, interfaces, structs, and attributes involved in the
|Name ||Type ||Purpose |
|struct ||Multithread-safe counter used by the callback scheduler to maintain statistics about the number of pending callbacks for a specific context. |
|class ||The Context for a callback consists of a scheduler, a queue of pending callbacks, and a map of delegate facades. |
|class ||The public class used by the client code to create a context-sensitive delegate. |
|class ||Default Factory which recreates a .NET delegate with no facade. |
|class ||Provides a facade for a .NET delegate that supports context-sensitive dispatch. |
|class ||Used to recreate context delegates. Advanced functionality not used by typical application code. |
|interface ||Interface used to recreate a context delegate from the underlying method and target object. Advanced functionality not used by typical application code. |
|interface ||Interface used to access the underlying method or target object. Advanced functionality not used by typical application code. |
|interface ||The interface implemented by context-sensitive delegate schedulers. |
|class ||A non-display window used to schedule callbacks by the STAScheduler. |
|class ||A scheduler for standard apartment model threads (i.e. WinForms client code). This scheduler ensures that callbacks are delivered in a manner that ensures user interface responsiveness. |
|class ||A scheduler for ASP.NET client applications. |
|attribute ||Methods with this attribute will have context-sensitive delegates invoked on any available thread including the calling thread. |
|class ||Singleton class that exposes selected Win32 API calls used in message dispatch. |
|class ||A map that maintains only weak references to keys and values. |
WeakMap is used by the
Context class to hold facades.
ContextDelegate facility currently supports .NET delegates with the following parameter signatures:
(object sender, EventArgs args)
Facade class can be extended to support other signatures. The facility currently supports a scheduler for WinForms and ASP.NET applications.
The classes in the
ContextDelegate facility interact in the following manner.
|1||Creates a |
Context with an associated scheduler and dispatch queue.
|2||Calls the |
CreateDelegate method of
Context to create a
Facade for the requested delegate signature.
|3||Creates a |
Facade for the requested delegate signature.
|5||Queues pending callbacks for the associated scheduler. Calls the |
Schedule method of the associated scheduler to schedule the dispatch of the callback.
|4||Calls the |
Post method of
Context to schedule a callback.
|6||Calls the |
Post method of the
MessageWnd to post a
|7||Supplies the message procedure to handle the message posted to the |
MessageWnd. This message procedure is called in the context (i.e. thread) of the client that created the scheduler.
|8||Calls the |
Dispatch method of
Context to actually invoke the callback.
Visual Studio 2005 projects for the
ContextDelegate implementation and a sample WinForms client application are provided in ZIP file attachments. Use the CtxDelegateSample.sln to build and execute
ContextDelegate and the sample.
ContextDelegate implementation runs in production with high update rate multithreaded services. Nevertheless, there are some aspects of the implementation that can be enhanced or further generalized. These are appropriately marked in the source code.
The sample application displays updates from a background subscriber thread on a listbox. The background thread is unaware that it is calling a method that requires UI context. The delegate used by the background thread is passed, along with update rate and update count. The callback from the thread procedure is marshaled to the UI thread by
ContextDelegate. The updates are displayed in the UI thread and the user interface remains completely responsive even with high update rates.
In future articles, more advanced usage of
ContextDelegate by infrastructure services will be explored. An ASP.NET sample application will be provided. Solutions for high update rate environments (e.g. real-time market data), within an MVC pattern, will be described.