...
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	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	

View as plain text