Home | History | Annotate | Download | only in sync
      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 }
    127