@@ -17,10 +17,9 @@ use crate::bindings::ddog_php_prof_get_active_fiber_test as ddog_php_prof_get_ac
17
17
18
18
use crate :: bindings:: { datadog_php_profiling_get_profiling_context, zend_execute_data} ;
19
19
use crate :: config:: SystemSettings ;
20
+ use crate :: profiling:: thread_utils:: { JoinError , ThreadPanicError } ;
20
21
use crate :: { CLOCKS , TAGS } ;
21
22
use chrono:: Utc ;
22
- #[ cfg( feature = "timeline" ) ]
23
- use core:: { ptr, str} ;
24
23
use crossbeam_channel:: { Receiver , Sender , TrySendError } ;
25
24
use datadog_profiling:: api:: {
26
25
Function , Label as ApiLabel , Location , Period , Sample , ValueType as ApiValueType ,
@@ -35,11 +34,14 @@ use std::hash::Hash;
35
34
use std:: intrinsics:: transmute;
36
35
use std:: mem:: forget;
37
36
use std:: num:: NonZeroI64 ;
37
+ use std:: ops:: BitOrAssign ;
38
38
use std:: sync:: atomic:: { AtomicBool , AtomicPtr , AtomicU32 , Ordering } ;
39
39
use std:: sync:: { Arc , Barrier } ;
40
40
use std:: thread:: JoinHandle ;
41
41
use std:: time:: { Duration , Instant , SystemTime } ;
42
42
43
+ #[ cfg( feature = "timeline" ) ]
44
+ use core:: { ptr, str} ;
43
45
#[ cfg( feature = "timeline" ) ]
44
46
use std:: time:: UNIX_EPOCH ;
45
47
@@ -689,33 +691,53 @@ impl Profiler {
689
691
/// Completes the shutdown process; to start it, call [Profiler::stop]
690
692
/// before calling [Profiler::shutdown].
691
693
/// Note the timeout is per thread, and there may be multiple threads.
694
+ /// Returns Ok(true) if any thread hit a timeout.
692
695
///
693
696
/// Safety: only safe to be called in `SHUTDOWN`/`MSHUTDOWN` phase
694
- pub fn shutdown ( timeout : Duration ) {
697
+ pub fn shutdown ( timeout : Duration ) -> Result < bool , ShutdownError > {
695
698
// SAFETY: the `take` access is a thread-safe API, and the PROFILER is
696
699
// not being mutated outside single-threaded phases such as minit and
697
700
// mshutdown.
698
701
if let Some ( profiler) = unsafe { PROFILER . take ( ) } {
699
- profiler. join_collector_and_uploader ( timeout) ;
702
+ Ok ( profiler. join_collector_and_uploader ( timeout) ?)
703
+ } else {
704
+ Ok ( false )
700
705
}
701
706
}
702
707
703
- pub fn join_collector_and_uploader ( self , timeout : Duration ) {
708
+ fn join_collector_and_uploader ( self , timeout : Duration ) -> Result < bool , ThreadPanicError > {
704
709
if self . should_join . load ( Ordering :: SeqCst ) {
705
- thread_utils:: join_timeout (
706
- self . time_collector_handle ,
707
- timeout,
708
- "Recent samples may be lost." ,
709
- ) ;
710
+ let result = thread_utils:: join_timeout ( self . time_collector_handle , timeout) ;
711
+ let mut hit_timeout = if let Err ( err) = result {
712
+ match err {
713
+ JoinError :: ThreadPanic ( err) => Err ( err) ?,
714
+ timeout_err => {
715
+ warn ! ( "{}, recent samples may be lost" , timeout_err) ;
716
+ true
717
+ }
718
+ }
719
+ } else {
720
+ false
721
+ } ;
710
722
711
723
// Wait for the time_collector to join, since that will drop
712
724
// the sender half of the channel that the uploader is
713
725
// holding, allowing it to finish.
714
- thread_utils:: join_timeout (
715
- self . uploader_handle ,
716
- timeout,
717
- "Recent samples are most likely lost." ,
718
- ) ;
726
+ let result = thread_utils:: join_timeout ( self . uploader_handle , timeout) ;
727
+ hit_timeout. bitor_assign ( if let Err ( err) = result {
728
+ match err {
729
+ JoinError :: ThreadPanic ( err) => Err ( err) ?,
730
+ timeout_err => {
731
+ warn ! ( "{}, recent samples are most likely lost" , timeout_err) ;
732
+ true
733
+ }
734
+ }
735
+ } else {
736
+ false
737
+ } ) ;
738
+ Ok ( hit_timeout)
739
+ } else {
740
+ Ok ( false )
719
741
}
720
742
}
721
743
@@ -1288,6 +1310,15 @@ impl Profiler {
1288
1310
}
1289
1311
}
1290
1312
1313
+ #[ derive( thiserror:: Error , Debug ) ]
1314
+ pub enum ShutdownError {
1315
+ #[ error( transparent) ]
1316
+ ThreadPanic {
1317
+ #[ from]
1318
+ payload : ThreadPanicError ,
1319
+ } ,
1320
+ }
1321
+
1291
1322
#[ cfg( test) ]
1292
1323
mod tests {
1293
1324
use super :: * ;
0 commit comments