diff --git a/src/lime/app/Future.hx b/src/lime/app/Future.hx index 39f74e566..5e8af5877 100644 --- a/src/lime/app/Future.hx +++ b/src/lime/app/Future.hx @@ -84,8 +84,7 @@ import lime.utils.Log; } #end - FutureWork.forMode(useThreads ? MULTI_THREADED : SINGLE_THREADED) - .run(dispatchWorkFunction, doWork, promise); + FutureWork.forMode(useThreads ? MULTI_THREADED : SINGLE_THREADED).run(dispatchWorkFunction, doWork, promise); } } @@ -331,7 +330,8 @@ import lime.utils.Log; @return A new `Future` instance. @see lime.system.ThreadPool **/ - public static function withEventualValue(doWork:WorkFunction WorkOutput -> Void>, ?state:State, mode:ThreadMode = #if html5 SINGLE_THREADED #else MULTI_THREADED #end):Future + public static function withEventualValue(doWork:WorkFunctionWorkOutput->Void>, ?state:State, + mode:ThreadMode = #if html5 SINGLE_THREADED #else MULTI_THREADED #end):Future { var future = new Future(); var promise = new Promise(); @@ -345,7 +345,7 @@ import lime.utils.Log; /** (For backwards compatibility.) Dispatches the given zero-argument function. **/ - @:noCompletion private static function dispatchWorkFunction(doWork:WorkFunction T>, output:WorkOutput):Void + @:noCompletion private static function dispatchWorkFunction(doWork:WorkFunctionT>, output:WorkOutput):Void { output.sendComplete(doWork.dispatch()); } @@ -389,6 +389,7 @@ enum FutureStatus @:dox(hide) class FutureWork { public static var singleThread(get, null):FutureWork; + private static inline function get_singleThread():FutureWork { if (singleThread == null) @@ -400,6 +401,7 @@ enum FutureStatus #if lime_threads public static var multiThread(get, null):FutureWork; + private static inline function get_multiThread():FutureWork { if (multiThread == null) @@ -411,6 +413,7 @@ enum FutureStatus #end public static var totalActiveJobs(get, never):Int; + private static inline function get_totalActiveJobs():Int { return singleThread.activeJobs #if lime_threads + multiThread.activeJobs #end; @@ -420,7 +423,8 @@ enum FutureStatus private static function forMode(mode:ThreadMode):FutureWork { #if lime_threads - if (mode == MULTI_THREADED) { + if (mode == MULTI_THREADED) + { return multiThread; } #end @@ -431,7 +435,7 @@ enum FutureStatus // Because `Promise` is `@:generic`, we can't always store it as `Promise`. // Instead, we'll store the specific methods we need. - private var promises:Map Dynamic, error:Dynamic -> Dynamic, progress:Int -> Int -> Dynamic}> = new Map(); + private var promises:MapDynamic, error:Dynamic->Dynamic, progress:Int->Int->Dynamic}> = new Map(); public var minThreads(get, set):Int; public var maxThreads(get, set):Int; @@ -480,6 +484,7 @@ enum FutureStatus { return threadPool.minThreads; } + private inline function set_minThreads(value:Int):Int { return threadPool.minThreads = value; @@ -489,6 +494,7 @@ enum FutureStatus { return threadPool.maxThreads; } + private inline function set_maxThreads(value:Int):Int { return threadPool.maxThreads = value; diff --git a/src/lime/system/ThreadPool.hx b/src/lime/system/ThreadPool.hx index 7713efc73..f02d0eb03 100644 --- a/src/lime/system/ThreadPool.hx +++ b/src/lime/system/ThreadPool.hx @@ -38,7 +38,7 @@ import lime._internal.backend.html5.HTML5Thread as Thread; trigger an `onComplete` event on the main thread. @see `lime.system.WorkOutput.WorkFunction` for important information about - `doWork`. + `doWork`. @see https://player03.com/openfl/threads-guide/ for a tutorial. **/ #if !lime_debug @@ -69,7 +69,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.__ @@ -152,16 +152,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. @@ -180,6 +183,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; @@ -390,6 +394,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; @@ -448,7 +453,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; @@ -462,6 +467,7 @@ class ThreadPool extends WorkOutput // Do it all again. } }); + // @formatter:on } #end @@ -519,8 +525,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; @@ -664,33 +669,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 @@ -834,7 +862,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) @@ -869,17 +898,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 a35b5fb71..3d746bdf2 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. @@ -351,7 +355,8 @@ class JobData var EXIT = "EXIT"; } -typedef ThreadEvent = { +typedef ThreadEvent = +{ var event:ThreadEventType; @:optional var message:Dynamic; @:optional var job:JobData; @@ -379,7 +384,6 @@ class JSAsync } // Define platform-specific types - #if target.threaded // Haxe 3 compatibility: "target.threaded" can't go in parentheses. #elseif !(cpp || neko)