Asynchronicity and Threading
By default Trial will run two threads: the main thread, which is initiated via launch-with-context
and controlled by the context, and the separate render-thread, initiated by the render-loop
. These threads are kept separate so that the main thread can always respond to window events and inputs and schedule them up as fast as possible, preventing the application from appearing as locked up even if the render thread might be busy for a spell, such as with a loading operation.
However, this still means that by default, for all intents and purposes, your game will run single-threaded, with all steps happening on the render thread. This is fine for most things, but especially long-running operations like network requests or long saves and loads could cause stutters or hangups in the UI. In this case you may want to keep a separate back thread to perform such long-running operations.
Trial offers a very simple system based on the simple-tasks and promise libraries. Simply have your main
class inherit from task-runner-main
, and then schedule tasks on the back thread via with-eval-in-task-thread
. You can also use simple-tasks:schedule-task
with a function object and the main
instance.
Both will return a promise
object so that you can chain the operation and keep track of the result value or failure state. See the promise library above for more information on how to use the promises.
Some operations will also require the reverse: running stuff in the render loop. You can do this with with-eval-in-render-loop
. This macro can also be very useful for REPL interaction while the game is running, as then you can simple compile a form like that in, and have it run synchronously on the render thread, avoiding issues with asynchronous resource interaction.