...
Run Format

Source file src/sync/mutex.go

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

View as plain text