Skip to content

Instantly share code, notes, and snippets.

@lavie
Created November 22, 2013 06:42
Show Gist options
  • Save lavie/7595828 to your computer and use it in GitHub Desktop.
Save lavie/7595828 to your computer and use it in GitHub Desktop.
Idle Worker ("background" processing) in Flex. This code is not self contained and relies on some internal libraries, but those aren't important for the understanding of this pattern, which is the aim of this gist.
package com.gigantt.utils
{
import flash.events.Event;
public interface IBackgroundWorker
{
// Do stuff before running - Optional
function prepare() : void;
// Post processing work - Optional. Returns event to be raised by CPU.
function finishUp() : Event;
// Return a function that executes the next chunk of work - Mandatory
function getNextChunk() : Function;
function describe() : String;
function progress() : Number; // Returns 0..1
function priority() : Number; // 0..1, 0 == top priority
}
}
package com.gigantt.logic
{
import com.gigantt.utils.IBackgroundWorker;
public interface ICPU
{
function process(bw : IBackgroundWorker) : void;
function cancelJobs(keepCriterion : Function) : void;
function hasWork() : Boolean;
}
}
package com.gigantt.logic
{
import com.gigantt.framework.Gig;
import com.gigantt.utils.IBackgroundWorker;
import com.gigantt.utils.Log;
import com.gigantt.utils.array;
import flash.events.Event;
import flash.events.EventDispatcher;
import mx.utils.StringUtil;
public class IdleCPU extends EventDispatcher implements ICPU
{
private var _idleQueue : Array = [];
public function process(bw : IBackgroundWorker) : void
{
_idleQueue.push(bw);
_idleQueue.sort(function(left : IBackgroundWorker, right : IBackgroundWorker) : int
{
return left.priority() - right.priority();
});
}
public function cancelJobs(criterionToStay : Function) : void
{
var count : int = _idleQueue.length;
var cancelled : Array = [];
_idleQueue = array.filter(_idleQueue, criterionToStay, cancelled);
if (count != _idleQueue.length)
{
Log.debug(StringUtil.substitute("Cancelled {0} jobs", count - _idleQueue.length));
// for each (var job : IBackgroundWorker in cancelled)
// Log.debug(StringUtil.substitute("Job cancelled: {0}", job.describe()));
}
Gig.notify("gigantt_cancel_scheduling");
}
public function executeChunk() : void
{
try
{
if (!hasWork())
return;
var next : IBackgroundWorker = _idleQueue[0];
var chunk : Function = next.getNextChunk();
if (chunk == null)
{
_idleQueue.shift();
var e : Event = next.finishUp();
if (e != null)
dispatchEvent(e);
dispatchEvent(new WorkerProgressEvent(next));
}
else
{
chunk();
dispatchEvent(new WorkerProgressEvent(next));
}
}
catch (er : Error)
{
Log.exception(er, "executeChunk");
}
}
public function hasWork() : Boolean
{
return _idleQueue.length > 0;
}
}
}
protected function application1_enterFrameHandler(event : Event) : void
{
try
{
pumpIdleQueue();
}
catch (e : Error)
{
Log.exception(e, "enterFrame");
}
}
private var _frameTimes : BoundedCyclicQueue = new BoundedCyclicQueue(10);
private var _frameCounter : int = 0;
public static const IDLE_THRESHOLD_FRAME_RATE : Number = 24;
public function pumpIdleQueue() : void
{
if (!cpu.hasWork())
return;
lastFrames.push(new Date());
if (lastFrames.length > FRAMES_TO_COUNT)
lastFrames.shift();
if (lastFrames.length == FRAMES_TO_COUNT)
{
var runningAvg : Number = ((lastFrames[FRAMES_TO_COUNT - 1] as Date).time - (lastFrames[0] as Date).time) / (FRAMES_TO_COUNT - 1);
var actualFrameRate : Number = 1000 / Math.max(0.0001, runningAvg);
var idleThreshold : Number = IDLE_THRESHOLD_FRAME_RATE;
var thisRoundStartTime : Number = (new Date()).time;
while (actualFrameRate > idleThreshold)
{
try
{
cpu.executeChunk();
}
catch (e : Error)
{
Log.exception(e, "idle queue");
}
var now : Number = (new Date()).time;
runningAvg += (now - thisRoundStartTime);
actualFrameRate = 1000 / Math.max(0.0001, runningAvg);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment