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  }