Posts in Category "async"

State Machine Transitions

In my initial posting on state machines I necessarily skipped over a lot of detail that I’d like to start filling in. Let’s start by going back to this comment:

  // switch to stateNorthSouthGreen

In this state machine implementation, the current state is represented by a function, and that function is stored in the m_state member variable. Switching states is therefore almost as simple as just updating this variable. Let’s wrap it in a function with a helpful name:

function transition( newState:Function ):void {
// it's not quite this simple...
m_state = newState;
}

Recall that the dispatch() function simply forwards all events to the current state function. The new state function will receive all subsequently dispatched events. Continuing with our traffic light example, that means that CarEvents, for example, will continue to be dispatched, but now to our new state function.

It’s often useful to be able to do some additional work when entering or exiting a state. For example, a traffic light might might want to start a timer when entering a state which turns on the walk signal. This behavior can be added directly to the state functions by dispatching a couple of new events. Let’s define them first:

class StateMachineEvent extends Event {
public static const ENTER:String = "enter";
public static const EXIT:String = "exit";
...
}

Then we refine our implementation of transition():

function transition( newState:Function ):void {
m_state( new StateEvent( StateMachineEvent.EXIT ));
m_state = newState;
m_state( new StateEvent( StateMachineEvent.ENTER ));
}

(In other state machine models, individual states are sometimes modeled as classes. If you think of states that way, the enter and exit events are like the constructor and destructor, respectively.)

Finally, we add some code to our state function to handle these new events. I like to use the following skeleton for state functions:

function stateFunction( event:Event ):void {
switch( event.type ) {
case StateMachineEvent.ENTER:
// start Timers, etc.
break;
case ...:
// handle various other events
break;
case StateMachineEvent.EXIT:
// stop Timers, clean up , etc.
break;
default:
// Unexpected event! Maybe throw?
break;
}
}
}

Async APIs and State Machines

Continuing my posts on dealing with async APIs, I’ve been hinting that there options beyond rafts of callbacks functions, be they anonymous or not. So let’s get on to it–I’ve been promising this post for a while.

Briefly, state machines model the behavior of a system as a set of discrete states, events which are accepted in each state, and actions–including changing states–which occur when inputs are received. State machines turn out to be particularly well-suited to asynchronous programming because they don’t force you to organize your logic in sequential steps.

For example, imagine programming a controller for a sensored traffic light. The events it deals with–the arrival of a car, or a push on the crosswalk button–are inherently asynchronous. It’s difficult to write code that, given the arrival of an event, then examines which lights are red, which are green, determines which to change, and so on. It’s much easier to organize your code around the state of the light–which are green, which are red–and then, for every state, specify how each event is handled.

Now there’s more than one way to implement state machines in code, but I’m going to use the implementation proposed by [Samek]. Let’s start translating this to code:


class TrafficLight {
var m_state:Function = stateEastWestGreen;

function stateEastWestGreen( event:Event ):void ...
function stateNorthSouthGreen( event:Event ):void ...

Note that I’m modeling each state as a function and the events that can occur are modeled as, well, events. (You see how well this fits together!) Now a real traffic light probably has more states then this, but it’ll do as an example.

Each state has to deal with all possible events that it might receive and want to react to. For example, suppose I have traffic sensors that dispatch CarEvent each time a sensor is triggered:


function stateEastWestGreen( event:Event ):void {
if( event.type == CarEvent.CAR ) {
var carEvent:CarEvent = event as CarEvent;
if( carEvent.direction == CarEvent.NORTH_SOUTH ) {
// switch to stateNorthSouthGreen
} else {
// We're already green in this direction; nothing to do
}
} else {
// Some other type of event...
}
}

Now I’m not a traffic engineer, but it’s easy for me to read this code and realize that yes, when the light is green in one direction, if a car arrives in the other direction I’m going to want to eventually change the lights for it. For now I’m skipping over the details of turning the lights yellow, etc. but we’ll get back to that.

One more snippet for today, and that’s to get the events from our sensors into our state function. Still inside the TrafficLight class:


function dispatch( event:Event ):void {
m_state( event );
}

function TrafficLight() {
northSouthSensor.addEventListener( CarEvent.CAR, dispatch );
eastWestSensor.addEventListener( CarEvent.CAR, dispatch );
}

Note that I register the same callback function for all events I listen for, and that it in turn simply forwards them to the function that represents the current state. Et voila.

References

[Samek] Practical Statecharts in C/C++: Quantum Programming for Embedded Systems, Miro Samek, CMP Books, 2002.

Async APIs and Anonymous Functions

ActionScript has a nifty feature called anonymous functions that is often handy when working with asynchronous APIs. Here’s how it works:

var t:Timer = new Timer( 0 );
var message:String = “Hello, World!”;
t.addEventListener( TimerEvent.TIMER_COMPLETE, function( evt:Event ):void { trace( message ); });

Here, function creates a new, anonymous function taking one argument (an event) and with the body specified inline. I don’t recommend using this technique for long event handler functions; those are usually easier to write as callback functions. But this is a great technique to use when your callback function is short. Replacing a whole set of short callback functions with anonymous functions is a great code cleanup.

Note in the example above that the function body is able to reference message, which is a variable that was defined back in the code that created this function. Creating an anonymous function creates a closure that captures the in-scope variables visible where the function is defined and makes them available when the function executes. This saves you the trouble of littering your class with member variables that exist just to remember stuff until an event handler gets called. Another great code cleanup.

This wraps it up for the basic techniques for asynchronous programming. Next we’ll start in on more advanced topics.

Async APIs and Garbage Collection

In my previous post I made a point of calling out the need to keep a reference on a FileStream object while waiting for an asynchronous operation, such as a read, to complete. Although this behavior is not necessarily intuitive, I thought it at least had the advantage of being consistent across the Flash and AIR APIs.

It turns out I was incorrect about this and that some related objects, such as URLStream, are guaranteed to remain live until after they have dispatched a completion event. This behavior is both more useful and more intuitive; after all, it only makes sense that there would be a reference to the URLStream somewhere in the system while it’s hanging around receiving data.

Futhermore, a particular case of the must-keep-a-reference-for-yourself mode APIs was causing a lot of grief: File.browseForDirectory() and company. You see, garbage collection doesn’t happen immediately. So even though you might not have a reference to an object to keep it alive, it can still hang around for a while if the collector hasn’t gotten around to cleaning it up. But the File.browse…() methods, being interactive, tend to take much longer to complete. That pretty much guarantees that the collector is going to run, find them, and wham–no more browse dialog.

After discussing this internally, we’ve decided the URLStream behavior should be used wherever possible. It will apply to File, FileStream, and the like. The one case that we’re unlikely to change is Timer, and that’s out of concern for existing use of the API. (Timer is a Flash API and has shipped in a final release; we don’t have as much flexibility in changing it was we do the AIR APIs while we’re still pre-1.0.)

I’m confident that this change will eliminate a whole lot of hard-to-find bugs from a whole lot of applications.


Async APIs and Callback Functions

OK, let’s start digging into asynchronous APIs–starting simple.

Perhaps the most basic and obvious technique for dealing with asynchronous APIs is to deal with the events via callback functions. For example:

class SomeClass {

var fs:FileStream = new FileStream();

function someFunction( file:File ):void {

fs.addEventListener( Event.COMPLETE, onFileStreamComplete );
fs.openAsync( file, FileMode.READ );

}

When the file has been completely read, the function onFileStreamComplete will be "called back" by the FileStream. At that point all the bytes of the file are available to read:

function onFileStreamComplete( event:Event ):void {

var firstInt:int = fs.readInt();
// ... etc. ...

I said we’d start simple, and that’s almost all there is to this approach. However, there is one subtle issue to be aware of. Note in the example above I made the FileStream a member variable of SomeClass. This is convenient because it makes the FileStream easily available in the callback function.

Making the FileStream a member variable is also essential. If the FileStream were instead declared inside someFunction(), it would go out of scope when someFunction() returned and could be garbage collected before the file is read or events are delivered. Many people are surprised by this, but note that the only reference to the FileStream object is the member variable. The FileStream object has references on event listeners, etc. but only references to an object will protect it from collection.

Now, this will almost never happen to you because (a) the garbage collector runs incrementally and it’ll take it a while to collect the FileStream object and (b) most file reads will finish and dispatch a complete event long before then. But it can happen, which of course means it’ll probably only show up when doing big important demos.

While this is a fine technique for simple programs, there are a couple of problems with it for larger endeavors:

  1. The number of callback functions increases quickly as program size goes up. It becomes difficult to keep track of each callback and what it does.
  2. Code for performing a single logical operation (i.e., read the contents of a file) is spread across multiple functions, making it more complex than necessary.

Tune in next time for techniques that help mitigate these problems.

Dealing with Asynchronous APIs

A not uncommon complaint when developing for AIR is that asynchronous APIs–especially the filesystem and database APIs–are difficult to use. Why didn’t we make all of these APIs synchronous? So you can write better applications.

In AIR, all ActionScript execution runs on the same thread that updates the display. If your code gets stuck for a long time doing something–like reading a file–then the display won’t get redrawn until your code is done. Your application becomes unresponsive, which users hate.

To avoid this, use the async APIs. They schedule work on background threads and then notify you when it’s done. They don’t block the main thread, thus allowing your app to keep responding even while the work is being done. You’ll get notified when the background work is done via an event.

Does this mean using the async APIs is easy? Absolutely not. In some cases, such as the filesystem API, we offer synchronous alternatives. These are a perfectly good choice if you’re confident that the amount of work you need to do is small and won’t take long. But beware that this isn’t as easy to determine as it might seem: even reading a small file can take a while if it happens to be read from a slow network drive.

You can’t avoid using the async APIs forever, so I recommend also learning some techniques that make them easier to use. In upcoming posts I’ll describe various techniques for accomplishing this, discuss their trade-offs, and share some code that I use to manage asynchronous operation with the runtime itself.