github.com/haraldrudell/parl@v0.4.176/slow-detector-thread.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/haraldrudell/parl/parli" 14 "github.com/haraldrudell/parl/perrors" 15 "github.com/haraldrudell/parl/pmaps" 16 "github.com/haraldrudell/parl/sets" 17 ) 18 19 const ( 20 SlowDefault slowType = iota 21 SlowOwnThread 22 SlowShutdownThread 23 24 slowScanPeriod = time.Second 25 ) 26 27 // shared SlowDetectorThread for SlowDefault threads 28 var slowDetectorThread SlowDetectorThread 29 30 type SlowDetectorThread struct { 31 slowTyp slowType 32 nonReturnPeriod time.Duration 33 slowMap pmaps.RWMap[slowID, *SlowDetectorInvocation] 34 hasThread atomic.Bool 35 36 slowLock sync.Mutex 37 goGen GoGen 38 cancelGo func() 39 } 40 41 func NewSlowDetectorThread(slowTyp slowType, nonReturnPeriod time.Duration, goGen GoGen) (sdt *SlowDetectorThread) { 42 if goGen == nil { 43 panic(perrors.NewPF("goGen cannot be nil")) 44 } 45 46 // dedicated thread case 47 if slowTyp != SlowDefault { 48 return &SlowDetectorThread{ 49 slowTyp: slowTyp, 50 nonReturnPeriod: nonReturnPeriod, 51 slowMap: *pmaps.NewRWMap2[slowID, *SlowDetectorInvocation](), 52 goGen: goGen, 53 } 54 } 55 56 sdt = &slowDetectorThread 57 sdt.slowLock.Lock() 58 defer sdt.slowLock.Unlock() 59 60 if sdt.goGen != nil { 61 return // slowDetectorThread already initialized return 62 } 63 64 // slowDetectorThread initialization 65 sdt.slowTyp = slowTyp 66 sdt.nonReturnPeriod = nonReturnPeriod 67 sdt.slowMap = *pmaps.NewRWMap2[slowID, *SlowDetectorInvocation]() 68 sdt.goGen = goGen 69 70 return 71 } 72 73 func (sdt *SlowDetectorThread) Start(sdi *SlowDetectorInvocation) { 74 75 // store in map 76 sdt.slowMap.Put(sdi.sID, sdi) 77 78 if !sdt.hasThread.CompareAndSwap(false, true) { 79 return // thread already running return 80 } 81 82 // launch thread 83 subGo := sdt.goGen.SubGo() 84 g0 := subGo.Go() 85 go sdt.thread(g0) 86 if sdt.slowTyp != SlowShutdownThread { 87 return // thread is not to be shutdown return 88 } 89 90 // save cancel method 91 sdt.slowLock.Lock() 92 defer sdt.slowLock.Unlock() 93 94 sdt.cancelGo = subGo.Cancel 95 } 96 97 func (sdt *SlowDetectorThread) Stop(sdi *SlowDetectorInvocation) { 98 99 // remove from map 100 sdt.slowMap.Delete(sdi.sID, parli.MapDeleteWithZeroValue) 101 102 if sdt.slowMap.Length() > 0 || sdt.slowTyp != SlowShutdownThread { 103 return // not to be shutdown or not to be shutdown now return 104 } 105 106 sdt.cancelGo() 107 } 108 109 func (sdt *SlowDetectorThread) thread(g0 Go) { 110 var err error 111 defer g0.Register("SlowDetectorThread" + goID().String()).Done(&err) 112 defer PanicToErr(&err) 113 114 ticker := time.NewTicker(slowScanPeriod) 115 defer ticker.Stop() 116 117 var C <-chan time.Time = ticker.C 118 var done <-chan struct{} = g0.Context().Done() 119 var t time.Time 120 for { 121 select { 122 case <-done: 123 return // context cancelled return 124 case t = <-C: 125 } 126 127 // check all invocations for non-return 128 for _, sdi := range sdt.slowMap.List() { 129 // duration is how long the invocation has been in progress 130 duration := t.Sub(sdi.t0) 131 if duration < 0 { 132 // if t coming from the ticker was delayed, 133 // then t may be a time in the past, 134 // so early that sdi.t0 is after t 135 continue // ignore negative durations 136 } 137 sd := sdi.sd 138 sd.alwaysMax.Value(duration) 139 if sd.max.Value(duration) { 140 // it is a new max, check whether nonReturnPeriod has elapsed 141 if tLast := sdi.Time(time.Time{}); tLast.IsZero() || t.Sub(tLast) >= sdt.nonReturnPeriod { 142 143 // store new nonReturnPeriod start 144 sdi.Time(t) 145 sd.callback(sdi, false, duration) 146 } 147 } 148 } 149 } 150 } 151 152 type slowType uint8 153 154 func (st slowType) String() (s string) { 155 return slowTypeSet.StringT(st) 156 } 157 158 var slowTypeSet = sets.NewSet[slowType]([]sets.SetElement[slowType]{ 159 {ValueV: SlowDefault, Name: "sharedThread"}, 160 {ValueV: SlowOwnThread, Name: "ownThread"}, 161 {ValueV: SlowShutdownThread, Name: "shutdownThread"}, 162 })