github.com/akaros/go-akaros@v0.0.0-20181004170632-85005d477eab/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  	for {
    52  		old := m.state
    53  		new := old | mutexLocked
    54  		if old&mutexLocked != 0 {
    55  			new = old + 1<<mutexWaiterShift
    56  		}
    57  		if awoke {
    58  			// The goroutine has been woken from sleep,
    59  			// so we need to reset the flag in either case.
    60  			new &^= mutexWoken
    61  		}
    62  		if atomic.CompareAndSwapInt32(&m.state, old, new) {
    63  			if old&mutexLocked == 0 {
    64  				break
    65  			}
    66  			runtime_Semacquire(&m.sema)
    67  			awoke = true
    68  		}
    69  	}
    70  
    71  	if raceenabled {
    72  		raceAcquire(unsafe.Pointer(m))
    73  	}
    74  }
    75  
    76  // Unlock unlocks m.
    77  // It is a run-time error if m is not locked on entry to Unlock.
    78  //
    79  // A locked Mutex is not associated with a particular goroutine.
    80  // It is allowed for one goroutine to lock a Mutex and then
    81  // arrange for another goroutine to unlock it.
    82  func (m *Mutex) Unlock() {
    83  	if raceenabled {
    84  		_ = m.state
    85  		raceRelease(unsafe.Pointer(m))
    86  	}
    87  
    88  	// Fast path: drop lock bit.
    89  	new := atomic.AddInt32(&m.state, -mutexLocked)
    90  	if (new+mutexLocked)&mutexLocked == 0 {
    91  		panic("sync: unlock of unlocked mutex")
    92  	}
    93  
    94  	old := new
    95  	for {
    96  		// If there are no waiters or a goroutine has already
    97  		// been woken or grabbed the lock, no need to wake anyone.
    98  		if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
    99  			return
   100  		}
   101  		// Grab the right to wake someone.
   102  		new = (old - 1<<mutexWaiterShift) | mutexWoken
   103  		if atomic.CompareAndSwapInt32(&m.state, old, new) {
   104  			runtime_Semrelease(&m.sema)
   105  			return
   106  		}
   107  		old = m.state
   108  	}
   109  }