1 // Copyright 2016 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package contention 16 17 import ( 18 "sync" 19 "time" 20 ) 21 22 // TimeoutDetector detects routine starvations by 23 // observing the actual time duration to finish an action 24 // or between two events that should happen in a fixed 25 // interval. If the observed duration is longer than 26 // the expectation, the detector will report the result. 27 type TimeoutDetector struct { 28 mu sync.Mutex // protects all 29 maxDuration time.Duration 30 // map from event to time 31 // time is the last seen time of the event. 32 records map[uint64]time.Time 33 } 34 35 // NewTimeoutDetector creates the TimeoutDetector. 36 func NewTimeoutDetector(maxDuration time.Duration) *TimeoutDetector { 37 return &TimeoutDetector{ 38 maxDuration: maxDuration, 39 records: make(map[uint64]time.Time), 40 } 41 } 42 43 // Reset resets the NewTimeoutDetector. 44 func (td *TimeoutDetector) Reset() { 45 td.mu.Lock() 46 defer td.mu.Unlock() 47 48 td.records = make(map[uint64]time.Time) 49 } 50 51 // Observe observes an event for given id. It returns false and exceeded duration 52 // if the interval is longer than the expectation. 53 func (td *TimeoutDetector) Observe(which uint64) (bool, time.Duration) { 54 td.mu.Lock() 55 defer td.mu.Unlock() 56 57 ok := true 58 now := time.Now() 59 exceed := time.Duration(0) 60 61 if pt, found := td.records[which]; found { 62 exceed = now.Sub(pt) - td.maxDuration 63 if exceed > 0 { 64 ok = false 65 } 66 } 67 td.records[which] = now 68 return ok, exceed 69 } 70