@@ -187,6 +187,10 @@ enum Request {
187
187
CreationTimes {
188
188
reply_tx : oneshot:: Sender < BTreeMap < KstatPath , DateTime < Utc > > > ,
189
189
} ,
190
+ /// Return the list of IDs and intervals in the set of futures the sampler
191
+ /// is tracking.
192
+ #[ cfg( all( test, target_os = "illumos" ) ) ]
193
+ FutureDetails { reply_tx : oneshot:: Sender < Vec < ( TargetId , Duration ) > > } ,
190
194
}
191
195
192
196
/// Data about a single kstat target.
@@ -267,6 +271,17 @@ impl core::future::Future for YieldIdAfter {
267
271
}
268
272
}
269
273
274
+ // The operation we want to take on a future in our set, after handling an inbox
275
+ // message.
276
+ enum Operation {
277
+ // We want to add a new future.
278
+ Add ( YieldIdAfter ) ,
279
+ // Remove a future with the existing ID.
280
+ Remove ( TargetId ) ,
281
+ // We want to update an existing future.
282
+ Update ( ( TargetId , Duration ) ) ,
283
+ }
284
+
270
285
/// An owned type used to keep track of the creation time for each kstat in
271
286
/// which interest has been signaled.
272
287
#[ derive( Clone , Debug , Eq , Ord , PartialEq , PartialOrd ) ]
@@ -345,6 +360,9 @@ struct KstatSamplerWorker {
345
360
/// at construction time. In that case, we'll try again the next time we
346
361
/// need it.
347
362
self_stats : Option < self_stats:: SelfStats > ,
363
+
364
+ /// The futures that resolve when it's time to sample the next target.
365
+ sample_timeouts : FuturesUnordered < YieldIdAfter > ,
348
366
}
349
367
350
368
fn hostname ( ) -> Option < String > {
@@ -358,7 +376,7 @@ fn hostname() -> Option<String> {
358
376
359
377
/// Stores the number of samples taken, used for testing.
360
378
#[ cfg( all( test, target_os = "illumos" ) ) ]
361
- #[ derive( Clone , Copy , Debug ) ]
379
+ #[ derive( Clone , Copy , Debug , Default ) ]
362
380
pub ( crate ) struct SampleCounts {
363
381
pub total : usize ,
364
382
pub overflow : usize ,
@@ -393,6 +411,7 @@ impl KstatSamplerWorker {
393
411
sample_limit,
394
412
self_stat_queue,
395
413
self_stats,
414
+ sample_timeouts : FuturesUnordered :: new ( ) ,
396
415
} )
397
416
}
398
417
@@ -405,7 +424,6 @@ impl KstatSamplerWorker {
405
424
#[ cfg( all( test, target_os = "illumos" ) ) ]
406
425
sample_count_tx : mpsc:: UnboundedSender < SampleCounts > ,
407
426
) {
408
- let mut sample_timeouts = FuturesUnordered :: new ( ) ;
409
427
let mut creation_prune_interval =
410
428
interval ( CREATION_TIME_PRUNE_INTERVAL ) ;
411
429
creation_prune_interval. tick ( ) . await ; // Completes immediately.
@@ -420,7 +438,7 @@ impl KstatSamplerWorker {
420
438
) ;
421
439
}
422
440
}
423
- maybe_id = sample_timeouts. next( ) , if !sample_timeouts. is_empty( ) => {
441
+ maybe_id = self . sample_timeouts. next( ) , if !self . sample_timeouts. is_empty( ) => {
424
442
let Some ( ( id, interval) ) = maybe_id else {
425
443
unreachable!( ) ;
426
444
} ;
@@ -430,7 +448,7 @@ impl KstatSamplerWorker {
430
448
#[ cfg( all( test, target_os = "illumos" ) ) ]
431
449
& sample_count_tx,
432
450
) {
433
- sample_timeouts. push( next_timeout) ;
451
+ self . sample_timeouts. push( next_timeout) ;
434
452
}
435
453
}
436
454
maybe_request = self . inbox. recv( ) => {
@@ -443,19 +461,53 @@ impl KstatSamplerWorker {
443
461
"received request on inbox" ;
444
462
"request" => ?request,
445
463
) ;
446
- if let Some ( next_timeout) = self . handle_inbox_request( request) {
447
- sample_timeouts. push( next_timeout) ;
464
+ if let Some ( next_op) = self . handle_inbox_request( request) {
465
+ self . update_sample_timeouts( next_op) ;
466
+ }
467
+ }
468
+ }
469
+ }
470
+ }
471
+
472
+ fn update_sample_timeouts ( & mut self , next_op : Operation ) {
473
+ match next_op {
474
+ Operation :: Add ( fut) => self . sample_timeouts . push ( fut) ,
475
+ Operation :: Remove ( id) => {
476
+ // Swap out all futures, and then filter out the one we're now
477
+ // removing.
478
+ let old = std:: mem:: take ( & mut self . sample_timeouts ) ;
479
+ self . sample_timeouts
480
+ . extend ( old. into_iter ( ) . filter ( |fut| fut. id != id) ) ;
481
+ }
482
+ Operation :: Update ( ( new_id, new_interval) ) => {
483
+ // Update just the one future, if it exists, or insert one.
484
+ //
485
+ // NOTE: we update the _interval_, not the sleep object itself,
486
+ // which means this won't take effect until the next tick.
487
+ match self
488
+ . sample_timeouts
489
+ . iter_mut ( )
490
+ . find ( |fut| fut. id == new_id)
491
+ {
492
+ Some ( old) => old. interval = new_interval,
493
+ None => {
494
+ warn ! (
495
+ & self . log,
496
+ "attempting to update the samping future \
497
+ for a target, but no active future found \
498
+ in the set, it will be added directly";
499
+ "id" => %& new_id,
500
+ ) ;
501
+ self . sample_timeouts
502
+ . push ( YieldIdAfter :: new ( new_id, new_interval) ) ;
448
503
}
449
504
}
450
505
}
451
506
}
452
507
}
453
508
454
509
// Handle a message on the worker's inbox.
455
- fn handle_inbox_request (
456
- & mut self ,
457
- request : Request ,
458
- ) -> Option < YieldIdAfter > {
510
+ fn handle_inbox_request ( & mut self , request : Request ) -> Option < Operation > {
459
511
match request {
460
512
Request :: AddTarget { target, details, reply_tx } => {
461
513
match self . add_target ( target, details) {
@@ -475,7 +527,10 @@ impl KstatSamplerWorker {
475
527
"error" => ?e,
476
528
) ,
477
529
}
478
- Some ( YieldIdAfter :: new ( id, details. interval ) )
530
+ Some ( Operation :: Add ( YieldIdAfter :: new (
531
+ id,
532
+ details. interval ,
533
+ ) ) )
479
534
}
480
535
Err ( e) => {
481
536
error ! (
@@ -513,7 +568,7 @@ impl KstatSamplerWorker {
513
568
"error" => ?e,
514
569
) ,
515
570
}
516
- Some ( YieldIdAfter :: new ( id, details. interval ) )
571
+ Some ( Operation :: Update ( ( id, details. interval ) ) )
517
572
}
518
573
Err ( e) => {
519
574
error ! (
@@ -534,7 +589,7 @@ impl KstatSamplerWorker {
534
589
}
535
590
}
536
591
Request :: RemoveTarget { id, reply_tx } => {
537
- self . targets . remove ( & id) ;
592
+ let do_remove = self . targets . remove ( & id) . is_some ( ) ;
538
593
if let Some ( remaining_samples) =
539
594
self . samples . lock ( ) . unwrap ( ) . remove ( & id)
540
595
{
@@ -555,7 +610,7 @@ impl KstatSamplerWorker {
555
610
"error" => ?e,
556
611
) ,
557
612
}
558
- None
613
+ if do_remove { Some ( Operation :: Remove ( id ) ) } else { None }
559
614
}
560
615
Request :: TargetStatus { id, reply_tx } => {
561
616
trace ! (
@@ -594,6 +649,18 @@ impl KstatSamplerWorker {
594
649
debug ! ( self . log, "sent reply for creation times" ) ;
595
650
None
596
651
}
652
+ #[ cfg( all( test, target_os = "illumos" ) ) ]
653
+ Request :: FutureDetails { reply_tx } => {
654
+ debug ! ( self . log, "request for future details" ) ;
655
+ let details = self
656
+ . sample_timeouts
657
+ . iter ( )
658
+ . map ( |fut| ( fut. id , fut. interval ) )
659
+ . collect ( ) ;
660
+ reply_tx. send ( details) . unwrap ( ) ;
661
+ debug ! ( self . log, "sent reply for future details" ) ;
662
+ None
663
+ }
597
664
}
598
665
}
599
666
@@ -1296,6 +1363,15 @@ impl KstatSampler {
1296
1363
self . outbox . send ( request) . await . map_err ( |_| Error :: SendError ) . unwrap ( ) ;
1297
1364
reply_rx. await . map_err ( |_| Error :: RecvError ) . unwrap ( )
1298
1365
}
1366
+
1367
+ /// Return the IDs and sampling intervals for all futures in the sampler.
1368
+ #[ cfg( all( test, target_os = "illumos" ) ) ]
1369
+ pub ( crate ) async fn future_details ( & self ) -> Vec < ( TargetId , Duration ) > {
1370
+ let ( reply_tx, reply_rx) = oneshot:: channel ( ) ;
1371
+ let request = Request :: FutureDetails { reply_tx } ;
1372
+ self . outbox . send ( request) . await . map_err ( |_| Error :: SendError ) . unwrap ( ) ;
1373
+ reply_rx. await . map_err ( |_| Error :: RecvError ) . unwrap ( )
1374
+ }
1299
1375
}
1300
1376
1301
1377
impl oximeter:: Producer for KstatSampler {
0 commit comments