Created
November 22, 2013 06:42
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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