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_StatePending = 0
private final c_StateCompletedSynchronously = 1
private final c_StateCompletedAsynchronously = 2
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_AsyncCallback, self)

// 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_operationId = null

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_AsyncWaitHandle = null // 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
mre = ManualResetEvent(done)

if Interlocked.CompareExchange(m_AsyncWaitHandle, mre, null) != 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_result = null 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