123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110 |
- // Copyright 2009 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Package sync provides basic synchronization primitives such as mutual
- // exclusion locks. Other than the Once and WaitGroup types, most are intended
- // for use by low-level library routines. Higher-level synchronization is
- // better done via channels and communication.
- //
- // Values containing the types defined in this package should not be copied.
- package sync
- import (
- "sync/atomic"
- "unsafe"
- )
- // A Mutex is a mutual exclusion lock.
- // Mutexes can be created as part of other structures;
- // the zero value for a Mutex is an unlocked mutex.
- type Mutex struct {
- state int32
- sema uint32
- }
- // A Locker represents an object that can be locked and unlocked.
- type Locker interface {
- Lock()
- Unlock()
- }
- const (
- mutexLocked = 1 << iota // mutex is locked
- mutexWoken
- mutexWaiterShift = iota
- )
- // Lock locks m.
- // If the lock is already in use, the calling goroutine
- // blocks until the mutex is available.
- func (m *Mutex) Lock() {
- // Fast path: grab unlocked mutex.
- if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
- if raceenabled {
- raceAcquire(unsafe.Pointer(m))
- }
- return
- }
- awoke := false
- for {
- old := m.state
- new := old | mutexLocked
- if old&mutexLocked != 0 {
- new = old + 1<<mutexWaiterShift
- }
- if awoke {
- // The goroutine has been woken from sleep,
- // so we need to reset the flag in either case.
- new &^= mutexWoken
- }
- if atomic.CompareAndSwapInt32(&m.state, old, new) {
- if old&mutexLocked == 0 {
- break
- }
- runtime_Semacquire(&m.sema)
- awoke = true
- }
- }
- if raceenabled {
- raceAcquire(unsafe.Pointer(m))
- }
- }
- // Unlock unlocks m.
- // It is a run-time error if m is not locked on entry to Unlock.
- //
- // A locked Mutex is not associated with a particular goroutine.
- // It is allowed for one goroutine to lock a Mutex and then
- // arrange for another goroutine to unlock it.
- func (m *Mutex) Unlock() {
- if raceenabled {
- _ = m.state
- raceRelease(unsafe.Pointer(m))
- }
- // Fast path: drop lock bit.
- new := atomic.AddInt32(&m.state, -mutexLocked)
- if (new+mutexLocked)&mutexLocked == 0 {
- panic("sync: unlock of unlocked mutex")
- }
- old := new
- for {
- // If there are no waiters or a goroutine has already
- // been woken or grabbed the lock, no need to wake anyone.
- if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
- return
- }
- // Grab the right to wake someone.
- new = (old - 1<<mutexWaiterShift) | mutexWoken
- if atomic.CompareAndSwapInt32(&m.state, old, new) {
- runtime_Semrelease(&m.sema)
- return
- }
- old = m.state
- }
- }
|