github.com/huandu/go@v0.0.0-20151114150818-04e615e41150/src/sync/mutex.go (about)

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package sync provides basic synchronization primitives such as mutual
     6  // exclusion locks.  Other than the Once and WaitGroup types, most are intended
     7  // for use by low-level library routines.  Higher-level synchronization is
     8  // better done via channels and communication.
     9  //
    10  // Values containing the types defined in this package should not be copied.
    11  package sync
    12  
    13  import (
    14  	"sync/atomic"
    15  	"unsafe"
    16  )
    17  
    18  // A Mutex is a mutual exclusion lock.
    19  // Mutexes can be created as part of other structures;
    20  // the zero value for a Mutex is an unlocked mutex.
    21  type Mutex struct {
    22  	state int32
    23  	sema  uint32
    24  }
    25  
    26  // A Locker represents an object that can be locked and unlocked.
    27  type Locker interface {
    28  	Lock()
    29  	Unlock()
    30  }
    31  
    32  const (
    33  	mutexLocked = 1 << iota // mutex is locked
    34  	mutexWoken
    35  	mutexWaiterShift = iota
    36  )
    37  
    38  // Lock locks m.
    39  // If the lock is already in use, the calling goroutine
    40  // blocks until the mutex is available.
    41  func (m *Mutex) Lock() {
    42  	// Fast path: grab unlocked mutex.
    43  	if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
    44  		if raceenabled {
    45  			raceAcquire(unsafe.Pointer(m))
    46  		}
    47  		return
    48  	}
    49  
    50  	awoke := false
    51  	iter := 0
    52  	for {
    53  		old := m.state
    54  		new := old | mutexLocked
    55  		if old&mutexLocked != 0 {
    56  			if runtime_canSpin(iter) {
    57  				// Active spinning makes sense.
    58  				// Try to set mutexWoken flag to inform Unlock
    59  				// to not wake other blocked goroutines.
    60  				if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
    61  					atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
    62  					awoke = true
    63  				}
    64  				runtime_doSpin()
    65  				iter++
    66  				continue
    67  			}
    68  			new = old + 1<<mutexWaiterShift
    69  		}
    70  		if awoke {
    71  			// The goroutine has been woken from sleep,
    72  			// so we need to reset the flag in either case.
    73  			if new&mutexWoken == 0 {
    74  				panic("sync: inconsistent mutex state")
    75  			}
    76  			new &^= mutexWoken
    77  		}
    78  		if atomic.CompareAndSwapInt32(&m.state, old, new) {
    79  			if old&mutexLocked == 0 {
    80  				break
    81  			}
    82  			runtime_Semacquire(&m.sema)
    83  			awoke = true
    84  			iter = 0
    85  		}
    86  	}
    87  
    88  	if raceenabled {
    89  		raceAcquire(unsafe.Pointer(m))
    90  	}
    91  }
    92  
    93  // Unlock unlocks m.
    94  // It is a run-time error if m is not locked on entry to Unlock.
    95  //
    96  // A locked Mutex is not associated with a particular goroutine.
    97  // It is allowed for one goroutine to lock a Mutex and then
    98  // arrange for another goroutine to unlock it.
    99  func (m *Mutex) Unlock() {
   100  	if raceenabled {
   101  		_ = m.state
   102  		raceRelease(unsafe.Pointer(m))
   103  	}
   104  
   105  	// Fast path: drop lock bit.
   106  	new := atomic.AddInt32(&m.state, -mutexLocked)
   107  	if (new+mutexLocked)&mutexLocked == 0 {
   108  		panic("sync: unlock of unlocked mutex")
   109  	}
   110  
   111  	old := new
   112  	for {
   113  		// If there are no waiters or a goroutine has already
   114  		// been woken or grabbed the lock, no need to wake anyone.
   115  		if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
   116  			return
   117  		}
   118  		// Grab the right to wake someone.
   119  		new = (old - 1<<mutexWaiterShift) | mutexWoken
   120  		if atomic.CompareAndSwapInt32(&m.state, old, new) {
   121  			runtime_Semrelease(&m.sema)
   122  			return
   123  		}
   124  		old = m.state
   125  	}
   126  }