github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/util/deadlock-detector.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors All rights reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "os" 21 "sync" 22 "time" 23 24 "github.com/golang/glog" 25 ) 26 27 type rwMutexToLockableAdapter struct { 28 rw *sync.RWMutex 29 } 30 31 func (r *rwMutexToLockableAdapter) Lock() { 32 r.rw.RLock() 33 } 34 35 func (r *rwMutexToLockableAdapter) Unlock() { 36 r.rw.RUnlock() 37 } 38 39 type deadlockDetector struct { 40 name string 41 lock sync.Locker 42 maxLockPeriod time.Duration 43 exiter exiter 44 exitChannelFn func() <-chan time.Time 45 // Really only useful for testing 46 stopChannel <-chan bool 47 } 48 49 // DeadlockWatchdogReadLock creates a watchdog on read/write mutex. If the mutex can not be acquired 50 // for read access within 'maxLockPeriod', the program exits via glog.Exitf() or os.Exit() if that fails 51 // 'name' is a semantic name that is useful for the user and is printed on exit. 52 func DeadlockWatchdogReadLock(lock *sync.RWMutex, name string, maxLockPeriod time.Duration) { 53 DeadlockWatchdog(&rwMutexToLockableAdapter{lock}, name, maxLockPeriod) 54 } 55 56 func DeadlockWatchdog(lock sync.Locker, name string, maxLockPeriod time.Duration) { 57 if maxLockPeriod <= 0 { 58 panic("maxLockPeriod is <= 0, that can't be what you wanted") 59 } 60 detector := &deadlockDetector{ 61 lock: lock, 62 name: name, 63 maxLockPeriod: maxLockPeriod, 64 exitChannelFn: func() <-chan time.Time { return time.After(maxLockPeriod) }, 65 stopChannel: make(chan bool), 66 } 67 go detector.run() 68 } 69 70 // Useful for injecting tests 71 type exiter interface { 72 Exitf(format string, args ...interface{}) 73 } 74 75 type realExiter struct{} 76 77 func (realExiter) Exitf(format string, args ...interface{}) { 78 func() { 79 defer func() { 80 // Let's just be extra sure we die, even if Exitf panics 81 if r := recover(); r != nil { 82 glog.Errorf(format, args...) 83 os.Exit(2) 84 } 85 }() 86 glog.Exitf(format, args...) 87 }() 88 } 89 90 func (d *deadlockDetector) run() { 91 for { 92 if !d.runOnce() { 93 return 94 } 95 time.Sleep(d.maxLockPeriod / 2) 96 } 97 } 98 99 func (d *deadlockDetector) runOnce() bool { 100 ch := make(chan bool, 1) 101 go func() { 102 d.lock.Lock() 103 d.lock.Unlock() 104 105 ch <- true 106 }() 107 exitCh := d.exitChannelFn() 108 select { 109 case <-exitCh: 110 d.exiter.Exitf("Deadlock on %s, exiting", d.name) 111 // return is here for when we use a fake exiter in testing 112 return false 113 case <-ch: 114 glog.V(6).Infof("%s is not deadlocked", d.name) 115 case <-d.stopChannel: 116 glog.V(4).Infof("Stopping deadlock detector for %s", d.name) 117 return false 118 } 119 return true 120 }