Repository: bevyengine/bevy
File: crates/bevy_diagnostic/src/system_information_diagnostics_plugin.rs (Rust)

Code before:
    use atomic_waker::AtomicWaker;
    use bevy_app::{App, First, Startup, Update};
    use bevy_ecs::resource::Resource;
    use bevy_ecs::{prelude::ResMut, system::Commands};
    use bevy_platform::{cell::SyncCell, time::Instant};
    use bevy_tasks::AsyncComputeTaskPool;
    use log::info;
    use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System};

    use crate::{Diagnostic, Diagnostics, DiagnosticsStore};

    use super::{SystemInfo, SystemInformationDiagnosticsPlugin};

    const BYTES_TO_GIB: f64 = 1.0 / 1024.0 / 1024.0 / 1024.0;

    pub(super) fn setup_plugin(app: &mut App) {
        app.add_systems(Startup, setup_system)
            .add_systems(First, wake_diagnostic_task)
            .add_systems(Update, read_diagnostic_task);
    }

    fn setup_system(mut diagnostics: ResMut<DiagnosticsStore>, mut commands: Commands) {
        let (tx, rx) = mpsc::channel();
        let diagnostic_task = DiagnosticTask::new(tx);
        let waker = Arc::clone(&diagnostic_task.waker);
        AsyncComputeTaskPool::get().spawn(diagnostic_task).detach();
        commands.insert_resource(SysinfoTask {
            receiver: SyncCell::new(rx),
            waker,
        });

        diagnostics.add(
            Diagnostic::new(SystemInformationDiagnosticsPlugin::SYSTEM_CPU_USAGE).with_suffix("%"),
        );
        diagnostics.add(
            Diagnostic::new(SystemInformationDiagnosticsPlugin::SYSTEM_MEM_USAGE).with_suffix("%"),
        );
        diagnostics.add(
            Diagnostic::new(SystemInformationDiagnosticsPlugin::PROCESS_CPU_USAGE).with_suffix("%"),
        );
        diagnostics.add(
            Diagnostic::new(SystemInformationDiagnosticsPlugin::PROCESS_MEM_USAGE)
                .with_suffix("GiB"),
        );
    }

    struct SysinfoRefreshData {
        system_cpu_usage: f64,
        system_mem_usage: f64,
        process_cpu_usage: f64,
        process_mem_usage: f64,

Review comment (suggestion):
It may be better to just embed the `Task<()>` handle into `SysInfoTask`, just to ensure that the task is cancelled if/when the resource itself is dropped for whatever reason.

Code after:
    use atomic_waker::AtomicWaker;
    use bevy_app::{App, First, Startup, Update};
    use bevy_ecs::resource::Resource;
    use bevy_ecs::{prelude::ResMut, system::Commands};
    use bevy_platform::{cell::SyncCell, time::Instant};
    use bevy_tasks::{AsyncComputeTaskPool, Task};
    use log::info;
    use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System};

    use crate::{Diagnostic, Diagnostics, DiagnosticsStore};

    use super::{SystemInfo, SystemInformationDiagnosticsPlugin};

    const BYTES_TO_GIB: f64 = 1.0 / 1024.0 / 1024.0 / 1024.0;

    /// Sets up the system information diagnostics plugin.
    ///
    /// The plugin spawns a single background task in the async task pool that always reschedules.
    /// The [`wake_diagnostic_task`] system wakes this task once per frame during the [`First`]
    /// schedule. If enough time has passed since the last refresh, it sends [`SysinfoRefreshData`]
    /// through a channel. The [`read_diagnostic_task`] system receives this data during the
    /// [`Update`] schedule and adds it as diagnostic measurements.
    pub(super) fn setup_plugin(app: &mut App) {
        app.add_systems(Startup, setup_system)
            .add_systems(First, wake_diagnostic_task)
            .add_systems(Update, read_diagnostic_task);
    }

    fn setup_system(mut diagnostics: ResMut<DiagnosticsStore>, mut commands: Commands) {
        let (tx, rx) = mpsc::channel();
        let diagnostic_task = DiagnosticTask::new(tx);
        let waker = Arc::clone(&diagnostic_task.waker);
        let task = AsyncComputeTaskPool::get().spawn(diagnostic_task);
        commands.insert_resource(SysinfoTask {
            _task: task,
            receiver: SyncCell::new(rx),
            waker,
        });

        diagnostics.add(
            Diagnostic::new(SystemInformationDiagnosticsPlugin::SYSTEM_CPU_USAGE).with_suffix("%"),
        );
        diagnostics.add(
            Diagnostic::new(SystemInformationDiagnosticsPlugin::SYSTEM_MEM_USAGE).with_suffix("%"),
        );
        diagnostics.add(
            Diagnostic::new(SystemInformationDiagnosticsPlugin::PROCESS_CPU_USAGE).with_suffix("%"),
        );
        diagnostics.add(
            Diagnostic::new(SystemInformationDiagnosticsPlugin::PROCESS_MEM_USAGE)
                .with_suffix("GiB"),