2/19/2009

Actionscript and Concurrency (Part I of III)

This is a three part series from a talk I gave at the Atlanta Flex and Flash User Group meeting in February. Original slides are here.



I can hear it now Actionscript can't do that. And some of you might say what is concurrency? Concurrency goes by a lot of names, but it simply means doing two things at once. More accurately stated doing two things simultaneously. Now I can hear what you might say "But, Flash already does that? I mean I can animate two objects on the screen at once." While that's true, Flash is optimized for animation not for general purpose concurrency. In fact the design of animation is so foundational it governs everything that happens in Flash.

Timeline


If you're a Flash developer I'm sure you're familiar with the timeline. The timeline is divided into a series of frames, and Flash executes those frames at a particular rate one after the other. For flex applications the rate is 24 frames / second. If you do the math that means each frame lasts a little more than 40ms. The timeline is very natural for designers. But, for developers this concept is a little strange because it's hard to understand where my code is executed?

Event Queue


For developers I think a thought experiment helps clarify the timeline. Say you were hired to implement the timeline concept. What data structure would help you in doing this task? The answer is surprisingly simple and bears a lot of resemblance to most UI toolkits out there. At the heart of the Flash platform there exists a queue of events. The Event Queue, as it's known, is where all code is triggered. Every piece of code in Flex and Flash is related back to some event being triggered. So when you move your mouse, click a button, type on the keyboard, set timers they all go onto the queue. Flash then pops off those events from the queue and executes them one by one. When one event is done processing the next event is processed. There's no way any two events can be processed at the same time. It all happens one at a time.

Now in Flash even those frames from the timeline are modeled as events. The difference between them and other events is that frame events must occur at certain points in time. Unlike mouse or keyboard which can wait till the next frame to be processed. Frame events must be processed every ~40ms (or 1000 ms / 24).

That means if one event takes too long to process Flash can't update the screen, process other events, or doing anything. What happens is Flash locks up and you get a pinwheel of death or a Not Responding next to your application. In fact Flash punishes such acts and stops processing mouse, keyboard, or button clicks till it catches up. Why? Well that because if you block the queue from processing it won't update that nice animation, and Flash prioritizes that over anything else.

Rule number one is that any code you write must execute within the time span of a frame. If not you run the risk of having Flash coming down on you. This leads us to the second rule which is Flash is not concurrent. There is no way for Flash to update the UI and run your job at the same time.

Timeslicing


So what do you do if you have a long running job? Well the answer lies in how animation works in Flash. Animating several objects at once requires a series of many smaller steps. Say you want to move a ball from one side of the screen to the other. That means you need to move it a little, redraw, move it some more, redraw, etc until the ball is at the other side of the screen. In a way animation is chopping up a long running job into several smaller jobs that run once per frame. We can do that too by chopping up our job into many smaller jobs, and executing them a little at time until we're done!

So how might you do this? Well there are several tricks you can use, and they all perform the same. It's more a matter of taste in which one you choose, but remember technically there is no real difference between these. Our next part we'll look at each of these techniques and discuss unique problems when doing concurrent actions in Flash.

4 comments:

meshgraphics said...

Good articles, Chuck. I got a lot out of it.

I've been looking around for a way to pause or throtle the SWFLoader class (in Flex).

In it's simplist form, I've got two SWFLoader tags in a Flex app. The smaller one
is something like an intro but the annimation gets frozen by the drag of the big swf
getting loaded.

From what I can tell from the Flex docs, I'll have to dig into the framework code.

-------
mx:SWFLoader id="introMovie" source="media/gussy.swf" width="150" /
mx:SWFLoader id="bigMovie" autoLoad="false" source="media/BigmxmlApp.swf" width="640"/

-------


Any thoughts?
Jon

Sean said...

I'm just leaving this comment to clarify the "Event Queue" concept. I was trying to debug some very event-intensive actionscript and had to go as far as the virtual machine source code to clarify some of the behavior. The term 'queue' can be very misleading:

- Events are pretty much the same as function calls. 'Stack' might be a more appropriate term. When you dispatch an event, all of it's side effects, including other events it dispatches, will be dealt with before your next line of code is executed.

- While this code is executing, the user is still able to move the mouse, press keys, etc... and timers are going off. With the possible exception of frame events- as you pointed out - this kind of input is queued, and dealt with once the stack is otherwise empty. The event listeners are triggered one at a time, and create new 'stacks'.

chubbsondubs said...

@Sean

Any event sent through the dispatch() method will be sent to the listeners immediately. Just like any other function call would. So dispatch() will block until all notification has finished.

The exception to this is if one of those listeners chooses to use invokeLater() which would place that function call onto the EventQueue. You can control this behavior yourself by surrounding your call to dispatch with invokeLater() too. Rule: The only way an event you create can get into the EventQueue is through invokeLater(). All other events are just regular function calls.

The stack you are referring to is simply the program's call stack. Just like any language has uses function calls. The program stack allows one function to wait on the results of another function.

The mouse, keyboard, etc could be thought of as simple functions that call invokeLater with their results (that's not actually how it works, but conceptually you can't tell the difference).

chubbsondubs said...

@meshgraphics

Sorry I missed your comment. For some reason I didn't get an email when you posted it.

It sounds like your second graphic is dragging down the entire system. Maybe it's too big. My guess is BigxmlApp is hogging the CPU on load and that's causing an overall drag on gussy. Try breaking them apart and just load gussy. Then try creating a new SWF that does a minimal amount of work. Try running that new SWF and gussy together. Does it lag like before?

If not, then you know BigxmlApp.swf is the culprit to the slowness. Try optimizing just that SWF. Figure out what's taking so long. Using the profiler in Flex Builder would help out. However, disabling things that load on startup would also work too. Try turning off certain callbacks to see which one is dragging it down. Once you find that one then you could use the GreenThread approach to asynchronously load stuff, and allow flash to service the other loader. Or you might find some simple optimization will do it.