Javascript is single-threaded,When calling `Engine.RunGuiPage` a promise is returned. The promise is fulfilled when the "child" page is closed. but can support cooperative multithreading using That allows to `await` it inside `async/await and promises` functions.
Spidermonkey exposes bindings to allow running promises (code that currently tries to use Promises will crash)Previously the callback is run right inside the call to `Engine.PopGuiPage`. Now the continuation of the promise is called at the end of the "tick".
This diff implements the necessary logic to support that,won't help performance. which is mostly two things:
- A JSContext* hook to dispatch promise queuing.
- Logic to run the dispatched jobs (aka the 'event loop')It will morelikely make things worse. Since gui pages aren't opened or closed that frequently, it doesn't matter that much.
However, doing that only allows asynchronous JS calls, which is not terribly useful by itself because everything remains single-threaded.For the engine side:
This diff also implements a simple interface to let JS wait on C++ threaded tasks, using the thread pool manager.
The logic is barebones and should perhaps be expended, but workse promise is stored in the `CGUIManager::SGUIPage` (like previously the callback). I've added fairly simple tests covering the above.
One particular inefficiency is that we iterate futures when calling RunJob,When the promise is fulfilled it enqueues a callback in the `JobQueue` of the `JSContext`. which could be inefficient if there are lots of pending futures.
Some notes:Our `JobQueue` only forwards the callback to a job-queue owned by the `ScriptInterface` so that we get controll when which `ScriptInterface` calls it's jobs.
- RunJobs() should be run manually in any context that supports Promises - It's probably a good idea to fail queuing jobs if the scriptinterface isn't supposed to run jobsThe jobs of the topmost gui page are called at the end of every tick.
- The futures are passed to the script interface so that it knows to wait/cancel them when being destroyed. This looks a lot like Spidermonkey's `OffThreadPromiseTask` but they kind of assume JSContext lifetime. I think doing it manually is better.
WIP:
- I think the code should be moved from ScriptInterface to a 'ScriptTasks' class somewhere,The jobs of the other `ScriptInterfaces` aren't called. probably still owned by the s(TODO: fail when trying to use promises in such `ScriptInterface.s`)
Future work:
- Allow the child-page to return a promise instead of calling `Engine.PopGugPage`.
- It would be nice to be able to run JS work in a thread manner, but that's more work.
This demonstrates a very simple one that calls the resolve job instantly when the asynchronous task ends.
Design questions:
- Is it a good idea to support asynchronous calls from Javascript? It might well lead to unpredictable lag.
- If so, when and where?