Use of an asynchronous runtime for the main application
Overview
The Aquarium Control application uses a hybrid execution model to balance responsiveness, low latency, and hardware constraints. It uses the **Tokio asynchronous runtime** for coordinating sensor logging, network communication, safety interlocks, and scheduling. Concurrently, it spawns dedicated **native operating system threads** for operations requiring strict timing or blocking I/O (such as direct GPIO sensor reading).
---
Task and Thread Inventory
The table below lists all tasks and threads running in the application:
| Name | Description | Type (OS Thread / Async Task) | Usage of `block_on` | |---|---|---|---| | **Main Thread** | Bootstraps the application, reads configuration, establishes database pools, builds the Tokio runtime, and blocks on the execution of all tasks. | OS Thread | Yes (`rt.block_on`) | | **`TcpCommunication`** | Mock simulator thread that handles TCP connections and commands from the GUI simulator. Runs only when `use_simulator` is active. | OS Thread | No | | **`Dht`** | Direct driver reader thread for the DHT22 ambient temperature and humidity sensor. Handled as an OS thread to prevent blocking the async runtime. | OS Thread | No | | **`i2c_interface`** | Manages serial I2C bus communications for sensor expansion units. | Async Task | No | | **`signal_handler`** | Captures shutdown signals (SIGINT, SIGTERM) and coordinates the graceful, phase-based shutdown of all sub-components. | Async Task | No | | **`schedule_check`** | Performs regular database checks against the active rules database to ensure operations are authorized based on the current time. | Async Task | No | | **`sensor_manager`** | Coordinates, aggregates, and caches values read from individual sensor tasks (DS18B20, DHT22, Atlas Scientific). | Async Task | No | | **`atlas_scientific`** | Manages data acquisition and calibration for the Atlas Scientific EZO pH and conductivity probes. | Async Task | No | | **`relay_manager`** | Acts as the central actuator gateway, processing on/off/pulse commands and sending them directly to the hardware relays. | Async Task | No | | **`data_logger`** | Periodically logs sensor values and thermal controller statuses into the MySQL database. | Async Task | No | | **`temperature_gradient`** | Monitors the rate of temperature changes to detect heater or ventilation inefficiencies/failures. | Async Task | No | | **`heating`** | Orchestrates the heating loop, using hysteresis or safety controls to actuate the aquarium heater. | Async Task | No | | **`ventilation`** | Controls the surface cooling fan loop, maintaining temperature thresholds. | Async Task | No | | **`monitors`** | Runs regular diagnostic checks and outputs warning/critical logs when states drift outside target thresholds. | Async Task | No | | **`refill`** | Drives the fresh water top-off system, managing level switch states and pump runs with safety timers. | Async Task | No | | **`feed`** | Monitors the feeding schedule and actuates the automatic feeder. | Async Task | No | | **`balling`** | Oversees peristaltic mineral pumps to dose minerals into the aquarium according to schedule. | Async Task | No | | **`tank_level_switch`** | Periodically reads the state of physical water level floats or simulator ports. | Async Task | No | | **`watchdog`** | Performs periodic heartbeats to ensure system safety loops are executing. | Async Task | No | | **`ds18b20`** | Periodically queries 1-Wire DS18B20 digital thermometers for water temperature. | Async Task | No | | **`messaging`** | Stub mock receiver that routes command queues when running on non-Linux platforms (like macOS). | Async Task | No |
---
Tokio Runtime Configuration
The Tokio runtime is explicitly built at startup with a constrained configuration:
```rust let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(1) .max_blocking_threads(4) .enable_all() .build()
```
- Why this configuration is chosen:
1. **`worker_threads(1)`**: The application runs on low-power embedded single-board computers (like the Raspberry Pi). Limiting the event loop scheduler to one worker thread minimizes CPU context-switching overhead and prevents resource exhaustion. 2. **`max_blocking_threads(4)`**: Restricting the blocking thread pool prevents the runtime from spawning too many concurrent threads when database pings, file operations, or synchronous calculations are invoked (such as via `tokio::task::spawn_blocking` or `block_in_place`).
---
- Developer Diagnostics with Tokio Console
For runtime inspection, task profiling, and detecting deadlocks or starvation, you can run the application with **Tokio Console** integration.
- Prerequisites & Compilation
Tokio Console requires unstable instrumentation. 1. Add the `debug_tokio` feature flag, which initializes the `console-subscriber` in `run.rs`:
```bash RUSTFLAGS="--cfg tokio_unstable" cargo build --features "debug_tokio" ```
2. The `tokio_unstable` compiler flag ensures that Tokio exposes internal tracing endpoints (like the task builder names).
- Installing the Terminal Console
Install the client CLI on your development machine (macOS/Linux) via Cargo: ```bash cargo install --locked tokio-console ```
- Running and Reading
1. Launch the compiled application:
```bash RUSTFLAGS="--cfg tokio_unstable" cargo run --features "debug_tokio" ```
2. Open a separate terminal window and run:
```bash tokio-console ```
This presents a live dashboard of running tasks, showing busy poll times, total lifetime, and idle periods.
- Interpreting Task Size Warnings
When checking with `tokio-console`, you may see warnings that tasks like `signal_handler`, `relay_manager`, `data_logger`, or `heating` are **1024 bytes or larger**.
- **These warnings can be safely ignored.**
- The size refers to the stack footprint of the compiler-generated `Future` state machine. In debug builds, these are larger due to lack of optimization.
- Because these tasks are long-lived workers spawned once at startup and run until shutdown, they are allocated on the heap only once. A 1–2 KiB heap footprint has zero impact on application performance, fragmentation, or memory leaks.