Make concurrency classes support Haxe 3.
This commit is contained in:
@@ -13,14 +13,16 @@ using haxe.macro.TypedExprTools;
|
||||
#else
|
||||
// Not safe to import js package during macros.
|
||||
import js.Browser;
|
||||
import js.html.MessageEvent;
|
||||
import js.html.URL;
|
||||
import js.html.Worker;
|
||||
import js.html.*;
|
||||
import js.Lib;
|
||||
#if haxe4
|
||||
import js.lib.Function;
|
||||
import js.lib.Object;
|
||||
import js.lib.Promise;
|
||||
import js.Syntax;
|
||||
#else
|
||||
import js.Promise;
|
||||
#end
|
||||
// Same with classes that import lots of other things.
|
||||
import lime.app.Application;
|
||||
#end
|
||||
@@ -86,15 +88,12 @@ class HTML5Thread {
|
||||
url.hash += __workerCount;
|
||||
__workerCount++;
|
||||
|
||||
// Prepare to send the job.
|
||||
job.makePortable();
|
||||
|
||||
// Create the worker. Because the worker's scope will not include a
|
||||
// `window`, `HTML5Thread.__init__()` will add a listener.
|
||||
var thread:HTML5Thread = new HTML5Thread(url.href, new Worker(url.href, {name: url.hash}));
|
||||
var thread:HTML5Thread = new HTML5Thread(url.href, new Worker(url.href));
|
||||
|
||||
// Send a message to the listener.
|
||||
thread.sendMessage(job);
|
||||
thread.sendMessage(job.toMessage());
|
||||
|
||||
return thread;
|
||||
#else
|
||||
@@ -120,13 +119,15 @@ class HTML5Thread {
|
||||
**/
|
||||
public static macro function readMessage(block:ExprOf<Bool>):Dynamic
|
||||
{
|
||||
var jsCode:Expr = macro #if haxe4 js.Syntax.code #else untyped __js__ #end;
|
||||
|
||||
// `onmessage` events are only received when the main function is
|
||||
// suspended, so we must insert `await` even if `block` is false.
|
||||
// TODO: find a more efficient way to read messages.
|
||||
var zeroDelayExpr:Expr = macro @:privateAccess {
|
||||
js.Syntax.code("await {0}", lime._internal.backend.html5.HTML5Thread.zeroDelay());
|
||||
lime._internal.backend.html5.HTML5Thread.__messages.pop();
|
||||
};
|
||||
var zeroDelayExpr:Expr = macro @:privateAccess
|
||||
$jsCode("await {0}", lime._internal.backend.html5.HTML5Thread.zeroDelay())
|
||||
.then(function(_) return lime._internal.backend.html5.HTML5Thread.__messages.pop());
|
||||
|
||||
switch (block.expr)
|
||||
{
|
||||
case EConst(CIdent("false")):
|
||||
@@ -134,7 +135,8 @@ class HTML5Thread {
|
||||
default:
|
||||
return macro if ($block && @:privateAccess lime._internal.backend.html5.HTML5Thread.__messages.isEmpty())
|
||||
{
|
||||
js.Syntax.code("await {0}", new js.lib.Promise(function(resolve, _):Void
|
||||
$jsCode("await {0}", new #if haxe4 js.lib.Promise #else js.Promise #end
|
||||
(function(resolve, _):Void
|
||||
{
|
||||
@:privateAccess lime._internal.backend.html5.HTML5Thread.__resolveMethods.add(resolve);
|
||||
}
|
||||
@@ -351,19 +353,18 @@ abstract WorkFunction<T:haxe.Constraints.Function>(WorkFunctionData<T>) from Wor
|
||||
**/
|
||||
public macro function dispatch(self:ExprOf<WorkFunction<Dynamic>>, args:Array<Expr>):Expr
|
||||
{
|
||||
var underlyingType:ComplexType;
|
||||
switch (self.typeof().follow().toComplexType())
|
||||
{
|
||||
case TPath({ sub: "WorkFunction", params: [TPType(t = TFunction(_, _))] }):
|
||||
underlyingType = t;
|
||||
default:
|
||||
throw "Underlying function type not found.";
|
||||
}
|
||||
|
||||
return macro ($self:$underlyingType)($a{args});
|
||||
return macro $self.toFunction()($a{args});
|
||||
}
|
||||
|
||||
@:to private function toFunction():T
|
||||
#if haxe4 @:to #end
|
||||
public inline function toMessage():Message
|
||||
{
|
||||
makePortable();
|
||||
return this;
|
||||
}
|
||||
|
||||
#if haxe4 @:to #end
|
||||
public function toFunction():T
|
||||
{
|
||||
if (this.func != null)
|
||||
{
|
||||
@@ -456,14 +457,15 @@ abstract Message(Dynamic) from Dynamic to Dynamic
|
||||
// Skip `null` for obvious reasons.
|
||||
return object == null
|
||||
// No need to preserve a primitive type.
|
||||
|| !Std.isOfType(object, Object)
|
||||
|| #if (haxe_ver >= 4.2) !Std.isOfType(object, Object) #else untyped __js__('typeof {0} != "object" && typeof {0} != "function"', object) #end
|
||||
// Objects with this field have been deliberately excluded.
|
||||
|| Reflect.field(object, SKIP_FIELD) == true
|
||||
// A `Uint8Array` (the type used by `haxe.io.Bytes`) can have
|
||||
// thousands or millions of fields, which can take entire seconds to
|
||||
// enumerate. This also applies to `Int8Array`, `Float64Array`, etc.
|
||||
|| object.byteLength != null && object.byteOffset != null
|
||||
&& object.buffer != null && Std.isOfType(object.buffer, #if haxe4 js.lib.ArrayBuffer #else js.html.ArrayBuffer #end);
|
||||
&& object.buffer != null
|
||||
&& #if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (object.buffer, #if haxe4 js.lib.ArrayBuffer #else js.html.ArrayBuffer #end);
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -499,11 +501,22 @@ abstract Message(Dynamic) from Dynamic to Dynamic
|
||||
}
|
||||
|
||||
// Preserve this object's class.
|
||||
if (!Std.isOfType(this, Array))
|
||||
if (!#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (this, Array))
|
||||
{
|
||||
try
|
||||
{
|
||||
Reflect.setField(this, PROTOTYPE_FIELD, this.__class__ != null ? this.__class__.__name__ : null);
|
||||
if (this.__class__ != null)
|
||||
{
|
||||
#if haxe4
|
||||
Reflect.setField(this, PROTOTYPE_FIELD, this.__class__.__name__);
|
||||
#else
|
||||
Reflect.setField(this, PROTOTYPE_FIELD, this.__class__.__name__.join("."));
|
||||
#end
|
||||
}
|
||||
else
|
||||
{
|
||||
Reflect.setField(this, PROTOTYPE_FIELD, null);
|
||||
}
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
@@ -533,14 +546,20 @@ abstract Message(Dynamic) from Dynamic to Dynamic
|
||||
|
||||
@param flag Leave this `null`.
|
||||
**/
|
||||
private function restoreClasses(flag:Int = null):Void
|
||||
private function restoreClasses(flag:Int = null, depth:Int = 0):Void
|
||||
{
|
||||
#if !macro
|
||||
// Attempt to choose a unique flag.
|
||||
if (flag == null)
|
||||
if (depth > 10)
|
||||
{
|
||||
// Stay well below 2^53.
|
||||
flag = Std.int(Math.random() * 0xFFFFFFFF);
|
||||
untyped __js__("console.log({0})", this);
|
||||
return;
|
||||
}
|
||||
|
||||
// Attempt to choose a unique flag.
|
||||
if (flag == null #if !haxe4 || flag == 0 #end)
|
||||
{
|
||||
// JavaScript's limit is 2^53; Haxe 3's limit is much lower.
|
||||
flag = Std.int(Math.random() * 0x7FFFFFFF);
|
||||
if (Reflect.field(this, RESTORE_FIELD) == flag)
|
||||
{
|
||||
flag++;
|
||||
@@ -552,19 +571,19 @@ abstract Message(Dynamic) from Dynamic to Dynamic
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Reflect.setField(this, RESTORE_FIELD, flag);
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
// Probably a frozen object; no need to continue.
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore this object's class.
|
||||
if (Reflect.field(this, PROTOTYPE_FIELD) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Reflect.setField(this, RESTORE_FIELD, flag);
|
||||
}
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
// Probably a frozen object; no need to continue.
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Object.setPrototypeOf(this,
|
||||
@@ -577,7 +596,7 @@ abstract Message(Dynamic) from Dynamic to Dynamic
|
||||
// Recurse.
|
||||
for (child in Object.values(this))
|
||||
{
|
||||
(child:Message).restoreClasses(flag);
|
||||
(child:Message).restoreClasses(flag, depth + 1);
|
||||
}
|
||||
#end
|
||||
}
|
||||
@@ -613,3 +632,12 @@ abstract Transferable(Dynamic) #if macro from Dynamic
|
||||
#else from lime.utils.ArrayBuffer from js.html.MessagePort from js.html.ImageBitmap #end
|
||||
{
|
||||
}
|
||||
|
||||
#if (!haxe4 && !macro)
|
||||
@:native("Object")
|
||||
extern class Object {
|
||||
static function setPrototypeOf<T:{}>(obj:T, prototype:Null<{}>):T;
|
||||
@:pure static function values(obj:{}):Array<Dynamic>;
|
||||
static var prototype(default, never):Dynamic;
|
||||
}
|
||||
#end
|
||||
|
||||
@@ -77,13 +77,6 @@ import lime.utils.Log;
|
||||
var promise = new Promise<T>();
|
||||
promise.future = this;
|
||||
|
||||
#if (lime_threads && html5)
|
||||
if (useThreads)
|
||||
{
|
||||
work.makePortable();
|
||||
}
|
||||
#end
|
||||
|
||||
FutureWork.run(dispatchWorkFunction, work, promise, useThreads ? MULTI_THREADED : SINGLE_THREADED, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +46,12 @@ import lime._internal.backend.html5.HTML5Thread as Thread;
|
||||
#end
|
||||
class ThreadPool extends WorkOutput
|
||||
{
|
||||
#if lime_threads
|
||||
#if (haxe4 && lime_threads)
|
||||
/**
|
||||
A thread or null value to be compared against `Thread.current()`. Don't
|
||||
do anything with this other than check for equality.
|
||||
|
||||
Unavailable in Haxe 3 as thread equality checking doesn't work there.
|
||||
**/
|
||||
private static var __mainThread:Thread =
|
||||
#if html5
|
||||
@@ -191,7 +193,7 @@ class ThreadPool extends WorkOutput
|
||||
**/
|
||||
public function cancel(error:Dynamic = null):Void
|
||||
{
|
||||
#if lime_threads
|
||||
#if (haxe4 && lime_threads)
|
||||
if (Thread.current() != __mainThread)
|
||||
{
|
||||
throw "Call cancel() only from the main thread.";
|
||||
@@ -300,7 +302,7 @@ class ThreadPool extends WorkOutput
|
||||
**/
|
||||
public function run(doWork:WorkFunction<State->WorkOutput->Void> = null, state:State = null):Int
|
||||
{
|
||||
#if lime_threads
|
||||
#if (haxe4 && lime_threads)
|
||||
if (Thread.current() != __mainThread)
|
||||
{
|
||||
throw "Call run() only from the main thread.";
|
||||
@@ -361,7 +363,7 @@ class ThreadPool extends WorkOutput
|
||||
{
|
||||
event = Thread.readMessage(true);
|
||||
}
|
||||
while (!Std.isOfType(event, ThreadEvent));
|
||||
while (!#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (event, ThreadEvent));
|
||||
|
||||
output.resetJobProgress();
|
||||
}
|
||||
@@ -394,7 +396,7 @@ class ThreadPool extends WorkOutput
|
||||
event.job.doWork.dispatch(event.job.state, output);
|
||||
}
|
||||
}
|
||||
catch (e)
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
output.sendError(e);
|
||||
}
|
||||
@@ -406,7 +408,7 @@ class ThreadPool extends WorkOutput
|
||||
// Work is done; wait for more.
|
||||
event = null;
|
||||
}
|
||||
else if(Std.isOfType(interruption, ThreadEvent))
|
||||
else if(#if (haxe_ver >= 4.2) Std.isOfType #else Std.is #end (interruption, ThreadEvent))
|
||||
{
|
||||
// Work on the new job.
|
||||
event = interruption;
|
||||
@@ -438,7 +440,7 @@ class ThreadPool extends WorkOutput
|
||||
**/
|
||||
private function __update(deltaTime:Int):Void
|
||||
{
|
||||
#if lime_threads
|
||||
#if (haxe4 && lime_threads)
|
||||
if (Thread.current() != __mainThread)
|
||||
{
|
||||
return;
|
||||
@@ -488,7 +490,7 @@ class ThreadPool extends WorkOutput
|
||||
}
|
||||
while (!__jobComplete.value && timeElapsed < __workPerFrame);
|
||||
}
|
||||
catch (e)
|
||||
catch (e:Dynamic)
|
||||
{
|
||||
sendError(e);
|
||||
}
|
||||
@@ -621,9 +623,14 @@ class ThreadPool extends WorkOutput
|
||||
}
|
||||
}
|
||||
|
||||
@:forward @:forward.new
|
||||
@:forward
|
||||
abstract JobList(List<JobData>)
|
||||
{
|
||||
public inline function new()
|
||||
{
|
||||
this = new List<JobData>();
|
||||
}
|
||||
|
||||
public inline function exists(job:JobData):Bool
|
||||
{
|
||||
return getByID(job.id) != null;
|
||||
|
||||
@@ -404,10 +404,17 @@ class JSAsync
|
||||
|
||||
// Define platform-specific types
|
||||
|
||||
#if !(target.threaded || cpp || neko)
|
||||
@:forward(push, add) @:forward.new
|
||||
#if target.threaded
|
||||
// Haxe 3 compatibility: "target.threaded" can't go in parentheses.
|
||||
#elseif !(cpp || neko)
|
||||
@:forward(push, add)
|
||||
abstract Deque<T>(List<T>) from List<T> to List<T>
|
||||
{
|
||||
public inline function new()
|
||||
{
|
||||
this = new List<T>();
|
||||
}
|
||||
|
||||
public inline function pop(block:Bool):Null<T>
|
||||
{
|
||||
return this.pop();
|
||||
|
||||
Reference in New Issue
Block a user