// 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 xadd(&m.key, -1) == 0 { // changed from 1 to 0; no contention return } runtime.Semrelease(&m.sema) }
func HammerSemaphore(s *uint32, loops int, cdone chan bool) { for i := 0; i < loops; i++ { runtime.Semacquire(s) runtime.Semrelease(s) } cdone <- true }
func BenchmarkSemaUncontended(b *testing.B) { type PaddedSem struct { sem uint32 pad [32]uint32 } const CallsPerSched = 1000 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, procs) for p := 0; p < procs; p++ { go func() { sem := new(PaddedSem) for atomic.AddInt32(&N, -1) >= 0 { runtime.Gosched() for g := 0; g < CallsPerSched; g++ { runtime.Semrelease(&sem.sem) runtime.Semacquire(&sem.sem) } } c <- true }() } for p := 0; p < procs; p++ { <-c } }
// Signal wakes one goroutine waiting on c, if there is any. // // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Signal() { c.m.Lock() if c.waiters > 0 { c.waiters-- runtime.Semrelease(c.sema) } c.m.Unlock() }
func benchmarkSema(b *testing.B, block, work bool) { const CallsPerSched = 1000 const LocalWork = 100 procs := runtime.GOMAXPROCS(-1) N := int32(b.N / CallsPerSched) c := make(chan bool, procs) c2 := make(chan bool, procs/2) sem := uint32(0) if block { for p := 0; p < procs/2; p++ { go func() { runtime.Semacquire(&sem) c2 <- true }() } } for p := 0; p < procs; p++ { go func() { foo := 0 for atomic.AddInt32(&N, -1) >= 0 { runtime.Gosched() for g := 0; g < CallsPerSched; g++ { runtime.Semrelease(&sem) if work { for i := 0; i < LocalWork; i++ { foo *= 2 foo /= 2 } } runtime.Semacquire(&sem) } } c <- foo == 42 runtime.Semrelease(&sem) }() } if block { for p := 0; p < procs/2; p++ { <-c2 } } for p := 0; p < procs; p++ { <-c } }
// Broadcast wakes all goroutines waiting on c. // // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Broadcast() { c.m.Lock() // Wake both generations. if c.oldWaiters > 0 { for i := 0; i < c.oldWaiters; i++ { runtime.Semrelease(c.oldSema) } c.oldWaiters = 0 } if c.newWaiters > 0 { for i := 0; i < c.newWaiters; i++ { runtime.Semrelease(c.newSema) } c.newWaiters = 0 c.newSema = nil } c.m.Unlock() }
// RUnlock undoes a single RLock call; // it does not affect other simultaneous readers. // It is a run-time error if rw is not locked for reading // on entry to RUnlock. func (rw *RWMutex) RUnlock() { if atomic.AddInt32(&rw.readerCount, -1) < 0 { // A writer is pending. if atomic.AddInt32(&rw.readerWait, -1) == 0 { // The last reader unblocks the writer. runtime.Semrelease(&rw.writerSem) } } }
// Unlock unlocks rw for writing. It is a run-time error if rw is // not locked for writing on entry to Unlock. // // As with Mutexes, a locked RWMutex is not associated with a particular // goroutine. One goroutine may RLock (Lock) an RWMutex and then // arrange for another goroutine to RUnlock (Unlock) it. func (rw *RWMutex) Unlock() { // Announce to readers there is no active writer. r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders) // Unblock blocked readers, if any. for i := 0; i < int(r); i++ { runtime.Semrelease(&rw.readerSem) } // Allow other writers to proceed. rw.w.Unlock() }
// 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() { switch v := atomic.AddInt32(&m.key, -1); { case v == 0: // changed from 1 to 0; no contention return case v == -1: // changed from 0 to -1: wasn't locked // (or there are 4 billion goroutines waiting) panic("sync: unlock of unlocked mutex") } runtime.Semrelease(&m.sema) }
// Signal wakes one goroutine waiting on c, if there is any. // // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Signal() { c.m.Lock() if c.oldWaiters == 0 && c.newWaiters > 0 { // Retire old generation; rename new to old. c.oldWaiters = c.newWaiters c.oldSema = c.newSema c.newWaiters = 0 c.newSema = nil } if c.oldWaiters > 0 { c.oldWaiters-- runtime.Semrelease(c.oldSema) } c.m.Unlock() }
// Add adds delta, which may be negative, to the WaitGroup counter. // If the counter becomes zero, all goroutines blocked on Wait() are released. func (wg *WaitGroup) Add(delta int) { wg.m.Lock() if delta < -wg.counter { wg.m.Unlock() panic("sync: negative WaitGroup count") } wg.counter += delta if wg.counter == 0 && wg.waiters > 0 { for i := 0; i < wg.waiters; i++ { runtime.Semrelease(wg.sema) } wg.waiters = 0 wg.sema = nil } wg.m.Unlock() }
// Add adds delta, which may be negative, to the WaitGroup counter. // If the counter becomes zero, all goroutines blocked on Wait() are released. func (wg *WaitGroup) Add(delta int) { v := atomic.AddInt32(&wg.counter, int32(delta)) if v < 0 { panic("sync: negative WaitGroup count") } if v > 0 || atomic.LoadInt32(&wg.waiters) == 0 { return } wg.m.Lock() for i := int32(0); i < wg.waiters; i++ { runtime.Semrelease(wg.sema) } wg.waiters = 0 wg.sema = nil wg.m.Unlock() }
// Broadcast wakes all goroutines waiting on c. // // It is allowed but not required for the caller to hold c.L // during the call. func (c *Cond) Broadcast() { c.m.Lock() if c.waiters > 0 { s := c.sema n := c.waiters for i := 0; i < n; i++ { runtime.Semrelease(s) } // We just issued n wakeups via the semaphore s. // To ensure that they wake up the existing waiters // and not waiters that arrive after Broadcast returns, // clear c.sema. The next operation will allocate // a new one. c.sema = nil c.waiters = 0 } c.m.Unlock() }
// 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() { // 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 } }
func (this *Semaphore) V() { runtime.Semrelease((*uint32)(this)) }
func (p *Parker) Unpark() { runtime.Semrelease(&p.sema) }