github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/trackedlock/api.go (about)

     1  package trackedlock
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"sync/atomic"
     7  
     8  	"github.com/swiftstack/ProxyFS/logger"
     9  )
    10  
    11  /*
    12  
    13   * The trackedlock packages provides an implementation of sync.Mutex and
    14   * sync.RWMutex interfaces that provides additional functionality in the form of
    15   * lock hold tracking.  In addition, the trackedlock.RWMutexTrack routines can
    16   * be used to track other RWMutex-like synchronization primitives, like
    17   * dlm.RWLockStruct and chunked put connections.
    18   *
    19   * Specifically, if lock tracking is enabled, the trackedlock packages checks
    20   * the lock hold time.  When a lock unlocked, if it was held longer than
    21   * "LockHoldTimeLimit" then a warning is logged along with the stack trace of
    22   * the Lock() and Unlock() of the lock.  In addition, a daemon, the trackedlock
    23   * watcher, periodically checks to see if any lock has been locked too long.
    24   * When a lock is held too long, the daemon logs the goroutine ID and the stack
    25   * trace of the goroutine that acquired the lock.
    26   *
    27   * Subsequent versions of this package may include lock heirarchy checking.
    28   *
    29   * The config variable "TrackedLock.LockHoldTimeLimit" is the hold time that
    30   * triggers warning messages being logged.  If it is 0 then locks are not
    31   * tracked and the overhead of this package is minimal.
    32   *
    33   * The config variable "TrackedLock.LockCheckPeriod" is how often the daemon
    34   * checks tracked locks.  If it is 0 then no daemon is created and lock hold
    35   * time is checked only when the lock is unlocked (assuming it is unlocked).
    36   *
    37   * trackedlock locks can be locked before this package is initialized, but they
    38   * will not be tracked until the first time they are locked after
    39   * initializaiton.
    40   *
    41   * The API consists of the config based trackedlock.Up() / Down() /
    42   * PauseAndContract() / ExpandAndResume() (which are not defined here) and then
    43   * the Mutex, RWMutex, and RWMutexTrack interfaces.
    44   */
    45  
    46  // The Mutex type that we export, which wraps sync.Mutex to add tracking of lock
    47  // hold time and the stack trace of the locker.
    48  //
    49  type Mutex struct {
    50  	wrappedMutex sync.Mutex // the actual Mutex
    51  	tracker      MutexTrack // tracking information for the Mutex
    52  }
    53  
    54  // The RWMutex type that we export, which wraps sync.RWMutex to add tracking of
    55  // lock hold time and the stack trace of the locker.
    56  //
    57  type RWMutex struct {
    58  	wrappedRWMutex sync.RWMutex // actual Mutex
    59  	rwTracker      RWMutexTrack // track holds in shared (reader) mode
    60  }
    61  
    62  //
    63  // Tracked Mutex API
    64  //
    65  func (m *Mutex) Lock() {
    66  	m.wrappedMutex.Lock()
    67  
    68  	m.tracker.lockTrack(m, nil)
    69  }
    70  
    71  func (m *Mutex) Unlock() {
    72  	m.tracker.unlockTrack(m)
    73  
    74  	m.wrappedMutex.Unlock()
    75  }
    76  
    77  //
    78  // Tracked RWMutex API
    79  //
    80  func (m *RWMutex) Lock() {
    81  	m.wrappedRWMutex.Lock()
    82  
    83  	m.rwTracker.lockTrack(m)
    84  }
    85  
    86  func (m *RWMutex) Unlock() {
    87  	m.rwTracker.unlockTrack(m)
    88  
    89  	m.wrappedRWMutex.Unlock()
    90  }
    91  
    92  func (m *RWMutex) RLock() {
    93  	m.wrappedRWMutex.RLock()
    94  
    95  	m.rwTracker.rLockTrack(m)
    96  }
    97  
    98  func (m *RWMutex) RUnlock() {
    99  	m.rwTracker.rUnlockTrack(m)
   100  
   101  	m.wrappedRWMutex.RUnlock()
   102  }
   103  
   104  //
   105  // Direct access to trackedlock API for DLM locks
   106  //
   107  func (rwmt *RWMutexTrack) LockTrack(lck interface{}) {
   108  	rwmt.lockTrack(lck)
   109  }
   110  
   111  func (rwmt *RWMutexTrack) UnlockTrack(lck interface{}) {
   112  	rwmt.unlockTrack(lck)
   113  }
   114  
   115  func (rwmt *RWMutexTrack) RLockTrack(lck interface{}) {
   116  	rwmt.rLockTrack(lck)
   117  }
   118  
   119  func (rwmt *RWMutexTrack) RUnlockTrack(lck interface{}) {
   120  	rwmt.rUnlockTrack(lck)
   121  }
   122  
   123  func (rwmt *RWMutexTrack) DLMUnlockTrack(lck interface{}) {
   124  	// This uses m.tracker.lockCnt without holding the mutex that protects it.
   125  	// Because this goroutine holds the lock, it cannot change from -1 to 0
   126  	// or >0 to 0 (unless there's a bug where another goroutine releases the
   127  	// lock).  It can change from, say, 1 to 2 or 4 to 3, but that's benign
   128  	// (let's hope the race detector doesn't complain).
   129  	lockCnt := atomic.LoadInt32(&rwmt.tracker.lockCnt)
   130  	switch {
   131  	case lockCnt == -1:
   132  		rwmt.unlockTrack(lck)
   133  	case lockCnt > 0:
   134  		rwmt.rUnlockTrack(lck)
   135  	default:
   136  		errstring := fmt.Errorf("tracker for RWMutexTrack has illegal lockCnt %d", lockCnt)
   137  		logger.PanicfWithError(errstring, "%T lock at %p: %+v", lck, lck, lck)
   138  	}
   139  	return
   140  }