This is the second in a series of posts about the AsyncPattern in WCF.
However, a question still remains: when and how to use AsyncPattern = true on the service-side?
- The WCF documentation states that
“Use an asynchronous approach in a service operation implementation if the operation service implementation makes a blocking call, such as doing I/O work. When you are in an asynchronous operation implementation, try to call asynchronous operations and methods to extend the asynchronous call path as far as possible
- Similarly, Kenny Wolf (from the WCF/WF group) states that
and also that
If you aren’t doing something that’s “natively async”, then you shouldn’t be using AsyncPattern=true. That is, you shouldn’t just create a thread just for the sake of performing “background work” as part of an asynchronous operation
My conjecture is that asynchronous operation implementation should be used when there are conditions to release the calling thread, without requiring the creation of a new thread, and the time needed for the operation completion is significant. This happens when the operation performs “lengthy” IO requests. If the IO interface has an asynchronous interface, then the asynchronous operation call can be “linked” to the asynchronous IO call (see diagram below). Note that a service request is just a special kind of an IO request and that WCF also provides an asynchronous interface for clients.
The following sequence diagram shows the “linkage” between the asynchronous operation implementation and an asynchronous IO request.
- The WCF dispatcher calls the BeginOperation method, which in turn starts the asynchronous IO request (BeginIO) and returns immediately,. This sequence is done on an IO thread, which is “released” after BeginOperation returns.
- When the IO request is completed, the Operation Callback is called on an IO thread (not necessarily the same from the above sequence). This callback:
- Calls EndIO to finish the IO request and collects the IO result.
- Calls the WCF provided callback (passed as a parameter to BeginOperation).
- The WCF callback then calls EndOperation to finish the operation and collect the operation result.
Notice that the operation’s execution is divided into two sequences (two threads):
- The “left-to-right” sequence processes the request, running on the IO thread associated with the service request.
- The “right-to-left” sequence produces the reply, running on the IO thread associated with the IO request completion.
Namely there is no blocked thread between the start of the operation and the completion of the IO. This property is important in scenarios were there can be a significant amount of operations waiting for “lengthy” IO requests (e.g. middle-tier services).
The OperationContractAttribute is used, in WCF, to define operations on a service contract. This attribute contains the AsyncPattern property that can be used to implement or use services asynchronously.
AsyncPattern at the service side
Consider the following excerpt
- The first [OperationContract], which has AsyncPattern set to false (the default), defines one request-response operation called ‘Operation1′. This operation is implemented synchronously by one method: Operation1. Synchronously means that the WCF dispatcher will call the Operation1 method, passing the RequestData. This call will block during the execution of the operation. Finally, the method returns the ReplyData that will be used in the response sent to the client.
- The second [OperationContract], which has AsyncPattern set to true, also defines one request-response operation called ‘Operation2′. However, this operation is implemented asynchronously by two methods: BeginOperation2 and EndOperation2. Asynchronously (see Asynchronous Programming Overview) means that:
- The WCF dispatcher will call the BeginOperation2 method, passing the RequestData, an AsyncCallback and some state information.
- This method should start the execution of the operation and return an IAsyncResult immediately.
- When the execution of the operation is completed, then the AsyncCallback should be called.
- This AsyncCallback, implemented by the WCF runtime, will then call EndOperation2 to collect the ReplyData that will be used in the response sent to the client.
Note that, when using AsyncPattern set to true, there is no longer a 1-to-1 relation between the service contract methods and the service operations. Namely, the BeginOperation2 and EndOperation2 methods are both associated to a single operation named ‘Operation2′.
Also, from the outside of the service, both operations (‘Operation1′ and ‘Operation2′) are seen as request-response operations. This is visible in the generated WSDL description
So, concluding, the AsyncPattern is an implementation detail of the service: it defines the communication pattern between the WCF runtime and the service implementation but not the communication pattern between the service client and the service.
The Information Card Foundation will support and guide industry efforts to enable the
development of an open, trusted and interoperable identity layer for the Internet that
maximizes control over personal information by individuals. To do so, the Information
Card infrastructure will use existing and emerging data exchange and security protocols,
standards and software components.
The first event of the Application Lifecycle Management cycle, organized by Microsoft Portugal, is announced here.
From the online preface, whose reading I recommend:
This is not the easiest book on beginning programming; it is not meant to be. I just aim for it to be the easiest book from which you can learn the basics of real-world programming. That’s quite an ambitious goal because much modern software relies on techniques considered advanced just a few years ago.
From Philip Wadler’s blog: Add FP to the ACM curriculum.
This SIGPLAN Proposal has the details.
The only given explanation was:
In a related post, the blog’s author stated that
why does BeginInvoke break when all the TP [Transparent Proxy]and RP [Real Proxy] plumbing is in place? This question is much trickier than it seems to be, after a bit of research on the default implementation of TP and RP mechanism used by .NET remoting using both .NET reflector and the rotor 2.0 implementation of CLR, I finally figure out that for the current implementation of TP and RP mechanism, it only supports asynchronous call when the default RemotingProxy is in place, since the ServiceChannelProxy is WCF’s own implementation, it gets ignored by the BeginInvoke mechanism, and the BeginInvoke call against WCF’s proxies will be performed synchronously.
This explanation was not completely clear to me, however it provided some clues to the reasons why the asynchronous call could block.
So, my next steps were to grab the SSCLI source code and analyze how BeginInvoke is implemented. My observations follow:
- When a compiler defines a delegate class, the BeginInvoke method does not contain any implementation. Instead, this method is annotated with the MethodImplAttribute attribute, with the MethodCodeType field set to MethodCodeType.Runtime. This means that the BeginInvoke implementation will be given by the runtime.
- In SSCLI, the class COMDelegate (defined in the comdelegate.h and comdelegate.cpp files) represents the native methods of the Delegate class. In this C++ class, the GetInvokeMethodStub method is used to get the stubs for each of the synchronous and asynchronous invoke methods. For the BeginInvoke method, this stub is returned by the TheAsyncDelegateStub function (defined in remoting.h).
- The stub returned by the TheAsyncDelegateStub method is created by the CTPMethodTable::EmitSetupFrameCode method (defined in remotingx86.h). This last method emits x86 assembly code that includes a call to the CTPMethodTable::OnCall method (defined in remoting.cpp).
- The CTPMethodTable::OnCall, among other things, tests if the target of the delegate is a transparent proxy.
- If it IS NOT a transparent proxy:
- the OnCall method calls the RemotingProxy.Invoke static method, which performs the expected ThreadPool.QueueUserWorkItem call.
- However, if it IS a transparent proxy:
- the OnCall method obtains the real proxy behind the transparent proxy, and calls the RealProxy.PrivateInvoke method.
- This method ends up calling the virtual Invoke method of ServiceChannelFactory (the type of real proxy behind the transparent proxy returned by a ChannelFactory<T>). This method checks if the called channels’ method is synchronous, and if so completes the call synchronously, despite the call being made via a delegate’s BeginInvoke.
So, in conclusion:
- A delegates’s BeginInvoke method call is handled by the remoting infrastructure. Aparently, this so to optimize the asynchronous remoting calls, avoiding extra context switchs (see this post by Chris Brumme).
- If the target of the delegate is not a transparent proxy, then a regular ThreadPool.QueueUserWorkItem is performed.
- If the target is a transparent proxy, then the BeginInvoke call is processed by the real proxy. If the delegate’s method is synchronous, then the WCF’s real proxy performs a synchronous service call.
Note, however, that an asynchronous delegate call on the synchronous method of the “client proxy” generated by svcutil.exe will not block waiting for the service’s response. This is so because this “client proxy” is a regular class and not a transparent or real proxy, so the “normal” BeginInvoke behavior applies.
This does not mean that using asynchronous delegate calls on the synchronous “client proxy” methods is recommended. The appropriate method is to use the /async switch of the svcutil.exe tool to generate a service interface and a “client proxy” with asynchronous methods, as described in the original Nicholas Allen’s post.