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

Golang

Source file src/pkg/sync/cond.go

     1	// Copyright 2011 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
     6	
     7	// Cond implements a condition variable, a rendezvous point
     8	// for goroutines waiting for or announcing the occurrence
     9	// of an event.
    10	//
    11	// Each Cond has an associated Locker L (often a *Mutex or *RWMutex),
    12	// which must be held when changing the condition and
    13	// when calling the Wait method.
    14	type Cond struct {
    15		L Locker // held while observing or changing the condition
    16		m Mutex  // held to avoid internal races
    17	
    18		// We must be careful to make sure that when Signal
    19		// releases a semaphore, the corresponding acquire is
    20		// executed by a goroutine that was already waiting at
    21		// the time of the call to Signal, not one that arrived later.
    22		// To ensure this, we segment waiting goroutines into
    23		// generations punctuated by calls to Signal.  Each call to
    24		// Signal begins another generation if there are no goroutines
    25		// left in older generations for it to wake.  Because of this
    26		// optimization (only begin another generation if there
    27		// are no older goroutines left), we only need to keep track
    28		// of the two most recent generations, which we call old
    29		// and new.
    30		oldWaiters int     // number of waiters in old generation...
    31		oldSema    *uint32 // ... waiting on this semaphore
    32	
    33		newWaiters int     // number of waiters in new generation...
    34		newSema    *uint32 // ... waiting on this semaphore
    35	}
    36	
    37	// NewCond returns a new Cond with Locker l.
    38	func NewCond(l Locker) *Cond {
    39		return &Cond{L: l}
    40	}
    41	
    42	// Wait atomically unlocks c.L and suspends execution
    43	// of the calling goroutine.  After later resuming execution,
    44	// Wait locks c.L before returning.  Unlike in other systems,
    45	// Wait cannot return unless awoken by Broadcast or Signal.
    46	//
    47	// Because c.L is not locked when Wait first resumes, the caller
    48	// typically cannot assume that the condition is true when
    49	// Wait returns.  Instead, the caller should Wait in a loop:
    50	//
    51	//    c.L.Lock()
    52	//    for !condition() {
    53	//        c.Wait()
    54	//    }
    55	//    ... make use of condition ...
    56	//    c.L.Unlock()
    57	//
    58	func (c *Cond) Wait() {
    59		c.m.Lock()
    60		if c.newSema == nil {
    61			c.newSema = new(uint32)
    62		}
    63		s := c.newSema
    64		c.newWaiters++
    65		c.m.Unlock()
    66		c.L.Unlock()
    67		runtime_Semacquire(s)
    68		c.L.Lock()
    69	}
    70	
    71	// Signal wakes one goroutine waiting on c, if there is any.
    72	//
    73	// It is allowed but not required for the caller to hold c.L
    74	// during the call.
    75	func (c *Cond) Signal() {
    76		c.m.Lock()
    77		if c.oldWaiters == 0 && c.newWaiters > 0 {
    78			// Retire old generation; rename new to old.
    79			c.oldWaiters = c.newWaiters
    80			c.oldSema = c.newSema
    81			c.newWaiters = 0
    82			c.newSema = nil
    83		}
    84		if c.oldWaiters > 0 {
    85			c.oldWaiters--
    86			runtime_Semrelease(c.oldSema)
    87		}
    88		c.m.Unlock()
    89	}
    90	
    91	// Broadcast wakes all goroutines waiting on c.
    92	//
    93	// It is allowed but not required for the caller to hold c.L
    94	// during the call.
    95	func (c *Cond) Broadcast() {
    96		c.m.Lock()
    97		// Wake both generations.
    98		if c.oldWaiters > 0 {
    99			for i := 0; i < c.oldWaiters; i++ {
   100				runtime_Semrelease(c.oldSema)
   101			}
   102			c.oldWaiters = 0
   103		}
   104		if c.newWaiters > 0 {
   105			for i := 0; i < c.newWaiters; i++ {
   106				runtime_Semrelease(c.newSema)
   107			}
   108			c.newWaiters = 0
   109			c.newSema = nil
   110		}
   111		c.m.Unlock()
   112	}