@@ -70,19 +70,25 @@ pub struct SystemInfo {
70
70
feature = "std" ,
71
71
) ) ]
72
72
pub mod internal {
73
+ use core:: {
74
+ pin:: Pin ,
75
+ task:: { Context , Poll , Waker } ,
76
+ } ;
77
+ use std:: sync:: {
78
+ mpsc:: { self , Receiver , Sender } ,
79
+ Arc , Mutex ,
80
+ } ;
81
+
73
82
use alloc:: {
74
83
format,
75
84
string:: { String , ToString } ,
76
- sync:: Arc ,
77
- vec:: Vec ,
78
85
} ;
79
86
use bevy_app:: { App , First , Startup , Update } ;
80
87
use bevy_ecs:: resource:: Resource ;
81
- use bevy_ecs:: { prelude:: ResMut , system:: Local } ;
88
+ use bevy_ecs:: { prelude:: ResMut , system:: Commands } ;
82
89
use bevy_platform:: time:: Instant ;
83
- use bevy_tasks:: { available_parallelism , block_on , poll_once , AsyncComputeTaskPool , Task } ;
90
+ use bevy_tasks:: AsyncComputeTaskPool ;
84
91
use log:: info;
85
- use std:: sync:: Mutex ;
86
92
use sysinfo:: { CpuRefreshKind , MemoryRefreshKind , RefreshKind , System } ;
87
93
88
94
use crate :: { Diagnostic , Diagnostics , DiagnosticsStore } ;
@@ -93,12 +99,20 @@ pub mod internal {
93
99
94
100
pub ( super ) fn setup_plugin ( app : & mut App ) {
95
101
app. add_systems ( Startup , setup_system)
96
- . add_systems ( First , launch_diagnostic_tasks)
97
- . add_systems ( Update , read_diagnostic_tasks)
98
- . init_resource :: < SysinfoTasks > ( ) ;
102
+ . add_systems ( First , wake_diagnostic_task)
103
+ . add_systems ( Update , read_diagnostic_task) ;
99
104
}
100
105
101
- fn setup_system ( mut diagnostics : ResMut < DiagnosticsStore > ) {
106
+ fn setup_system ( mut diagnostics : ResMut < DiagnosticsStore > , mut commands : Commands ) {
107
+ let ( tx, rx) = mpsc:: channel ( ) ;
108
+ let diagnostic_task = DiagnosticTask :: new ( tx) ;
109
+ let waker = Arc :: clone ( & diagnostic_task. waker ) ;
110
+ AsyncComputeTaskPool :: get ( ) . spawn ( diagnostic_task) . detach ( ) ;
111
+ commands. insert_resource ( SysinfoTask {
112
+ receiver : Mutex :: new ( rx) ,
113
+ waker,
114
+ } ) ;
115
+
102
116
diagnostics. add (
103
117
Diagnostic :: new ( SystemInformationDiagnosticsPlugin :: SYSTEM_CPU_USAGE ) . with_suffix ( "%" ) ,
104
118
) ;
@@ -121,78 +135,100 @@ pub mod internal {
121
135
process_mem_usage : f64 ,
122
136
}
123
137
124
- #[ derive( Resource , Default ) ]
125
- struct SysinfoTasks {
126
- tasks : Vec < Task < SysinfoRefreshData > > ,
138
+ impl SysinfoRefreshData {
139
+ fn new ( system : & mut System ) -> Self {
140
+ let pid = sysinfo:: get_current_pid ( ) . expect ( "Failed to get current process ID" ) ;
141
+ system. refresh_processes ( sysinfo:: ProcessesToUpdate :: Some ( & [ pid] ) , true ) ;
142
+
143
+ system. refresh_cpu_specifics ( CpuRefreshKind :: nothing ( ) . with_cpu_usage ( ) ) ;
144
+ system. refresh_memory ( ) ;
145
+
146
+ let system_cpu_usage = system. global_cpu_usage ( ) . into ( ) ;
147
+ let total_mem = system. total_memory ( ) as f64 ;
148
+ let used_mem = system. used_memory ( ) as f64 ;
149
+ let system_mem_usage = used_mem / total_mem * 100.0 ;
150
+
151
+ let process_mem_usage = system
152
+ . process ( pid)
153
+ . map ( |p| p. memory ( ) as f64 * BYTES_TO_GIB )
154
+ . unwrap_or ( 0.0 ) ;
155
+
156
+ let process_cpu_usage = system
157
+ . process ( pid)
158
+ . map ( |p| p. cpu_usage ( ) as f64 / system. cpus ( ) . len ( ) as f64 )
159
+ . unwrap_or ( 0.0 ) ;
160
+
161
+ Self {
162
+ system_cpu_usage,
163
+ system_mem_usage,
164
+ process_cpu_usage,
165
+ process_mem_usage,
166
+ }
167
+ }
168
+ }
169
+
170
+ #[ derive( Resource ) ]
171
+ struct SysinfoTask {
172
+ receiver : Mutex < Receiver < SysinfoRefreshData > > ,
173
+ waker : Arc < Mutex < Option < Waker > > > ,
127
174
}
128
175
129
- fn launch_diagnostic_tasks (
130
- mut tasks : ResMut < SysinfoTasks > ,
131
- // TODO: Consider a fair mutex
132
- mut sysinfo : Local < Option < Arc < Mutex < System > > > > ,
133
- // TODO: FromWorld for Instant?
134
- mut last_refresh : Local < Option < Instant > > ,
135
- ) {
136
- let sysinfo = sysinfo. get_or_insert_with ( || {
137
- Arc :: new ( Mutex :: new ( System :: new_with_specifics (
138
- RefreshKind :: nothing ( )
139
- . with_cpu ( CpuRefreshKind :: nothing ( ) . with_cpu_usage ( ) )
140
- . with_memory ( MemoryRefreshKind :: everything ( ) ) ,
141
- ) ) )
142
- } ) ;
176
+ struct DiagnosticTask {
177
+ system : System ,
178
+ last_refresh : Instant ,
179
+ sender : Sender < SysinfoRefreshData > ,
180
+ waker : Arc < Mutex < Option < Waker > > > ,
181
+ }
143
182
144
- let last_refresh = last_refresh. get_or_insert_with ( Instant :: now) ;
145
-
146
- let thread_pool = AsyncComputeTaskPool :: get ( ) ;
147
-
148
- // Only queue a new system refresh task when necessary
149
- // Queuing earlier than that will not give new data
150
- if last_refresh. elapsed ( ) > sysinfo:: MINIMUM_CPU_UPDATE_INTERVAL
151
- // These tasks don't yield and will take up all of the task pool's
152
- // threads if we don't limit their amount.
153
- && tasks. tasks . len ( ) * 2 < available_parallelism ( )
154
- {
155
- let sys = Arc :: clone ( sysinfo) ;
156
- let task = thread_pool. spawn ( async move {
157
- let mut sys = sys. lock ( ) . unwrap ( ) ;
158
- let pid = sysinfo:: get_current_pid ( ) . expect ( "Failed to get current process ID" ) ;
159
- sys. refresh_processes ( sysinfo:: ProcessesToUpdate :: Some ( & [ pid] ) , true ) ;
160
-
161
- sys. refresh_cpu_specifics ( CpuRefreshKind :: nothing ( ) . with_cpu_usage ( ) ) ;
162
- sys. refresh_memory ( ) ;
163
- let system_cpu_usage = sys. global_cpu_usage ( ) . into ( ) ;
164
- let total_mem = sys. total_memory ( ) as f64 ;
165
- let used_mem = sys. used_memory ( ) as f64 ;
166
- let system_mem_usage = used_mem / total_mem * 100.0 ;
167
-
168
- let process_mem_usage = sys
169
- . process ( pid)
170
- . map ( |p| p. memory ( ) as f64 * BYTES_TO_GIB )
171
- . unwrap_or ( 0.0 ) ;
172
-
173
- let process_cpu_usage = sys
174
- . process ( pid)
175
- . map ( |p| p. cpu_usage ( ) as f64 / sys. cpus ( ) . len ( ) as f64 )
176
- . unwrap_or ( 0.0 ) ;
177
-
178
- SysinfoRefreshData {
179
- system_cpu_usage,
180
- system_mem_usage,
181
- process_cpu_usage,
182
- process_mem_usage,
183
- }
184
- } ) ;
185
- tasks. tasks . push ( task) ;
186
- * last_refresh = Instant :: now ( ) ;
183
+ impl DiagnosticTask {
184
+ fn new ( sender : Sender < SysinfoRefreshData > ) -> Self {
185
+ Self {
186
+ system : System :: new_with_specifics (
187
+ RefreshKind :: nothing ( )
188
+ . with_cpu ( CpuRefreshKind :: nothing ( ) . with_cpu_usage ( ) )
189
+ . with_memory ( MemoryRefreshKind :: everything ( ) ) ,
190
+ ) ,
191
+ last_refresh : Instant :: now ( ) ,
192
+ sender,
193
+ waker : Arc :: default ( ) ,
194
+ }
187
195
}
188
196
}
189
197
190
- fn read_diagnostic_tasks ( mut diagnostics : Diagnostics , mut tasks : ResMut < SysinfoTasks > ) {
191
- tasks. tasks . retain_mut ( |task| {
192
- let Some ( data) = block_on ( poll_once ( task) ) else {
193
- return true ;
194
- } ;
198
+ impl DiagnosticTask {
199
+ fn update_waker ( & mut self , cx : & mut Context < ' _ > ) {
200
+ let mut waker = self . waker . lock ( ) . unwrap ( ) ;
201
+ * waker = Some ( cx. waker ( ) . clone ( ) ) ;
202
+ }
203
+ }
204
+
205
+ impl Future for DiagnosticTask {
206
+ type Output = ( ) ;
207
+
208
+ fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
209
+ self . update_waker ( cx) ;
210
+
211
+ if self . last_refresh . elapsed ( ) > sysinfo:: MINIMUM_CPU_UPDATE_INTERVAL {
212
+ self . last_refresh = Instant :: now ( ) ;
213
+
214
+ let sysinfo_refresh_data = SysinfoRefreshData :: new ( & mut self . system ) ;
215
+ self . sender . send ( sysinfo_refresh_data) . unwrap ( ) ;
216
+ }
195
217
218
+ Poll :: Pending
219
+ }
220
+ }
221
+
222
+ fn wake_diagnostic_task ( task : ResMut < SysinfoTask > ) {
223
+ let mut waker = task. waker . lock ( ) . unwrap ( ) ;
224
+ if let Some ( waker) = waker. take ( ) {
225
+ waker. wake_by_ref ( ) ;
226
+ }
227
+ }
228
+
229
+ fn read_diagnostic_task ( mut diagnostics : Diagnostics , task : ResMut < SysinfoTask > ) {
230
+ let receiver = task. receiver . lock ( ) . unwrap ( ) ;
231
+ while let Ok ( data) = receiver. try_recv ( ) {
196
232
diagnostics. add_measurement (
197
233
& SystemInformationDiagnosticsPlugin :: SYSTEM_CPU_USAGE ,
198
234
|| data. system_cpu_usage ,
@@ -209,8 +245,7 @@ pub mod internal {
209
245
& SystemInformationDiagnosticsPlugin :: PROCESS_MEM_USAGE ,
210
246
|| data. process_mem_usage ,
211
247
) ;
212
- false
213
- } ) ;
248
+ }
214
249
}
215
250
216
251
impl Default for SystemInfo {
0 commit comments