From f31adb845f980b01562d234efb375cfe9b47f412 Mon Sep 17 00:00:00 2001 From: Joseph Cloutier Date: Sun, 27 Mar 2022 13:40:08 -0400 Subject: [PATCH] Partially fix `HTML5Thread.readMessage(false)`. It now works correctly, but it's much slower. --- .../_internal/backend/html5/HTML5Thread.hx | 39 ++++++++++++------- src/lime/system/WorkOutput.hx | 9 +++-- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/src/lime/_internal/backend/html5/HTML5Thread.hx b/src/lime/_internal/backend/html5/HTML5Thread.hx index 1a0e028e1..c925819c6 100644 --- a/src/lime/_internal/backend/html5/HTML5Thread.hx +++ b/src/lime/_internal/backend/html5/HTML5Thread.hx @@ -19,6 +19,7 @@ import js.html.Worker; import js.Lib; import js.lib.Function; import js.lib.Object; +import js.lib.Promise; import js.Syntax; // Same with classes that import lots of other things. import lime.app.Application; @@ -101,34 +102,46 @@ class HTML5Thread { #end } - /** - Reads a message from the thread queue. Returns `null` if no argument is - available. + #if !macro + private static inline function zeroDelay():Promise + { + return new Promise(function(resolve, _):Void + { + js.Lib.global.setTimeout(resolve); + }); + } + #end - @param block If true, uses the `await` keyword to wait for the next - message. Requires the calling function to be `async`. + /** + Reads a message from the thread queue. Returns `null` if no message is + available. This may only be called inside an `async` function. + @param block If true, waits for the next message before returning. @see `lime.system.WorkOutput.JSAsync.async()` **/ public static macro function readMessage(block:ExprOf):Dynamic { - var blockFalseExpr:Expr = macro @:privateAccess lime._internal.backend.html5.HTML5Thread.__messages.pop(); + // `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(); + }; switch (block.expr) { case EConst(CIdent("false")): - return blockFalseExpr; + return zeroDelayExpr; default: return macro if ($block && @:privateAccess lime._internal.backend.html5.HTML5Thread.__messages.isEmpty()) { - js.Syntax.code("await {0}", new js.lib.Promise(function(resolve, _) + js.Syntax.code("await {0}", new js.lib.Promise(function(resolve, _):Void { @:privateAccess lime._internal.backend.html5.HTML5Thread.__resolveMethods.add(resolve); } )); } else - { - $blockFalseExpr; - }; + $zeroDelayExpr; } } @@ -186,8 +199,8 @@ class HTML5Thread { } /** - Send a message to the thread queue. This message can be read by - listening for the `onMessage` event. + Send a message to the thread queue. This message can be read using + `readMessage()` or by listening for the `onMessage` event. @param preserveClasses Whether to call `preserveClasses()` first. **/ diff --git a/src/lime/system/WorkOutput.hx b/src/lime/system/WorkOutput.hx index 19ab7c86c..f6e9100e3 100644 --- a/src/lime/system/WorkOutput.hx +++ b/src/lime/system/WorkOutput.hx @@ -225,12 +225,13 @@ class WorkOutput All work will be done on a background thread. Unlike single-threaded mode, there is no risk of causing lag spikes. - Even so, `doWork` should return at regular intervals, storing its - progress in `state`. This will make it easier to cancel the thread. If - not canceled, `doWork` will be called again immediately. + Even so, `doWork` should return periodically, to allow canceling the + thread. If not canceled, `doWork` will be called again immediately. In HTML5, web workers will be used to achieve this. This means `doWork` - must be a static function, and you can't use `bind()`. + must be a static function, and you can't use `bind()`. Web workers also + impose a longer delay each time `doWork` returns, so it shouldn't return + as often in multi-threaded mode as in single-threaded mode. **/ var MULTI_THREADED = true; }