diff options
author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
---|---|---|
committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
commit | f154da9e12608589e8d5f0508f908a0c3e88a1bb (patch) | |
tree | f8255d51e10c6f1e0ed69702200b966c9556a431 /src/sync/mutex.go | |
parent | 8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff) | |
download | golang-upstream/1.4.tar.gz |
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'src/sync/mutex.go')
-rw-r--r-- | src/sync/mutex.go | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/src/sync/mutex.go b/src/sync/mutex.go new file mode 100644 index 000000000..73b337702 --- /dev/null +++ b/src/sync/mutex.go @@ -0,0 +1,109 @@ +// 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 + } +} |