The Nemo engine can be started from within Rust. To do this, we must initialize the reasoner and then await its output, meaning we are forced to use asynchronous programming:
// main function is async
let nemo_engine_input = nemo::api::Engine::initialize(
input_program,
ImportManager::new(ResourceProviders::default()),
);
let output = nemo_engine_input.await
As such, one could expect nemo to support async programming utilities, such as a timeout https://docs.rs/tokio/latest/tokio/time/fn.timeout.html:
// main function is async
let nemo_engine_input = nemo::api::Engine::initialize(
input_program,
ImportManager::new(ResourceProviders::default()),
);
let output = match timeout(Duration::from_mins(timeout_after_mins), nemo_engine_input).await {
Err(time) => {
error!("Timeout after {time}!");
// Store statistics and that I timed out
exit(1);
}
Ok(res) => match res {
Err(r) => {
error!("Failed nemo calc.");
error!("{r}");
exit(1);
}
Ok(v) => v,
},
};
In practice, this is not the case, because Nemo blocks the thread. This is not necessarily an issue, but perhaps non-idiomatic to asynchronous programming:
https://rust-lang.github.io/async-book/part-guide/more-async-await.html#blocking-computation
You can also block the thread by doing computation (this is not quite the same as blocking I/O, since the OS is not involved, but the effect is similar). If you have a long-running computation (with or without blocking I/O) without yielding control to the runtime, then that task will never give the runtime's scheduler a chance to schedule other tasks. Remember that async programming uses cooperative multitasking. Here a task is not cooperating, so other tasks won't get a chance to get work done. We'll discuss ways to mitigate this later.
https://rust-lang.github.io/async-book/part-guide/io.html#other-blocking-operations
Doing long-running (i.e., cpu-intensive or cpu-bound) work will prevent the scheduler from running other tasks. This is a kind of blocking, but it is not as bad as blocking on IO or waiting because at least your program is making some progress. However (without care and consideration), it is likely to be sub-optimal for performance by some measure (e.g., tail latency) and perhaps a correctness issue if the tasks that can't run needed to be run at a particular time. There is a meme that you should simply not use async Rust (or general purpose async runtimes like Tokio) for CPU-intensive work, but that is an over-simplification. What is correct is that you cannot mix IO- and CPU-bound (or more precisely, long-running and latency-sensitive) tasks without some special handling and expect to have a good time.
I believe this could be fixed by having Nemo engine spawn its own threads and await them, meaning the main Nemo thread waits occasionally, allowing the scheduler to allow the thread that spawned the Nemo engine to check whether the timeout has been reached, but I am new to asynchronous Rust programming, so I am not sure.
The Nemo engine can be started from within Rust. To do this, we must initialize the reasoner and then
awaitits output, meaning we are forced to use asynchronous programming:As such, one could expect nemo to support async programming utilities, such as a timeout https://docs.rs/tokio/latest/tokio/time/fn.timeout.html:
In practice, this is not the case, because Nemo blocks the thread. This is not necessarily an issue, but perhaps non-idiomatic to asynchronous programming:
https://rust-lang.github.io/async-book/part-guide/more-async-await.html#blocking-computation
https://rust-lang.github.io/async-book/part-guide/io.html#other-blocking-operations
I believe this could be fixed by having Nemo engine spawn its own threads and
awaitthem, meaning the main Nemo thread waits occasionally, allowing the scheduler to allow the thread that spawned the Nemo engine to check whether the timeout has been reached, but I am new to asynchronous Rust programming, so I am not sure.