github.com/haraldrudell/parl@v0.4.176/halt/halt-detector.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 // Package halt detects Go runtime execution halts. 7 package halt 8 9 import ( 10 "time" 11 12 "github.com/haraldrudell/parl" 13 ) 14 15 const ( 16 // timeSleepDuration is how long sleep is requested from the Go runtime time.Sleep function 17 // - needs to be about 10x shorter than the advertised minimum Go garbage-collector stop-the-world 18 // intervale, which is 1 ms 19 timeSleepDuration = 100 * time.Microsecond 20 defaultReportingThreshold = 500 * time.Microsecond 21 ) 22 23 // HaltDetector sends detected Go runtime execution halts on channel ch. 24 type HaltDetector struct { 25 reportingThreshold time.Duration 26 ch parl.NBChan[*HaltReport] 27 } 28 29 // HaltReport is a value object representing a detected Go runtime execution halt 30 type HaltReport struct { 31 N int // report number 1… 32 T time.Time // when halt started 33 D time.Duration // halt duration 34 } 35 36 // NewHaltDetector returns an object that sends detected Go runtime execution halts on a channel 37 func NewHaltDetector(reportingThreshold ...time.Duration) (haltDetector *HaltDetector) { 38 var t time.Duration 39 if len(reportingThreshold) > 0 { 40 t = reportingThreshold[0] 41 } 42 if t < defaultReportingThreshold { 43 t = defaultReportingThreshold 44 } 45 return &HaltDetector{reportingThreshold: t} 46 } 47 48 // Thread detects execution halts and sends them on h.ch 49 func (h *HaltDetector) Thread(g0 parl.Go) { 50 var err error 51 defer g0.Register().Done(&err) 52 defer parl.PanicToErr(&err) 53 54 timeTicker := time.NewTicker(time.Millisecond) 55 defer timeTicker.Stop() 56 57 var done = g0.Context().Done() 58 var elapsed time.Duration 59 var t0 time.Time 60 var t1 = time.Now() 61 var reportingThreshold = h.reportingThreshold 62 var n int 63 var C = timeTicker.C 64 for { 65 66 // sleep 67 t0 = t1 68 select { 69 case <-done: 70 return // g0 context cancel return 71 case <-C: 72 } 73 t1 = time.Now() 74 elapsed = t1.Sub(t0) 75 76 // report 77 if elapsed >= reportingThreshold { 78 n++ 79 h.ch.Send(&HaltReport{N: n, T: t0, D: elapsed}) 80 } 81 } 82 } 83 84 // Ch returns a receive channel for reports of Go rutnime execution halts 85 // - Ch never closes 86 func (h *HaltDetector) Ch() (ch <-chan *HaltReport) { 87 return h.ch.Ch() 88 }