diff --git a/src/lime/app/Future.hx b/src/lime/app/Future.hx index d409dced8e..64f36c5200 100644 --- a/src/lime/app/Future.hx +++ b/src/lime/app/Future.hx @@ -319,7 +319,7 @@ import lime.utils.Log; @:dox(hide) class FutureWork { private static var threadPool:ThreadPool; - private static var promises:Map Dynamic, error:Dynamic -> Dynamic}>; + private static var promises:MapDynamic, error:Dynamic->Dynamic}>; public static var minThreads(default, set):Int = 0; public static var maxThreads(default, set):Int = 1; @@ -328,7 +328,8 @@ import lime.utils.Log; @:allow(lime.app.Future) private static function run(work:Void->T, promise:Promise):Void { - if(threadPool == null) { + if (threadPool == null) + { threadPool = new ThreadPool(minThreads, maxThreads, MULTI_THREADED); threadPool.onComplete.add(threadPool_onComplete); threadPool.onError.add(threadPool_onError); diff --git a/src/lime/system/ThreadPool.hx b/src/lime/system/ThreadPool.hx index 4eb9c3c7eb..9dca868885 100644 --- a/src/lime/system/ThreadPool.hx +++ b/src/lime/system/ThreadPool.hx @@ -88,7 +88,7 @@ class ThreadPool extends WorkOutput frame. See `workIterations` for instructions to improve the accuracy of this estimate. **/ - public static var workLoad:Float = 1/2; + public static var workLoad:Float = 1 / 2; /** __Access this only from the main thread.__ @@ -171,16 +171,19 @@ class ThreadPool extends WorkOutput Dispatched at most once per job. **/ public var onComplete(default, null) = new EventVoid>(); + /** Dispatched on the main thread when `doWork` calls `sendError()`. Dispatched at most once per job. **/ public var onError(default, null) = new EventVoid>(); + /** Dispatched on the main thread when `doWork` calls `sendProgress()`. May be dispatched any number of times per job. **/ public var onProgress(default, null) = new EventVoid>(); + /** Dispatched on the main thread when a new job begins. Dispatched exactly once per job. @@ -199,6 +202,7 @@ class ThreadPool extends WorkOutput @:deprecated("Instead pass the callback to ThreadPool.run().") @:noCompletion @:dox(hide) public var doWork(get, never):PseudoEvent; + private var __doWork:WorkFunctionWorkOutput->Void>; private var __activeJobs:JobList; @@ -409,6 +413,7 @@ class ThreadPool extends WorkOutput **/ private static function __executeThread():Void { + // @formatter:off JSAsync.async({ var output:WorkOutput = #if html5 new WorkOutput(MULTI_THREADED) #else cast(Thread.readMessage(true), WorkOutput) #end; var event:ThreadEvent = null; @@ -467,7 +472,7 @@ class ThreadPool extends WorkOutput // Work is done; wait for more. event = interruption; } - else if(Reflect.hasField(interruption, "event")) + else if (Reflect.hasField(interruption, "event")) { // Work on the new job. event = interruption; @@ -481,6 +486,7 @@ class ThreadPool extends WorkOutput // Do it all again. } }); + // @formatter:on } #end @@ -538,8 +544,7 @@ class ThreadPool extends WorkOutput // `workLoad / frameRate` is the total time that pools may use per // frame. `workPriority / __totalWorkPriority` is this pool's // fraction of that total. - var maxTimeElapsed:Float = workPriority * workLoad - / (__totalWorkPriority * Application.current.window.frameRate); + var maxTimeElapsed:Float = workPriority * workLoad / (__totalWorkPriority * Application.current.window.frameRate); var startTime:Float = timestamp(); var timeElapsed:Float = 0; @@ -683,33 +688,56 @@ class ThreadPool extends WorkOutput } @:access(lime.system.ThreadPool) @:forward(canceled) -private abstract PseudoEvent(ThreadPool) from ThreadPool { +private abstract PseudoEvent(ThreadPool) from ThreadPool +{ @:noCompletion @:dox(hide) public var __listeners(get, never):Array; - private inline function get___listeners():Array { return []; }; + + private inline function get___listeners():Array + { + return []; + }; + @:noCompletion @:dox(hide) public var __repeat(get, never):Array; - private inline function get___repeat():Array { return []; }; - public function add(callback:Dynamic -> Void):Void { + private inline function get___repeat():Array + { + return []; + }; + + public function add(callback:Dynamic->Void):Void + { function callCallback(state:State, output:WorkOutput):Void { callback(state); } #if (lime_threads && html5) - if (this.mode == MULTI_THREADED) - throw "Unsupported operation; instead pass the callback to ThreadPool's constructor."; + if (this.mode == MULTI_THREADED) throw "Unsupported operation; instead pass the callback to ThreadPool's constructor."; else - this.__doWork = { func: callCallback }; + this.__doWork = {func: callCallback}; #else this.__doWork = callCallback; #end } public inline function cancel():Void {} + public inline function dispatch():Void {} - public inline function has(callback:Dynamic -> Void):Bool { return this.__doWork != null; } - public inline function remove(callback:Dynamic -> Void):Void { this.__doWork = null; } - public inline function removeAll():Void { this.__doWork = null; } + + public inline function has(callback:Dynamic->Void):Bool + { + return this.__doWork != null; + } + + public inline function remove(callback:Dynamic->Void):Void + { + this.__doWork = null; + } + + public inline function removeAll():Void + { + this.__doWork = null; + } } class JobList @@ -853,7 +881,8 @@ class JobList // Getters & Setters - private inline function set___addingWorkPriority(value:Bool):Bool { + private inline function set___addingWorkPriority(value:Bool):Bool + { if (pool != null && __addingWorkPriority != value && ThreadPool.isMainThread()) { if (value) @@ -888,17 +917,25 @@ class JobList that's in use by multiple jobs, the wrong job may be selected or canceled. **/ @:forward -abstract JobIdentifier(JobIdentifierImpl) from JobIdentifierImpl { - @:from private static inline function fromJob(job:JobData):JobIdentifier { +abstract JobIdentifier(JobIdentifierImpl) from JobIdentifierImpl +{ + @:from private static inline function fromJob(job:JobData):JobIdentifier + { return ID(job.id); } - @:from private static inline function fromID(id:Int):JobIdentifier { + + @:from private static inline function fromID(id:Int):JobIdentifier + { return ID(id); } - @:from private static inline function fromFunction(doWork:WorkFunctionWorkOutput->Void>):JobIdentifier { + + @:from private static inline function fromFunction(doWork:WorkFunctionWorkOutput->Void>):JobIdentifier + { return FUNCTION(doWork); } - @:from private static inline function fromState(state:State):JobIdentifier { + + @:from private static inline function fromState(state:State):JobIdentifier + { return STATE(state); } } diff --git a/src/lime/system/WorkOutput.hx b/src/lime/system/WorkOutput.hx index a35b5fb71c..48db2a5e2d 100644 --- a/src/lime/system/WorkOutput.hx +++ b/src/lime/system/WorkOutput.hx @@ -13,12 +13,10 @@ import neko.vm.Deque; import neko.vm.Thread; import neko.vm.Tls; #end - #if html5 import lime._internal.backend.html5.HTML5Thread as Thread; import lime._internal.backend.html5.HTML5Thread.Transferable; #end - #if macro import haxe.macro.Expr; @@ -54,6 +52,7 @@ class WorkOutput available on this target, `mode` will always be `SINGLE_THREADED`. **/ public var mode(get, never):ThreadMode; + #if lime_threads /** __Set this only via the constructor.__ @@ -65,6 +64,7 @@ class WorkOutput Messages sent by active jobs, received by the main thread. **/ private var __jobOutput:Deque = new Deque(); + /** Thread-local storage. Tracks whether `sendError()` or `sendComplete()` was called by this job. @@ -77,6 +77,7 @@ class WorkOutput Will be null in all other cases. **/ public var activeJob(get, set):Null; + @:noCompletion private var __activeJob:Tls = new Tls(); private inline function new(mode:Null) @@ -171,7 +172,8 @@ class WorkOutput var thread:Thread = Thread.create(executeThread); #if html5 - thread.onMessage.add(function(event:ThreadEvent) { + thread.onMessage.add(function(event:ThreadEvent) + { __jobOutput.add(event); }); #end @@ -195,6 +197,7 @@ class WorkOutput { return __activeJob.value; } + private inline function set_activeJob(value:JobData):JobData { return __activeJob.value = value; @@ -261,8 +264,8 @@ abstract WorkFunction(T) from T to T { switch (self.typeof().follow().toComplexType()) { - case TPath({ sub: "WorkFunction", params: [TPType(t)] }): - return macro ($self:$t)($a{args}); + case TPath({sub: "WorkFunction", params: [TPType(t)]}): + return macro($self : $t)($a{args}); default: throw "Underlying function type not found."; } @@ -275,8 +278,8 @@ abstract WorkFunction(T) from T to T only accepts a single argument, you can pass multiple values as part of an anonymous structure. (Or an array, or a class.) - // Does not work: too many arguments. - // threadPool.run(doWork, argument0, argument1, argument2); + // Does not work: too many arguments. + // threadPool.run(doWork, argument0, argument1, argument2); // Works: all arguments are combined into one `State` object. threadPool.run(doWork, { arg0: argument0, arg1: argument1, arg2: argument2 }); @@ -299,6 +302,7 @@ typedef State = Dynamic; class JobData { private static var nextID:Int = 0; + /** `JobData` instances will regularly be copied in HTML5, so checking equality won't work. Instead, compare identifiers. @@ -339,6 +343,7 @@ class JobData } #if haxe4 enum #else @:enum #end abstract ThreadEventType(String) + { // Events sent from a worker thread to the main thread var COMPLETE = "COMPLETE"; @@ -351,7 +356,8 @@ class JobData var EXIT = "EXIT"; } -typedef ThreadEvent = { +typedef ThreadEvent = +{ var event:ThreadEventType; @:optional var message:Dynamic; @:optional var job:JobData; @@ -379,7 +385,6 @@ class JSAsync } // Define platform-specific types - #if target.threaded // Haxe 3 compatibility: "target.threaded" can't go in parentheses. #elseif !(cpp || neko)