How to Implement IAsyncResult in Boo


In the previous post I gave a very brief introduction to Boo, and some pointers on where to learn more. Hopefully you had some time to look into it a bit since in this post we are diving in.

Before we can look at compiler extensibility, we need to have a place to start. Since we used our AsyncResult example to show how to use templates, it makes sense to port it to Boo and use it to see how compiler extensibility may help with the same problem. We can then compare the two approaches.

Porting the code

Once you get past a certain point of familiarity with Boo, porting the AsyncResultNoResult and AsyncResult implementations is pretty straight forward.

In Boo an example class that implementing an asynchronous operation would look something like this:

class MyClass:
    def constructor():
        pass
        
    def BeginSend(
        host as string,
        port as int,
        buffer as (byte),
        offset as int,
        size as int,
        asyncCallback as AsyncCallback
        state as objectas IAsyncResult:
            
        result = SendAsyncResult(host,port,buffer,offset,size,asyncCallback,state,self,"BeginSend")
        
        result.Process()
        
        return result
        
    def EndSend(result as IAsyncResult):
        AsyncResultNoResult.End(result, self"BeginSend")

 

This is the same network send operation that we used previously, but you may notice in Boo, that without the braces it takes a bit less space.

The main program to call this class is shown here:

// Program
def SendCallback(result as IAsyncResult):
    try:
        c1 = result.AsyncState as MyClass
        c1.EndSend(result)
    except exception:
        print "Send failed:", exception.Message
    ensure:
        print "Completed."
    
print "Hello, World!"

c = MyClass()
buffer = System.Text.Encoding.UTF8.GetBytes("GET /")

r = c.BeginSend(host, 80, buffer, 0, buffer.Length, SendCallback, c)

print "Press any key to continue . . . "
Console.ReadKey(true)

You can set host to a web server’s host name and it should asynchronously send a GET request to the server and disconnect.  Note that Boo’s equivalent of try/catch/finally is try/except/ensure.

AsyncResultNoResult

The translated code for AsyncResultNoResult is shown below:

internal partial class AsyncResultNoResult(IAsyncResult):
"""Implements common functionality for all asynchronous operations."""
    // Fields set in constructor
    private m_AsyncCallback as AsyncCallback
    private m_AsyncState as object
    
    // Fields set at construction that change
    private final c_StatePending0
    private final c_StateCompletedSynchronously1
    private final c_StateCompletedAsynchronously2
    private m_CompletedState as int = c_StatePending
    
    // Field may or may not get used based on usage
    private m_AsyncWaitHandle as ManualResetEvent
    
    // Field set when operation completes
    private m_exception as Exception
    
    // The object which started the operation
    private m_owner as object
    
    // Used to verify BeginXXX and EndXXX calls match    
    private m_operationId as string
        
    protected def constructor(
        asyncCallback as AsyncCallback
        state as object
        owner as object
        operationId as string):
        m_AsyncCallback = asyncCallback
        m_AsyncState = state
        m_owner = owner
        if String.IsNullOrEmpty(operationId):
            m_operationId = String.Empty
        else:
            m_operationId = operationId
            
    internal virtual def Process():
        pass
        
    protected def Complete(exception as Exception) as bool:
        return self.Complete(exception, false /*completedSynchronously*/);
            
    protected def Complete(exception as Exception, completedSynchronously as bool):
        result = false
        
        // The m_CompletedState field MUST be set prior to calling the callback
        newState = c_StateCompletedAsynchronously
        if completedSynchronously:
            newState = c_StateCompletedSynchronously
            
        prevState = Interlocked.Exchange(m_CompletedState, newState)
        if prevState == c_StatePending:
            // Passing null for exception means no error occurred
            // This is the common case
            m_exception = exception
            
            // Do any processing before completion
            self.Completing(exception, completedSynchronously)
            
            // If the event exists, set it
            if m_AsyncWaitHandle != null:
                m_AsyncWaitHandle.Set()
                
            self.MakeCallback(m_AsyncCallbackself)
            
            // Do any final processing after completion
            self.Completed(exception, completedSynchronously)
            
            result = true
            
        return result
        
    private def CheckUsage(owner as object, operationId as string):
        if not object.ReferenceEquals(owner, m_owner):
            raise InvalidOperationException(
                "End was called on a different object than Begin.")
            
        if object.ReferenceEquals(null, m_operationId):
            raise InvalidOperationException(
                "End was called multiple times for this operation.")
            
        if not String.Equals(operationId, m_operationId):
            raise ArgumentException(
                "End operation type was different than begin.")
            
        // Mark that end was already called
        m_operationIdnull
        
    public static def End(
        result as IAsyncResult
        owner as object
        operationId as string):

        asyncResult = result as AsyncResultNoResult
        
        if asyncResult == null:
            raise ArgumentException(
            "Result passed represents an operation not supported by this framework."
            "result")
            
        asyncResult.CheckUsage(owner, operationId)
        
        // This method assumes that only 1 thread calls EndInvoke for this object
        if not asyncResult.IsCompleted:
            // If the operation isn't done, wait for it
            asyncResult.AsyncWaitHandle.WaitOne()
            asyncResult.AsyncWaitHandle.Close()
            asyncResult.m_AsyncWaitHandlenull // Allow early GC
            
        // Operation is done: if an exception occurred, throw it
        if asyncResult.m_exception != null:
            raise asyncResult.m_exception
            
    public AsyncState as object:
        get:
            return m_AsyncState
            
    public CompletedSynchronously as bool:
        get:
            return Thread.VolatileRead(m_CompletedState) == c_StateCompletedSynchronously
            
    public AsyncWaitHandle as WaitHandle:
        get:
            if m_AsyncWaitHandle == null:
                done = IsCompleted
                mreManualResetEvent(done)
                
                if Interlocked.CompareExchange(m_AsyncWaitHandle, mrenull) != null:
                    // Another thread created this object's event; dispose 
                    // the event we just created
                    mre.Close()
                else:
                    if not done and IsCompleted:
                        // If the operation wasn't done when we created
                        // the event but now it is done, set the event
                        m_AsyncWaitHandle.Set()
                        
            return m_AsyncWaitHandle

    public IsCompleted as bool:
        get:
            return Thread.VolatileRead(m_CompletedState) != c_StatePending
    
    protected def Completing(
        exception as Exception, 
        completedSynchronously as bool):
        pass
        
    protected def MakeCallback(
        callback as AsyncCallback
        result as AsyncResultNoResult):
        // If a callback method was set, call it
        if callback != null:
            callback(result)
    
    protected def Completed(
        exception as Exception, 
        completedSynchronously as bool):
        pass
    

A couple of interesting things to note about the port, is the use of C# like keywords such as public, protected, private, and internal to indicate the accessibility of the method or property. This is not very Python like.

In addition the keyword final is like the C# keyword cost.  For implementing properties, you can use get and set keywords to specify the getter and setter. The C# keyword partial, can be used in the same way in Boo.

AsyncResult

The implementation of AsyncResult, which uses generics to specify the return type of the asynchronous operation, is shown below:

internal partial class AsyncResult[of TResult](AsyncResultNoResult):
    // Field set when operation completes
    private m_resultnull as TResult
    
    internal Result as TResult:
        get:
            return m_result
            
    protected def constructor(
        asyncCallback as AsyncCallback
        state as object
        owner as object
        operationId as string):
        super(asyncCallback,state,owner,operationId)
        
    protected def SetResult(result as TResult):
        m_result = result
        
    public static def End(
        result as IAsyncResult
        owner as object
        operationId as string):
        asyncResult = result as AsyncResult[of TResult]
        if asyncResult == null:
            raise ArgumentException(
                "Result passed represents an operation not supported by this framework.""result")
            
        // Wait until operation has completed
        AsyncResultNoResult.End(result, owner, operationId)
        
        // Return the result (if above didn't throw)
        return asyncResult.Result
                               

Other than the generic argument TResult, it also uses super to call the base class constructor similar to base used in C#.  There is also an override of a static method End.

Summary

In this post, I presented the AsyncResult implementation ported to Boo. From this port, you can get a more detailed look at how Boo implements many of the same keywords you find in C#.  Now that we have our framework ported, we can look at compiler extensibility of Boo.

Series

Start of series previous next

20110613_IAsyncResultBoo.zip

Skip to main content