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