src/pkg/sync/mutex.go - The Go Programming Language

Golang

Source file src/pkg/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 "sync/atomic"
    14	
    15	// A Mutex is a mutual exclusion lock.
    16	// Mutexes can be created as part of other structures;
    17	// the zero value for a Mutex is an unlocked mutex.
    18	type Mutex struct {
    19		state int32
    20		sema  uint32
    21	}
    22	
    23	// A Locker represents an object that can be locked and unlocked.
    24	type Locker interface {
    25		Lock()
    26		Unlock()
    27	}
    28	
    29	const (
    30		mutexLocked = 1 << iota // mutex is locked
    31		mutexWoken
    32		mutexWaiterShift = iota
    33	)
    34	
    35	// Lock locks m.
    36	// If the lock is already in use, the calling goroutine
    37	// blocks until the mutex is available.
    38	func (m *Mutex) Lock() {
    39		// Fast path: grab unlocked mutex.
    40		if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
    41			return
    42		}
    43	
    44		awoke := false
    45		for {
    46			old := m.state
    47			new := old | mutexLocked
    48			if old&mutexLocked != 0 {
    49				new = old + 1<<mutexWaiterShift
    50			}
    51			if awoke {
    52				// The goroutine has been woken from sleep,
    53				// so we need to reset the flag in either case.
    54				new &^= mutexWoken
    55			}
    56			if atomic.CompareAndSwapInt32(&m.state, old, new) {
    57				if old&mutexLocked == 0 {
    58					break
    59				}
    60				runtime_Semacquire(&m.sema)
    61				awoke = true
    62			}
    63		}
    64	}
    65	
    66	// Unlock unlocks m.
    67	// It is a run-time error if m is not locked on entry to Unlock.
    68	//
    69	// A locked Mutex is not associated with a particular goroutine.
    70	// It is allowed for one goroutine to lock a Mutex and then
    71	// arrange for another goroutine to unlock it.
    72	func (m *Mutex) Unlock() {
    73		// Fast path: drop lock bit.
    74		new := atomic.AddInt32(&m.state, -mutexLocked)
    75		if (new+mutexLocked)&mutexLocked == 0 {
    76			panic("sync: unlock of unlocked mutex")
    77		}
    78	
    79		old := new
    80		for {
    81			// If there are no waiters or a goroutine has already
    82			// been woken or grabbed the lock, no need to wake anyone.
    83			if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
    84				return
    85			}
    86			// Grab the right to wake someone.
    87			new = (old - 1<<mutexWaiterShift) | mutexWoken
    88			if atomic.CompareAndSwapInt32(&m.state, old, new) {
    89				runtime_Semrelease(&m.sema)
    90				return
    91			}
    92			old = m.state
    93		}
    94	}