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 	"internal/race"
     15 	"sync/atomic"
     16 	"unsafe"
     17 )
     18 
     19 func throw(string) // provided by runtime
     20 
     21 // A Mutex is a mutual exclusion lock.
     22 // Mutexes can be created as part of other structures;
     23 // the zero value for a Mutex is an unlocked mutex.
     24 //
     25 // A Mutex must not be copied after first use.
     26 type Mutex struct {
     27 	state int32
     28 	sema  uint32
     29 }
     30 
     31 // A Locker represents an object that can be locked and unlocked.
     32 type Locker interface {
     33 	Lock()
     34 	Unlock()
     35 }
     36 
     37 const (
     38 	mutexLocked = 1 << iota // mutex is locked
     39 	mutexWoken
     40 	mutexWaiterShift = iota
     41 )
     42 
     43 // Lock locks m.
     44 // If the lock is already in use, the calling goroutine
     45 // blocks until the mutex is available.
     46 func (m *Mutex) Lock() {
     47 	// Fast path: grab unlocked mutex.
     48 	if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
     49 		if race.Enabled {
     50 			race.Acquire(unsafe.Pointer(m))
     51 		}
     52 		return
     53 	}
     54 
     55 	awoke := false
     56 	iter := 0
     57 	for {
     58 		old := m.state
     59 		new := old | mutexLocked
     60 		if old&mutexLocked != 0 {
     61 			if runtime_canSpin(iter) {
     62 				// Active spinning makes sense.
     63 				// Try to set mutexWoken flag to inform Unlock
     64 				// to not wake other blocked goroutines.
     65 				if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&
     66 					atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {
     67 					awoke = true
     68 				}
     69 				runtime_doSpin()
     70 				iter++
     71 				continue
     72 			}
     73 			new = old + 1<<mutexWaiterShift
     74 		}
     75 		if awoke {
     76 			// The goroutine has been woken from sleep,
     77 			// so we need to reset the flag in either case.
     78 			if new&mutexWoken == 0 {
     79 				throw("sync: inconsistent mutex state")
     80 			}
     81 			new &^= mutexWoken
     82 		}
     83 		if atomic.CompareAndSwapInt32(&m.state, old, new) {
     84 			if old&mutexLocked == 0 {
     85 				break
     86 			}
     87 			runtime_SemacquireMutex(&m.sema)
     88 			awoke = true
     89 			iter = 0
     90 		}
     91 	}
     92 
     93 	if race.Enabled {
     94 		race.Acquire(unsafe.Pointer(m))
     95 	}
     96 }
     97 
     98 // Unlock unlocks m.
     99 // It is a run-time error if m is not locked on entry to Unlock.
    100 //
    101 // A locked Mutex is not associated with a particular goroutine.
    102 // It is allowed for one goroutine to lock a Mutex and then
    103 // arrange for another goroutine to unlock it.
    104 func (m *Mutex) Unlock() {
    105 	if race.Enabled {
    106 		_ = m.state
    107 		race.Release(unsafe.Pointer(m))
    108 	}
    109 
    110 	// Fast path: drop lock bit.
    111 	new := atomic.AddInt32(&m.state, -mutexLocked)
    112 	if (new+mutexLocked)&mutexLocked == 0 {
    113 		throw("sync: unlock of unlocked mutex")
    114 	}
    115 
    116 	old := new
    117 	for {
    118 		// If there are no waiters or a goroutine has already
    119 		// been woken or grabbed the lock, no need to wake anyone.
    120 		if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
    121 			return
    122 		}
    123 		// Grab the right to wake someone.
    124 		new = (old - 1<<mutexWaiterShift) | mutexWoken
    125 		if atomic.CompareAndSwapInt32(&m.state, old, new) {
    126 			runtime_Semrelease(&m.sema)
    127 			return
    128 		}
    129 		old = m.state
    130 	}
    131 }
    132