Runtime
The library runs on the Tokio asynchronous runtime: a pool of worker threads that runs every client
channel and server and calls your callbacks. You create one Runtime and share it across them.
Lifetime
Create a Runtime before any communication can take place. It's a shared resource, typically created just after initializing logging and shut down last when exiting.
Rust users can share the runtime with other libraries that also use Tokio. The bindings don't support sharing a runtime.
Examples
- Rust
- C
- C++
- Java
- C#
#[tokio::main(flavor = "multi_thread")]
async fn main() -> Result<(), Box<dyn Error>> {
// do things within the context of the runtime
// it is automatically shut down when this async fn returns
Ok(())
}
rodbus_runtime_t *runtime = NULL;
rodbus_runtime_config_t runtime_config = rodbus_runtime_config_init();
runtime_config.num_core_threads = 4;
rodbus_param_error_t err = rodbus_runtime_create(runtime_config, &runtime);
if (err) {
printf("unable to create runtime: %s\n", rodbus_param_error_to_string(err));
return EXIT_FAILURE;
}
auto runtime_config = rodbus::RuntimeConfig();
runtime_config.num_core_threads = 4;
auto runtime = rodbus::Runtime(runtime_config);
RuntimeConfig runtimeConfig = new RuntimeConfig().withNumCoreThreads(ushort(4));
Runtime runtime = new Runtime(runtimeConfig);
var runtime = new Runtime(new RuntimeConfig { NumCoreThreads = 4 });
Set the number of runtime threads to 0 to default to the number of system cores. This provides a safe default that will lead to good multi-core utilization.
Callbacks
The runtime's thread pool invokes callbacks from the library to user code. Blocking during a callback removes an entire thread from task execution. If all threads in a thread pool are blocked, no communication sessions execute until a thread unblocks.
For example, when you receive a log message via a callback, a synchronous call to write the message to a file will block a thread. If this frequently occurs on all your pool threads, it can cause poor throughput or even task starvation.
Avoid blocking in callbacks. Defer blocking calls to dedicated worker threads, such as a user-managed thread that writes log messages to file.
If you have a case where some blocking is unavoidable, set the number of worker threads to a multiple of the number of system cores, such as 2x or 3x.
Binding methods that wait for the runtime to complete an operation, such as creating a TCP or TLS server or changing a server's decode
level, can't be called from within a callback. They fail with the RUNTIME_CANNOT_BLOCK_WITHIN_ASYNC parameter error.
Shutdown
Shutting down the runtime is typically one of the last operations your program should perform before exiting. The call to shutdown is synchronous. When it completes you are assured there are no longer any tasks running and all of the Tokio worker threads have been joined.
A blocked worker thread can cause shutdown to deadlock. For example, if a communication channel makes a callback to user code
that permanently blocks, shutdown will cause a deadlock.
If you cannot ensure a clean shutdown, use Runtime.set_shutdown_timeout(..) to set an upper
time limit on shutdown. Call this method immediately after creating the Runtime.
Shutting down with a timeout can leak memory, because worker threads that haven't stopped when the timeout occurs are left running. Only use this method if you are exiting the process anyway.
Shutdown examples
- Rust
- C
- C++
- Java
- C#
Runtime shutdown is implicit in Rust when tokio::main returns.
rodbus_runtime_destroy(runtime);
The runtime shuts down when the Runtime object is destroyed, for example when it goes out of scope.
runtime.shutdown();
If you don't call shutdown() before exiting, the JVM will shutdown the runtime in its finalize method. However, garbage collection is not deterministic, so your program may never properly exit. Make sure you explicitly shut down the runtime before exiting.
runtime.Shutdown();
If you don't call Shutdown before exiting, the .NET runtime will shut down the runtime in its finalizer (also called the destructor). Again, you should explicitly shut down the runtime before exiting instead of relying on this behavior.