github.com/Cloud-Foundations/Dominator@v0.3.4/lib/cpusharer/fifo.go (about) 1 package cpusharer 2 3 import ( 4 "fmt" 5 "os" 6 "runtime" 7 "time" 8 9 "github.com/Cloud-Foundations/Dominator/lib/format" 10 ) 11 12 func newFifoCpuSharer() *FifoCpuSharer { 13 return &FifoCpuSharer{ 14 semaphore: make(chan struct{}, runtime.NumCPU()), 15 grabTimeout: -1, 16 lastAcquireEvent: time.Now(), 17 lastIdleEvent: time.Now(), 18 lastYieldEvent: time.Now(), 19 } 20 } 21 22 func (s *FifoCpuSharer) getStatistics() Statistics { 23 s.mutex.Lock() 24 defer s.mutex.Unlock() 25 s.Statistics.LastAcquireEvent = s.lastAcquireEvent 26 s.Statistics.LastIdleEvent = s.lastIdleEvent 27 s.Statistics.LastYieldEvent = s.lastYieldEvent 28 s.Statistics.NumCpuRunning = uint(len(s.semaphore)) 29 s.Statistics.NumCpu = uint(cap(s.semaphore)) 30 s.Statistics.NumIdleEvents = s.numIdleEvents 31 return s.Statistics 32 } 33 34 func (s *FifoCpuSharer) goWhenIdle(minIdleTime, timeout time.Duration, 35 goFunc func()) bool { 36 if !s.grabIdleCpu(minIdleTime, timeout) { 37 return false 38 } 39 go func() { 40 goFunc() 41 s.ReleaseCpu() 42 }() 43 return true 44 } 45 46 func (s *FifoCpuSharer) grabCpu() { 47 select { 48 case s.semaphore <- struct{}{}: // A CPU is immediately available. 49 s.mutex.Lock() 50 defer s.mutex.Unlock() 51 s.lastAcquireEvent = time.Now() 52 s.lastIdleEvent = s.lastAcquireEvent 53 s.numIdleEvents++ 54 return 55 default: // No CPU is available yet: block waiting with timeout. 56 s.mutex.Lock() 57 timeout := s.grabTimeout 58 s.mutex.Unlock() 59 if timeout < 0 { 60 s.semaphore <- struct{}{} // Block forever waiting for a CPU. 61 s.mutex.Lock() 62 defer s.mutex.Unlock() 63 s.lastAcquireEvent = time.Now() 64 return 65 } 66 timer := time.NewTimer(timeout) 67 select { 68 case s.semaphore <- struct{}{}: // A CPU became available. 69 if !timer.Stop() { 70 <-timer.C 71 } 72 s.mutex.Lock() 73 defer s.mutex.Unlock() 74 s.lastAcquireEvent = time.Now() 75 return 76 case <-timer.C: 77 stats := s.GetStatistics() 78 fmt.Fprintf(os.Stderr, 79 "CPU grabber timeout. Last acquire: %s, last yield: %s\n", 80 format.Duration(time.Since(stats.LastAcquireEvent)), 81 format.Duration(time.Since(stats.LastYieldEvent))) 82 fmt.Fprintln(os.Stderr, "Full stack track follows:") 83 buf := make([]byte, 1024*1024) 84 nBytes := runtime.Stack(buf, true) 85 os.Stderr.Write(buf[0:nBytes]) 86 os.Stderr.Write([]byte("\n")) 87 panic("timeout") 88 } 89 } 90 } 91 92 func (s *FifoCpuSharer) grabIdleCpu(minIdleTime, timeout time.Duration) bool { 93 var timeoutTime time.Time 94 if timeout >= 0 { 95 timeoutTime = time.Now().Add(timeout) 96 } 97 for timeout < 0 || time.Now().Before(timeoutTime) { 98 select { 99 case s.semaphore <- struct{}{}: // A CPU was idle. 100 if minIdleTime > 0 { 101 <-s.semaphore // Yield CPU and maybe check again later. 102 if timeout >= 0 { 103 if minIdleTime > time.Until(timeoutTime) { 104 return false 105 } 106 } 107 time.Sleep(minIdleTime) 108 select { 109 case s.semaphore <- struct{}{}: // A CPU was idle. 110 default: // No idle CPU: try again. 111 continue 112 } 113 } 114 s.mutex.Lock() 115 defer s.mutex.Unlock() 116 s.lastAcquireEvent = time.Now() 117 s.lastIdleEvent = s.lastAcquireEvent 118 s.numIdleEvents++ 119 return true 120 default: // No CPU is available yet: wait with possible timeout. 121 if timeout < 0 { 122 s.semaphore <- struct{}{} 123 } else { 124 timer := time.NewTimer(time.Until(timeoutTime)) 125 select { 126 case s.semaphore <- struct{}{}: // A CPU became available. 127 if !timer.Stop() { 128 <-timer.C 129 } 130 case <-timer.C: 131 return false // Timeout. 132 } 133 } 134 // Yield CPU and give someone else a chance to ensure really idle. 135 <-s.semaphore 136 runtime.Gosched() 137 } 138 } 139 return false 140 } 141 142 func (s *FifoCpuSharer) releaseCpu() { 143 s.mutex.Lock() 144 defer s.mutex.Unlock() 145 s.lastYieldEvent = time.Now() 146 <-s.semaphore 147 } 148 149 func (s *FifoCpuSharer) setGrabTimeout(timeout time.Duration) { 150 s.mutex.Lock() 151 defer s.mutex.Unlock() 152 s.grabTimeout = timeout 153 }