github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/src/runtime/sema.go (about)

     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  // Semaphore implementation exposed to Go.
     6  // Intended use is provide a sleep and wakeup
     7  // primitive that can be used in the contended case
     8  // of other synchronization primitives.
     9  // Thus it targets the same goal as Linux's futex,
    10  // but it has much simpler semantics.
    11  //
    12  // That is, don't think of these as semaphores.
    13  // Think of them as a way to implement sleep and wakeup
    14  // such that every sleep is paired with a single wakeup,
    15  // even if, due to races, the wakeup happens before the sleep.
    16  //
    17  // See Mullender and Cox, ``Semaphores in Plan 9,''
    18  // http://swtch.com/semaphore.pdf
    19  
    20  package runtime
    21  
    22  import "unsafe"
    23  
    24  // Asynchronous semaphore for sync.Mutex.
    25  
    26  type semaRoot struct {
    27  	lock  mutex
    28  	head  *sudog
    29  	tail  *sudog
    30  	nwait uint32 // Number of waiters. Read w/o the lock.
    31  }
    32  
    33  // Prime to not correlate with any user patterns.
    34  const semTabSize = 251
    35  
    36  var semtable [semTabSize]struct {
    37  	root semaRoot
    38  	pad  [_CacheLineSize - unsafe.Sizeof(semaRoot{})]byte
    39  }
    40  
    41  // Called from sync/net packages.
    42  func asyncsemacquire(addr *uint32) {
    43  	semacquire(addr, true)
    44  }
    45  
    46  func asyncsemrelease(addr *uint32) {
    47  	semrelease(addr)
    48  }
    49  
    50  // Called from runtime.
    51  func semacquire(addr *uint32, profile bool) {
    52  	gp := getg()
    53  	if gp != gp.m.curg {
    54  		gothrow("semacquire not on the G stack")
    55  	}
    56  
    57  	// Easy case.
    58  	if cansemacquire(addr) {
    59  		return
    60  	}
    61  
    62  	// Harder case:
    63  	//	increment waiter count
    64  	//	try cansemacquire one more time, return if succeeded
    65  	//	enqueue itself as a waiter
    66  	//	sleep
    67  	//	(waiter descriptor is dequeued by signaler)
    68  	s := acquireSudog()
    69  	root := semroot(addr)
    70  	t0 := int64(0)
    71  	s.releasetime = 0
    72  	if profile && blockprofilerate > 0 {
    73  		t0 = cputicks()
    74  		s.releasetime = -1
    75  	}
    76  	for {
    77  		lock(&root.lock)
    78  		// Add ourselves to nwait to disable "easy case" in semrelease.
    79  		xadd(&root.nwait, 1)
    80  		// Check cansemacquire to avoid missed wakeup.
    81  		if cansemacquire(addr) {
    82  			xadd(&root.nwait, -1)
    83  			unlock(&root.lock)
    84  			break
    85  		}
    86  		// Any semrelease after the cansemacquire knows we're waiting
    87  		// (we set nwait above), so go to sleep.
    88  		root.queue(addr, s)
    89  		goparkunlock(&root.lock, "semacquire")
    90  		if cansemacquire(addr) {
    91  			break
    92  		}
    93  	}
    94  	if s.releasetime > 0 {
    95  		blockevent(int64(s.releasetime)-t0, 3)
    96  	}
    97  	releaseSudog(s)
    98  }
    99  
   100  func semrelease(addr *uint32) {
   101  	root := semroot(addr)
   102  	xadd(addr, 1)
   103  
   104  	// Easy case: no waiters?
   105  	// This check must happen after the xadd, to avoid a missed wakeup
   106  	// (see loop in semacquire).
   107  	if atomicload(&root.nwait) == 0 {
   108  		return
   109  	}
   110  
   111  	// Harder case: search for a waiter and wake it.
   112  	lock(&root.lock)
   113  	if atomicload(&root.nwait) == 0 {
   114  		// The count is already consumed by another goroutine,
   115  		// so no need to wake up another goroutine.
   116  		unlock(&root.lock)
   117  		return
   118  	}
   119  	s := root.head
   120  	for ; s != nil; s = s.next {
   121  		if s.elem == unsafe.Pointer(addr) {
   122  			xadd(&root.nwait, -1)
   123  			root.dequeue(s)
   124  			break
   125  		}
   126  	}
   127  	unlock(&root.lock)
   128  	if s != nil {
   129  		if s.releasetime != 0 {
   130  			s.releasetime = cputicks()
   131  		}
   132  		goready(s.g)
   133  	}
   134  }
   135  
   136  func semroot(addr *uint32) *semaRoot {
   137  	return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
   138  }
   139  
   140  func cansemacquire(addr *uint32) bool {
   141  	for {
   142  		v := atomicload(addr)
   143  		if v == 0 {
   144  			return false
   145  		}
   146  		if cas(addr, v, v-1) {
   147  			return true
   148  		}
   149  	}
   150  }
   151  
   152  func (root *semaRoot) queue(addr *uint32, s *sudog) {
   153  	s.g = getg()
   154  	s.elem = unsafe.Pointer(addr)
   155  	s.next = nil
   156  	s.prev = root.tail
   157  	if root.tail != nil {
   158  		root.tail.next = s
   159  	} else {
   160  		root.head = s
   161  	}
   162  	root.tail = s
   163  }
   164  
   165  func (root *semaRoot) dequeue(s *sudog) {
   166  	if s.next != nil {
   167  		s.next.prev = s.prev
   168  	} else {
   169  		root.tail = s.prev
   170  	}
   171  	if s.prev != nil {
   172  		s.prev.next = s.next
   173  	} else {
   174  		root.head = s.next
   175  	}
   176  	s.elem = nil
   177  	s.next = nil
   178  	s.prev = nil
   179  }
   180  
   181  // Synchronous semaphore for sync.Cond.
   182  type syncSema struct {
   183  	lock mutex
   184  	head *sudog
   185  	tail *sudog
   186  }
   187  
   188  // Syncsemacquire waits for a pairing syncsemrelease on the same semaphore s.
   189  func syncsemacquire(s *syncSema) {
   190  	lock(&s.lock)
   191  	if s.head != nil && s.head.nrelease > 0 {
   192  		// Have pending release, consume it.
   193  		var wake *sudog
   194  		s.head.nrelease--
   195  		if s.head.nrelease == 0 {
   196  			wake = s.head
   197  			s.head = wake.next
   198  			if s.head == nil {
   199  				s.tail = nil
   200  			}
   201  		}
   202  		unlock(&s.lock)
   203  		if wake != nil {
   204  			wake.next = nil
   205  			goready(wake.g)
   206  		}
   207  	} else {
   208  		// Enqueue itself.
   209  		w := acquireSudog()
   210  		w.g = getg()
   211  		w.nrelease = -1
   212  		w.next = nil
   213  		w.releasetime = 0
   214  		t0 := int64(0)
   215  		if blockprofilerate > 0 {
   216  			t0 = cputicks()
   217  			w.releasetime = -1
   218  		}
   219  		if s.tail == nil {
   220  			s.head = w
   221  		} else {
   222  			s.tail.next = w
   223  		}
   224  		s.tail = w
   225  		goparkunlock(&s.lock, "semacquire")
   226  		if t0 != 0 {
   227  			blockevent(int64(w.releasetime)-t0, 2)
   228  		}
   229  		releaseSudog(w)
   230  	}
   231  }
   232  
   233  // Syncsemrelease waits for n pairing syncsemacquire on the same semaphore s.
   234  func syncsemrelease(s *syncSema, n uint32) {
   235  	lock(&s.lock)
   236  	for n > 0 && s.head != nil && s.head.nrelease < 0 {
   237  		// Have pending acquire, satisfy it.
   238  		wake := s.head
   239  		s.head = wake.next
   240  		if s.head == nil {
   241  			s.tail = nil
   242  		}
   243  		if wake.releasetime != 0 {
   244  			wake.releasetime = cputicks()
   245  		}
   246  		wake.next = nil
   247  		goready(wake.g)
   248  		n--
   249  	}
   250  	if n > 0 {
   251  		// enqueue itself
   252  		w := acquireSudog()
   253  		w.g = getg()
   254  		w.nrelease = int32(n)
   255  		w.next = nil
   256  		w.releasetime = 0
   257  		if s.tail == nil {
   258  			s.head = w
   259  		} else {
   260  			s.tail.next = w
   261  		}
   262  		s.tail = w
   263  		goparkunlock(&s.lock, "semarelease")
   264  		releaseSudog(w)
   265  	} else {
   266  		unlock(&s.lock)
   267  	}
   268  }
   269  
   270  func syncsemcheck(sz uintptr) {
   271  	if sz != unsafe.Sizeof(syncSema{}) {
   272  		print("runtime: bad syncSema size - sync=", sz, " runtime=", unsafe.Sizeof(syncSema{}), "\n")
   273  		gothrow("bad syncSema size")
   274  	}
   275  }