Introducing CoThreadAS3: A library to easily write threads in AS3/Flash/Flex/AIR

TL;DR;
CoThread lets you to create cooperative “threads” and coroutines in Actionscript 3 (Flex/AIR).

With CoThread, you can easily break up any task, including recursive functions, so that your UI stays responsive even while your application is doing significant processing. Asynchronous code that uses CoThread is easy to read and write.

CoThreadAS3 is hosted on GitHub and mirrored on BitBucket.

The below is the wordy behind-the-scenes version of the README file.

Motivation for CoThread

I have been looking for a way to write “threaded” Actionscript code for a while. All of the examples I have seen require you to manually break up your code into discreet blocks, and also require you to manually maintain the state of your operation between calls. Not only is this difficult and error-prone, it is extremely difficult to write recursive functions that can be arbitrarily paused and resumed.

To show how hard this is, imagine that you need to process every pixel in a large image:

function processAllPixels(pixels : Array) : void
{
	var numPixels : int = pixels.length;
	for (x=0; x<numPixels; ++x)
	{
		var pixel : int = pixels[x];
		processPixel(pixel);
	}
}

That’s fine, but you discover it takes 5 seconds to process all the pixels, and your boss doesn’t want the program to lock up while it’s working. Oh, and he wants a progress bar. Other examples I’ve seen told me to write a function that just processes some of the pixels with each call:

var whereLeftOffLastTime : int = 0;
static const int NUM_PIXELS_TO_PROCESS_EACH_FRAME = 500;
function continueProcessingPixels(pixels : Array) : Boolean
{
	var isDoneYet : Boolean = false;
	var numPixels : int = pixels.length;
	var indexToStop = whereLeftOffLastTime + NUM_PIXELS_TO_PROCESS_EACH_FRAME;
	if (indexToStop >= numPixels)
	{
		indexToStop = numPixels;
		isDoneYet = true;
	}
	for (x=whereLeftOffLastTime; x<indexToStop; ++x)
	{
		var pixel : int = pixels[x];
		processPixel(pixel);
	}
	whereLeftOffLastTime = indexToStop;
	return isDoneYet;
}

Anyway, you get the basic idea. I wrote the above code in a readme file, so it’s probably got compile errors and logic errors, and I’m sure it could be cleaner, but that’s also kinda my point. Just to split a for loop, I find myself keeping track of a lot of extra state. It’s slow and error-prone.

After I familiarized myself with the above techniques, I decided to apply it to my XML file saving code. It took a long time to write my files to disk, and I wanted to break it up. There was just one problem: My XML saving code was recursive! If I want to make sure that my XML processor only ran for 30ms each call, I might want it to arbitrarily stop when it was 20 function calls deep. How would I save and restore that state? Let me give you a simple recursive example:

//Recursively pretty-prints XML, indenting each deeper level
public function printXML(xml : XML, indentString : String) : void
{
	if ((xml == null) || (xml.name() == null))
	{
		return;
	}
	else
	{
		indentString += "\t";
		_textWriter.writeLine(indentString + xml.name()+":"+xml.text());
		for each(var child : XML in xml.children())
		{
			printXML(child, indentString);
		}
		_textWriter.writeLine(indentString + "/"+xml.name());
	}
}

(Yeah, I know this could be written without recursion, but the code to save a file to XML could not easily be, and this is a simple recursive example to illustrate my point.)

So imagine that you discover it takes 5 seconds to process your 250MB XML file. What are you going to do? How are you going to split this task up so that when you’re 20 function calls deep, your code knows how to stop what it’s doing, and then resume the next time you call it? That is a much harder problem. I knew I could use closures to save a significant amount of state automatically, but I couldn’t figure out a clean way to use them the way I wanted. So I gave up…

Then I stumbled across BrokenFunction’s json code. He had an example of an async json parser. It was recursive, but asynchronous. It could pause itself and resume at semi-arbitrary points. It was brilliant. I took his ideas and expounded on them to write a library I call CoThread. Here is an asynchronous version of the function above:

public function printXMLAsync(xml : XML, indentString : String, context : CoRoutineContext) : void
{
	if ((xml == null) || (xml.name() == null))
	{
		return;
	}
	else
	{
		indentString += "\t";
		_textWriter.writeLine(indentString + xml.name()+":"+xml.text());
		context.foreach(xml.children(),
			function printEachChild(child : XML) : Boolean
			{
				printXMLAsync(child, indentString, context);
				return true;
			},
			this,
			function afterDonePrintingChildren() : void
			{
				_textWriter.writeLine(indentString + "/"+xml.name());
			}
		);
	}
}

Here’s what I like about that code: It’s not extremely different from the synchronous version of the code. I didn’t have to manually save a lot of state. It just works.

In that example I gave the anonymous functions (printEachChild and afterDonePrintingChildren) a name so that they were easier to read, but I don’t have to do that.

Let’s take a look at our pixel example from earlier. Here’s an asynchronous version of that function:

public function processAllPixelsAsync(pixels : Array, context : CoRoutineContext) : void
{
	context.foreach(pixels,
		function(pixel : int) : Boolean
		{
			processPixel(pixel);
			//Returns true to continue the for loop
			return true;
		},
		this
	);
}

Again, this code isn’t terribly different from the original version, and I really like that.

Calling an asynchronous function

All asynchronous functions in the CoThread library take a “CoRoutineContext” as its last parameter. This context is required by the function in order to operate as a CoRoutine (asynchronously), and it has the added benefit of advertising to the world “I am an asynchronous function. I might return immediately, but I probably still have work to do.” To get a CoRoutineContext, you just need a CoThread:

//Instantiate a CoThread for the startPrinting function
var sampleThread : CoThread = new CoThread(startPrinting);

//Start the thread. (passes in its CoRoutineContext)
sampleThread.start();

function startPrinting(context : CoRoutineContext) : void
{
	printXMLAsync(sampleXML, "", context);
}

The function printXMLAsync now has a context on which to operate. Furthermore, it can now call any other asynchronous methods it wants, and can simply pass in its own CoRoutineContext. So let’s say that as part of your XML pretty printing, you want to do some Pixel processing.

Here is our original printEachChild function from printXMLAsync:

function printEachChild(child : XML) : Boolean
{
	printXMLAsync(child, indentString, context);
	return true;
}

Perhaps some of the children of the XML contain a bitmap we want to process:

function printEachChild(child : XML) : Boolean
{
	if (child.name() == "pixels")
	{
		var pixels : Array = getPixelData(child);
		//pass our context into the async processing function
		//processAllPixelsAsync might take 20 minutes to truly complete, but this
		//printEachChild function won't get called again for the next XML node
		//until it's done processing
		processAllPixelsAsync(pixels, context);
	}
	else
	{
		printXMLAsync(child, indentString, context);
	}
	return true;
}

And processAllPixelsAsync has a CoRoutineContext, so it can in turn call other asynchronous functions quite easily.

Chaining async calls

But when you call an asynchronous function, it can return almost immediately even though it might have 5 minutes of work left to do. Well, the context can come to our rescue again.

Let’s go back to our XML printing example. Recall this function:

function startPrinting(context : CoRoutineContext) : void
{
	printXMLAsync(sampleXML, "", context);
}

Imagine we want to do something after we print the XML. You can’t just do this:

function startPrinting(context : CoRoutineContext) : void
{
	printXMLAsync(sampleXML, "", context);
	//You can't just do this:
	trace("I'm done printing the XML!")
}

That’s because the call to printXMLAsync will just start printing the XML. It won’t finish printing all of the XML. So we need to tell our context, “After you’re done with that function, here’s the next function I want you to call.”

We do that with the “pushFunction” call:

function startPrinting(context : CoRoutineContext) : void
{
	//Tell the context to call "afterDonePrinting" when it returns from the next asynchronous call
	context.pushFunction(afterDonePrinting);
	printXMLAsync(sampleXML, "", context);

	function afterDonePrinting() : void
	{
		trace("I'm really done printing the XML!")
	}
}

This is relatively readable, but still a bit verbose, and I don’t like that I read the pushFunction call before I read the printXMLAsync call. It doesn’t read in the same order the code is called. Luckily, context.pushFunction returns the context as its return parameter, so we can do this instead:

function startPrinting(context : CoRoutineContext) : void
{
	printXMLAsync(sampleXML, "",
		context.pushFunction(afterDonePrinting));

	function afterDonePrinting() : void
	{
		trace("I'm really done printing the XML!")
	}
}

That’s a bit better. If you want to be even less verbose, you can’t just use an anonymous function:

function startPrinting(context : CoRoutineContext) : void
{
	printXMLAsync(sampleXML, "", context.pushFunction(
		function() : void
		{
			trace("I'm really done printing the XML!")
		})
	);

}

I personally find this the most readable style most of the time. I can write plenty of code inside that anonymous function, including further asynchronous calls, and still manage to read and write it linearly.

Performance

There is a performance penalty for these threaded functions. Instantiating and calling Closures is slower than a normal function call. In my tests using the Debugger, performing these functions takes twice as long as their non-threaded counterparts. I haven’t profiled it extensively to discover the bottlenecks, so it’s quite possible that we can bring it down, but even if we can’t, I think there are still times when you would be willing to pay such a performance penalty. My first use-case for it was saving our budget files without halting the UI of our app. I was happy to have a background save take twice as long as a foreground save. Who cares if it takes an extra few seconds? 5 seconds in the background is MUCH better for the user than 2.5 seconds of an unusable app!

Try CoThread yourself

Although I’m biased, CoThread is my personal favorite “threading” library for ActionScript. It lets me easily write threaded functions without having to wrap my head around a lot of new concepts or put lots of effort into breaking up my code and saving state.

There’s a lot more to the CoThread library, but that probably belongs in the documentation.
You can learn more at the CoThread project page on GitHub, or the mirror on BitBucket.

Note: (Adobe will eventually introduce Worker Threads, and that might well make this code obsolete, but in the meantime I thought I’d post my library because it’s usable today and I think it’s cool.)

Comments ( 2 )

Native signing not supported on mac

When packaging your AIR app for the captive runtime on the Mac, you will get the following error if you don’t have your parameters in the right order:
Native signing not supported on mac

I didn’t see that phrase anywhere else on the internet, which is always fun. (I must be a trailblazer!)

The answer is to make sure that you have your parameters like so (this works for me anyway):

adt -package -storetype pkcs12 -storepass PASSWORD -keystore ../../Path/cert.p12 -target bundle MyApp.app MyApp-app.xml (What you want to package goes here)

See Adobe help for another example.

Comments ( 2 )

FIXED: Framework linkage in Flash Builder keeps getting reset to “Merged into code”

Each time I would re-open Flash Builder, our Flex Library projects would get their “Framework linkage” settings in the “Flex Library Build Path” project properties reset to “Merged into code” instead of keeping my preferred setting of “Use default (external).” I couldn’t figure out why until I noticed one day that any new Flex Library project I created did not have this problem. After doing some folder/file comparisons, I figured out how to fix it.

The fix

In your Flex Library project folder, open this file: .flexLibProperties
(For those of us on Macs, this file is hidden since it begins with a period.)

The first line of the file will look like this:
<flexLibProperties includeAllClasses="false" useMultiPlatformConfig="false" version="1">/code>

Change the version value to "3" instead:
<flexLibProperties includeAllClasses="false" useMultiPlatformConfig="false" version="3">/code>

Now, close and re-open your project.
Change the "Framework linkage" back to "Use Default (external)" it will retain its framework linkage setting next time it is re-opened!

Why it happened

I think this was due to a bug in the way Flash Builder upgraded these projects, since it was only happening with projects that we had originally created in earlier versions of Flash Builder.

Comments ( 1 )

Painfully slow MXML editing in Flash Builder 4.0 and 4.5

We have been seeing an issue where in some of our MXML code, the editing gets painfully slow. As I type, it can take many seconds for the characters to appear on screen. It makes some of our components basically un-editable within Flash Builder.
I’m trying to get a good repro together for Adobe, and today I narrowed down the issue in one of our components to the presence of a mx:DataGrid tag with multiple attributes.

Take a look at this video I made illustrating the issue.

Has anyone else experienced this?

UPDATE (05/31/2011): I can get around it by subclassing DataGrid as something like DataGrid2. I don’t actually do anything different in my subclass. I just use that in my component instead of referencing DataGrid, and it works like a champ again.

P.S. We had already disabled Design mode a long time ago, so unfortunately, that is not the culprit in this particular case. I’ve also attempted to disable everything “content-assist” related within the MXML Content-assist settings, and that didn’t seem to help. I look forward to discovering a simple setting that can bring the performance back!

Comments ( 7 )

We are looking for an amazing Designer

I work for a small, profitable, growing company that needs more and more design work done. We need everything from UI/UX, to brand/identity, to educational print materials designed. In short, if you work for us, you’ll get to shape just about every client interaction we have. It will be fun!
Check out the job description.

Comments ( 0 )

A simple monkey patch to double Flex 4 instantiation performance

(That headline is a bit of hyperbole, but it was true for our project!)
UPDATE: According to Andrei (see comment below), this fix also halved the startup time of their app! Time to try to add some more keywords to this article in hopes that more folks will stop by and give this a shot: Flex 4 slow vs Flex 3. How to speed up Flex 4. Flex 4 performance. Also, Alex Harui recently posted on his blog about Flex 4 performance issues, and he mentions that their StyleProtoChain can be slow, and provides an alternate way of getting perf back. I haven’t tested his method though. If you do, I’d love to hear how it compares. And now on with the article…

We are in the process of upgrading our AIR app, You Need a Budget, to use Flex 4. We’re basically trying to change as little as possible, but we need to use Flex 4 for a new library of code we purchased. That means we’re still using our old MX controls, and we’re using the Halo theme. However, we quickly discovered a major performance bottleneck. Our app was taking at least twice as long to load as it did before. So, I loaded up the debugger and randomly started breaking into the code. Virtually every time I broke into the code, I was in the same darn function:

StyleProtoChain::matchStyleDeclarations

For every component being instantiated, Flex was doing a linear search through all of our styles and asking each of them, “Do you apply to this component being instantiated?” We have a stylesheet with about 300 declarations in it, so that meant that Flex was making 300 queries per object instantiated. In short, it was slow.

So, I monkey-patched StyleProtoChain.as to make it cache these style lookups. No more linear searches! My fix is quick and dirty, so there are scenarios where the styles could get changed out from under the cache. Although it appears to work great for us, I can imagine lots of scenarios where it will break, so your mileage may vary. Still, my guess is that you’ll see a tremendous speed improvement. Our load times went down by more than half. Any time I can get performance increases like this with so little code, I want to share it with the world.

This is a monkey patch of Flex 4.1, so I don’t know how/if it will work for Flex 4.0 or 4.5. To find my changes look for the “//TB” comments or anything related to the matchUniversalStyleDeclarations function I added.

If you’ve never monkey patched Flex before, just drop this file into your project in an “mx/styles” folder. Do a full rebuild, and I believe you’ll be good to go. You might have to reference StyleProtoChain elsewhere in your project to guarantee it gets compiled in, but I don’t recall.

Comments ( 7 )

Be careful how you “cast” Error objects in AS3!

I was doing some testing of my crash reporting class I wrote for our AIR app. The getStackTrace method I was calling on the error object though was completely wrong. Here is the code I had:

protected static function getExtraReportInfo(error : Object, userEmail : String, userDescription : String) : String
{
    var stackTrace : String;
    if (error is Error)
    {
      stackTrace = Error(error).getStackTrace();
    }
}

That was always returning the stack trace of the getExtraReportInfo function! It was NOT returning the stack trace of the error when it was thrown! I then tried this line of code instead, and it worked:

stackTrace = (error as Error).getStackTrace();

The reason it worked is that the Error(error) is not actually a cast. I believe it’s constructing a new Error instance based on the error object I’m passing in. The second example above IS a real cast, so I got the results I was expecting.

I wish casting syntax was not the same as construction syntax. Then we could avoid these sorts of bugs. I don’t normally do these sorts of inline casts anyway, but here is my working code now:

protected static function getExtraReportInfo(errorObj : Object, userEmail : String, userDescription : String) : String
{
     var stackTrace : String = "";
     var castError : Error = errorObj as Error;

      if (castError != null)
      {
        stackTrace = castError.getStackTrace();
      }
}
Comments ( 0 )

Moving a revision/changeset/patch from SVN to Mercurial

When I converted our SVN repository to Mercurial (Kiln), I left out some of our branches. Last night I wanted to get them ported back over to our Mercurial repository, and here’s how I did it:

  1. Browsed the old SVN repository using Tortoise SVN.
  2. Went to “View Log” for the branch I wanted to port over.
  3. In the log, I selected the first revision of the branch (where it was branched from trunk), and the last revision of the branch.
  4. Right-clicked, and clicked “View difference as unified diff”
  5. Copied the text that appeared and pasted it in a file called: branchPatch.txt
  6. Noted the date that I had originally made the branch in SVN. Let’s call it SVN Change # 500, on June 8, 2010
  7. In Mercurial, viewed history to find the corresponding changeset in Mercurial for SVN Change #500 on June 8th. Let’s call it revision #100
  8. Updated to that revision:
    hg up 100
    

  9. Made a branch:
    hg branch OldBranches/MyBranchName
    hg commit -m "Creating a branch"

  10. Imported the patch:
    hg import -p 0 --no-commit ../branchPatch.txt
    


    -p 0 tells it to use a “strip count” of 0. Otherwise, when applying the patch from SVN, it will try to strip the first part of the path off when finding files it needs to modify. You’ll get error messages that it couldn’t apply the patch.
    –no-commit makes the changes to your working folder instead of submitting each of the changes separately. Without this, my first attempt gave me 12 separate revisions in Mercurial, one for each file I modified.

  11. Commit the changes:
    hg commit -m "Now Oldbranches/MyBranchName has been moved over from SVN"
    

Warning: This does not preserve full history of the branch. Your corresponding branch in mercurial will contain the final state of the branch in SVN, but it will not contain the intermediate revisions to get you there. I didn’t care in my case because my branches were small and only had a few revisions leading up to their final state.

Comments ( 0 )

I’ve applied to be a speaker at Adobe MAX 2010

I learned a lot over the past year and a half since I started working with Adobe Flex/AIR, and I really want to give the talk that I wish someone had given me at Adobe MAX 2008. It would basically be a, “Here is what to look out for when you’re writing your first large Flex/AIR application, and here is a repository of source code to save yourself a few hundred hours of work”.

  • Cross platform gotchas
  • Localizing/Globalizing (how to cheaply include every currency and date format in the world)
  • Adding license key protection
  • Team workflow (source control and build system gotchas)
  • Smooth badge-based deployment
  • Adding crash reporting to your app (even in pre AIR 2.0 code)
  • Quickly adding a help system to your app
  • How to improve the built-in AIR auto-update mechanism
  • Custom component gotchas
  • Large scale QA/testing on a shoestring budget
  • Elegantly adapting code to the single threaded environment
  • Emulating type-safe containers
  • Reducing compile times

It would be more of a “wide dive” instead of the “deep dive” talks that are all of the rage, but the source code supplement would hopefully fill in the gaps. Whether I am accepted or not, I’ll put this talk together for my local Austin Flex user group, and I’ll post the source code and materials here.

Comments ( 2 )

Text “link” event won’t fire unless selectable=”true”

I couldn’t figure out why the link in my Text element’s htmlText field wasn’t firing. Turns out I had selectable=”false” on the Text element, and changing it to be selectable=”true” made it work again. Hopefully someone will see this when they do the same Google search I just did looking for this problem. :)

Comments ( 0 )
Page 1 of 512345