github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/runtime/chan.go (about)

     1  // Copyright 2014 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 runtime
     6  
     7  // This file contains the implementation of Go channels.
     8  
     9  import "unsafe"
    10  
    11  const (
    12  	maxAlign  = 8
    13  	hchanSize = unsafe.Sizeof(hchan{}) + uintptr(-int(unsafe.Sizeof(hchan{}))&(maxAlign-1))
    14  	debugChan = false
    15  )
    16  
    17  type hchan struct {
    18  	qcount   uint           // total data in the queue
    19  	dataqsiz uint           // size of the circular queue
    20  	buf      unsafe.Pointer // points to an array of dataqsiz elements
    21  	elemsize uint16
    22  	closed   uint32
    23  	elemtype *_type // element type
    24  	sendx    uint   // send index
    25  	recvx    uint   // receive index
    26  	recvq    waitq  // list of recv waiters
    27  	sendq    waitq  // list of send waiters
    28  	lock     mutex
    29  }
    30  
    31  type waitq struct {
    32  	first *sudog
    33  	last  *sudog
    34  }
    35  
    36  //go:linkname reflect_makechan reflect.makechan
    37  func reflect_makechan(t *chantype, size int64) *hchan {
    38  	return makechan(t, size)
    39  }
    40  
    41  func makechan(t *chantype, size int64) *hchan {
    42  	elem := t.elem
    43  
    44  	// compiler checks this but be safe.
    45  	if elem.size >= 1<<16 {
    46  		throw("makechan: invalid channel element type")
    47  	}
    48  	if hchanSize%maxAlign != 0 || elem.align > maxAlign {
    49  		throw("makechan: bad alignment")
    50  	}
    51  	if size < 0 || int64(uintptr(size)) != size || (elem.size > 0 && uintptr(size) > (_MaxMem-hchanSize)/uintptr(elem.size)) {
    52  		panic("makechan: size out of range")
    53  	}
    54  
    55  	var c *hchan
    56  	if elem.kind&kindNoPointers != 0 || size == 0 {
    57  		// Allocate memory in one call.
    58  		// Hchan does not contain pointers interesting for GC in this case:
    59  		// buf points into the same allocation, elemtype is persistent.
    60  		// SudoG's are referenced from their owning thread so they can't be collected.
    61  		// TODO(dvyukov,rlh): Rethink when collector can move allocated objects.
    62  		c = (*hchan)(mallocgc(hchanSize+uintptr(size)*uintptr(elem.size), nil, flagNoScan))
    63  		if size > 0 && elem.size != 0 {
    64  			c.buf = add(unsafe.Pointer(c), hchanSize)
    65  		} else {
    66  			// race detector uses this location for synchronization
    67  			// Also prevents us from pointing beyond the allocation (see issue 9401).
    68  			c.buf = unsafe.Pointer(c)
    69  		}
    70  	} else {
    71  		c = new(hchan)
    72  		c.buf = newarray(elem, uintptr(size))
    73  	}
    74  	c.elemsize = uint16(elem.size)
    75  	c.elemtype = elem
    76  	c.dataqsiz = uint(size)
    77  
    78  	if debugChan {
    79  		print("makechan: chan=", c, "; elemsize=", elem.size, "; elemalg=", elem.alg, "; dataqsiz=", size, "\n")
    80  	}
    81  	return c
    82  }
    83  
    84  // chanbuf(c, i) is pointer to the i'th slot in the buffer.
    85  func chanbuf(c *hchan, i uint) unsafe.Pointer {
    86  	return add(c.buf, uintptr(i)*uintptr(c.elemsize))
    87  }
    88  
    89  // entry point for c <- x from compiled code
    90  //go:nosplit
    91  func chansend1(t *chantype, c *hchan, elem unsafe.Pointer) {
    92  	chansend(t, c, elem, true, getcallerpc(unsafe.Pointer(&t)))
    93  }
    94  
    95  /*
    96   * generic single channel send/recv
    97   * If block is not nil,
    98   * then the protocol will not
    99   * sleep but return if it could
   100   * not complete.
   101   *
   102   * sleep can wake up with g.param == nil
   103   * when a channel involved in the sleep has
   104   * been closed.  it is easiest to loop and re-run
   105   * the operation; we'll see that it's now closed.
   106   */
   107  func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool {
   108  	if raceenabled {
   109  		raceReadObjectPC(t.elem, ep, callerpc, funcPC(chansend))
   110  	}
   111  	if msanenabled {
   112  		msanread(ep, t.elem.size)
   113  	}
   114  
   115  	if c == nil {
   116  		if !block {
   117  			return false
   118  		}
   119  		gopark(nil, nil, "chan send (nil chan)", traceEvGoStop, 2)
   120  		throw("unreachable")
   121  	}
   122  
   123  	if debugChan {
   124  		print("chansend: chan=", c, "\n")
   125  	}
   126  
   127  	if raceenabled {
   128  		racereadpc(unsafe.Pointer(c), callerpc, funcPC(chansend))
   129  	}
   130  
   131  	// Fast path: check for failed non-blocking operation without acquiring the lock.
   132  	//
   133  	// After observing that the channel is not closed, we observe that the channel is
   134  	// not ready for sending. Each of these observations is a single word-sized read
   135  	// (first c.closed and second c.recvq.first or c.qcount depending on kind of channel).
   136  	// Because a closed channel cannot transition from 'ready for sending' to
   137  	// 'not ready for sending', even if the channel is closed between the two observations,
   138  	// they imply a moment between the two when the channel was both not yet closed
   139  	// and not ready for sending. We behave as if we observed the channel at that moment,
   140  	// and report that the send cannot proceed.
   141  	//
   142  	// It is okay if the reads are reordered here: if we observe that the channel is not
   143  	// ready for sending and then observe that it is not closed, that implies that the
   144  	// channel wasn't closed during the first observation.
   145  	if !block && c.closed == 0 && ((c.dataqsiz == 0 && c.recvq.first == nil) ||
   146  		(c.dataqsiz > 0 && c.qcount == c.dataqsiz)) {
   147  		return false
   148  	}
   149  
   150  	var t0 int64
   151  	if blockprofilerate > 0 {
   152  		t0 = cputicks()
   153  	}
   154  
   155  	lock(&c.lock)
   156  	if c.closed != 0 {
   157  		unlock(&c.lock)
   158  		panic("send on closed channel")
   159  	}
   160  
   161  	if c.dataqsiz == 0 { // synchronous channel
   162  		sg := c.recvq.dequeue()
   163  		if sg != nil { // found a waiting receiver
   164  			if raceenabled {
   165  				racesync(c, sg)
   166  			}
   167  			unlock(&c.lock)
   168  
   169  			recvg := sg.g
   170  			if sg.elem != nil {
   171  				syncsend(c, sg, ep)
   172  			}
   173  			recvg.param = unsafe.Pointer(sg)
   174  			if sg.releasetime != 0 {
   175  				sg.releasetime = cputicks()
   176  			}
   177  			goready(recvg, 3)
   178  			return true
   179  		}
   180  
   181  		if !block {
   182  			unlock(&c.lock)
   183  			return false
   184  		}
   185  
   186  		// no receiver available: block on this channel.
   187  		gp := getg()
   188  		mysg := acquireSudog()
   189  		mysg.releasetime = 0
   190  		if t0 != 0 {
   191  			mysg.releasetime = -1
   192  		}
   193  		mysg.elem = ep
   194  		mysg.waitlink = nil
   195  		gp.waiting = mysg
   196  		mysg.g = gp
   197  		mysg.selectdone = nil
   198  		gp.param = nil
   199  		c.sendq.enqueue(mysg)
   200  		goparkunlock(&c.lock, "chan send", traceEvGoBlockSend, 3)
   201  
   202  		// someone woke us up.
   203  		if mysg != gp.waiting {
   204  			throw("G waiting list is corrupted!")
   205  		}
   206  		gp.waiting = nil
   207  		if gp.param == nil {
   208  			if c.closed == 0 {
   209  				throw("chansend: spurious wakeup")
   210  			}
   211  			panic("send on closed channel")
   212  		}
   213  		gp.param = nil
   214  		if mysg.releasetime > 0 {
   215  			blockevent(int64(mysg.releasetime)-t0, 2)
   216  		}
   217  		releaseSudog(mysg)
   218  		return true
   219  	}
   220  
   221  	// asynchronous channel
   222  	// wait for some space to write our data
   223  	var t1 int64
   224  	for futile := byte(0); c.qcount >= c.dataqsiz; futile = traceFutileWakeup {
   225  		if !block {
   226  			unlock(&c.lock)
   227  			return false
   228  		}
   229  		gp := getg()
   230  		mysg := acquireSudog()
   231  		mysg.releasetime = 0
   232  		if t0 != 0 {
   233  			mysg.releasetime = -1
   234  		}
   235  		mysg.g = gp
   236  		mysg.elem = nil
   237  		mysg.selectdone = nil
   238  		c.sendq.enqueue(mysg)
   239  		goparkunlock(&c.lock, "chan send", traceEvGoBlockSend|futile, 3)
   240  
   241  		// someone woke us up - try again
   242  		if mysg.releasetime > 0 {
   243  			t1 = mysg.releasetime
   244  		}
   245  		releaseSudog(mysg)
   246  		lock(&c.lock)
   247  		if c.closed != 0 {
   248  			unlock(&c.lock)
   249  			panic("send on closed channel")
   250  		}
   251  	}
   252  
   253  	// write our data into the channel buffer
   254  	if raceenabled {
   255  		raceacquire(chanbuf(c, c.sendx))
   256  		racerelease(chanbuf(c, c.sendx))
   257  	}
   258  	typedmemmove(c.elemtype, chanbuf(c, c.sendx), ep)
   259  	c.sendx++
   260  	if c.sendx == c.dataqsiz {
   261  		c.sendx = 0
   262  	}
   263  	c.qcount++
   264  
   265  	// wake up a waiting receiver
   266  	sg := c.recvq.dequeue()
   267  	if sg != nil {
   268  		recvg := sg.g
   269  		unlock(&c.lock)
   270  		if sg.releasetime != 0 {
   271  			sg.releasetime = cputicks()
   272  		}
   273  		goready(recvg, 3)
   274  	} else {
   275  		unlock(&c.lock)
   276  	}
   277  	if t1 > 0 {
   278  		blockevent(t1-t0, 2)
   279  	}
   280  	return true
   281  }
   282  
   283  func syncsend(c *hchan, sg *sudog, elem unsafe.Pointer) {
   284  	// Send on unbuffered channel is the only operation
   285  	// in the entire runtime where one goroutine
   286  	// writes to the stack of another goroutine. The GC assumes that
   287  	// stack writes only happen when the goroutine is running and are
   288  	// only done by that goroutine. Using a write barrier is sufficient to
   289  	// make up for violating that assumption, but the write barrier has to work.
   290  	// typedmemmove will call heapBitsBulkBarrier, but the target bytes
   291  	// are not in the heap, so that will not help. We arrange to call
   292  	// memmove and typeBitsBulkBarrier instead.
   293  	memmove(sg.elem, elem, c.elemtype.size)
   294  	typeBitsBulkBarrier(c.elemtype, uintptr(sg.elem), c.elemtype.size)
   295  	sg.elem = nil
   296  }
   297  
   298  func closechan(c *hchan) {
   299  	if c == nil {
   300  		panic("close of nil channel")
   301  	}
   302  
   303  	lock(&c.lock)
   304  	if c.closed != 0 {
   305  		unlock(&c.lock)
   306  		panic("close of closed channel")
   307  	}
   308  
   309  	if raceenabled {
   310  		callerpc := getcallerpc(unsafe.Pointer(&c))
   311  		racewritepc(unsafe.Pointer(c), callerpc, funcPC(closechan))
   312  		racerelease(unsafe.Pointer(c))
   313  	}
   314  
   315  	c.closed = 1
   316  
   317  	// release all readers
   318  	for {
   319  		sg := c.recvq.dequeue()
   320  		if sg == nil {
   321  			break
   322  		}
   323  		gp := sg.g
   324  		sg.elem = nil
   325  		gp.param = nil
   326  		if sg.releasetime != 0 {
   327  			sg.releasetime = cputicks()
   328  		}
   329  		goready(gp, 3)
   330  	}
   331  
   332  	// release all writers
   333  	for {
   334  		sg := c.sendq.dequeue()
   335  		if sg == nil {
   336  			break
   337  		}
   338  		gp := sg.g
   339  		sg.elem = nil
   340  		gp.param = nil
   341  		if sg.releasetime != 0 {
   342  			sg.releasetime = cputicks()
   343  		}
   344  		goready(gp, 3)
   345  	}
   346  	unlock(&c.lock)
   347  }
   348  
   349  // entry points for <- c from compiled code
   350  //go:nosplit
   351  func chanrecv1(t *chantype, c *hchan, elem unsafe.Pointer) {
   352  	chanrecv(t, c, elem, true)
   353  }
   354  
   355  //go:nosplit
   356  func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) {
   357  	_, received = chanrecv(t, c, elem, true)
   358  	return
   359  }
   360  
   361  // chanrecv receives on channel c and writes the received data to ep.
   362  // ep may be nil, in which case received data is ignored.
   363  // If block == false and no elements are available, returns (false, false).
   364  // Otherwise, if c is closed, zeros *ep and returns (true, false).
   365  // Otherwise, fills in *ep with an element and returns (true, true).
   366  func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
   367  	// raceenabled: don't need to check ep, as it is always on the stack.
   368  
   369  	if debugChan {
   370  		print("chanrecv: chan=", c, "\n")
   371  	}
   372  
   373  	if c == nil {
   374  		if !block {
   375  			return
   376  		}
   377  		gopark(nil, nil, "chan receive (nil chan)", traceEvGoStop, 2)
   378  		throw("unreachable")
   379  	}
   380  
   381  	// Fast path: check for failed non-blocking operation without acquiring the lock.
   382  	//
   383  	// After observing that the channel is not ready for receiving, we observe that the
   384  	// channel is not closed. Each of these observations is a single word-sized read
   385  	// (first c.sendq.first or c.qcount, and second c.closed).
   386  	// Because a channel cannot be reopened, the later observation of the channel
   387  	// being not closed implies that it was also not closed at the moment of the
   388  	// first observation. We behave as if we observed the channel at that moment
   389  	// and report that the receive cannot proceed.
   390  	//
   391  	// The order of operations is important here: reversing the operations can lead to
   392  	// incorrect behavior when racing with a close.
   393  	if !block && (c.dataqsiz == 0 && c.sendq.first == nil ||
   394  		c.dataqsiz > 0 && atomicloaduint(&c.qcount) == 0) &&
   395  		atomicload(&c.closed) == 0 {
   396  		return
   397  	}
   398  
   399  	var t0 int64
   400  	if blockprofilerate > 0 {
   401  		t0 = cputicks()
   402  	}
   403  
   404  	lock(&c.lock)
   405  	if c.dataqsiz == 0 { // synchronous channel
   406  		if c.closed != 0 {
   407  			return recvclosed(c, ep)
   408  		}
   409  
   410  		sg := c.sendq.dequeue()
   411  		if sg != nil {
   412  			if raceenabled {
   413  				racesync(c, sg)
   414  			}
   415  			unlock(&c.lock)
   416  
   417  			if ep != nil {
   418  				typedmemmove(c.elemtype, ep, sg.elem)
   419  			}
   420  			sg.elem = nil
   421  			gp := sg.g
   422  			gp.param = unsafe.Pointer(sg)
   423  			if sg.releasetime != 0 {
   424  				sg.releasetime = cputicks()
   425  			}
   426  			goready(gp, 3)
   427  			selected = true
   428  			received = true
   429  			return
   430  		}
   431  
   432  		if !block {
   433  			unlock(&c.lock)
   434  			return
   435  		}
   436  
   437  		// no sender available: block on this channel.
   438  		gp := getg()
   439  		mysg := acquireSudog()
   440  		mysg.releasetime = 0
   441  		if t0 != 0 {
   442  			mysg.releasetime = -1
   443  		}
   444  		mysg.elem = ep
   445  		mysg.waitlink = nil
   446  		gp.waiting = mysg
   447  		mysg.g = gp
   448  		mysg.selectdone = nil
   449  		gp.param = nil
   450  		c.recvq.enqueue(mysg)
   451  		goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3)
   452  
   453  		// someone woke us up
   454  		if mysg != gp.waiting {
   455  			throw("G waiting list is corrupted!")
   456  		}
   457  		gp.waiting = nil
   458  		if mysg.releasetime > 0 {
   459  			blockevent(mysg.releasetime-t0, 2)
   460  		}
   461  		haveData := gp.param != nil
   462  		gp.param = nil
   463  		releaseSudog(mysg)
   464  
   465  		if haveData {
   466  			// a sender sent us some data. It already wrote to ep.
   467  			selected = true
   468  			received = true
   469  			return
   470  		}
   471  
   472  		lock(&c.lock)
   473  		if c.closed == 0 {
   474  			throw("chanrecv: spurious wakeup")
   475  		}
   476  		return recvclosed(c, ep)
   477  	}
   478  
   479  	// asynchronous channel
   480  	// wait for some data to appear
   481  	var t1 int64
   482  	for futile := byte(0); c.qcount <= 0; futile = traceFutileWakeup {
   483  		if c.closed != 0 {
   484  			selected, received = recvclosed(c, ep)
   485  			if t1 > 0 {
   486  				blockevent(t1-t0, 2)
   487  			}
   488  			return
   489  		}
   490  
   491  		if !block {
   492  			unlock(&c.lock)
   493  			return
   494  		}
   495  
   496  		// wait for someone to send an element
   497  		gp := getg()
   498  		mysg := acquireSudog()
   499  		mysg.releasetime = 0
   500  		if t0 != 0 {
   501  			mysg.releasetime = -1
   502  		}
   503  		mysg.elem = nil
   504  		mysg.g = gp
   505  		mysg.selectdone = nil
   506  
   507  		c.recvq.enqueue(mysg)
   508  		goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv|futile, 3)
   509  
   510  		// someone woke us up - try again
   511  		if mysg.releasetime > 0 {
   512  			t1 = mysg.releasetime
   513  		}
   514  		releaseSudog(mysg)
   515  		lock(&c.lock)
   516  	}
   517  
   518  	if raceenabled {
   519  		raceacquire(chanbuf(c, c.recvx))
   520  		racerelease(chanbuf(c, c.recvx))
   521  	}
   522  	if ep != nil {
   523  		typedmemmove(c.elemtype, ep, chanbuf(c, c.recvx))
   524  	}
   525  	memclr(chanbuf(c, c.recvx), uintptr(c.elemsize))
   526  
   527  	c.recvx++
   528  	if c.recvx == c.dataqsiz {
   529  		c.recvx = 0
   530  	}
   531  	c.qcount--
   532  
   533  	// ping a sender now that there is space
   534  	sg := c.sendq.dequeue()
   535  	if sg != nil {
   536  		gp := sg.g
   537  		unlock(&c.lock)
   538  		if sg.releasetime != 0 {
   539  			sg.releasetime = cputicks()
   540  		}
   541  		goready(gp, 3)
   542  	} else {
   543  		unlock(&c.lock)
   544  	}
   545  
   546  	if t1 > 0 {
   547  		blockevent(t1-t0, 2)
   548  	}
   549  	selected = true
   550  	received = true
   551  	return
   552  }
   553  
   554  // recvclosed is a helper function for chanrecv.  Handles cleanup
   555  // when the receiver encounters a closed channel.
   556  // Caller must hold c.lock, recvclosed will release the lock.
   557  func recvclosed(c *hchan, ep unsafe.Pointer) (selected, recevied bool) {
   558  	if raceenabled {
   559  		raceacquire(unsafe.Pointer(c))
   560  	}
   561  	unlock(&c.lock)
   562  	if ep != nil {
   563  		memclr(ep, uintptr(c.elemsize))
   564  	}
   565  	return true, false
   566  }
   567  
   568  // compiler implements
   569  //
   570  //	select {
   571  //	case c <- v:
   572  //		... foo
   573  //	default:
   574  //		... bar
   575  //	}
   576  //
   577  // as
   578  //
   579  //	if selectnbsend(c, v) {
   580  //		... foo
   581  //	} else {
   582  //		... bar
   583  //	}
   584  //
   585  func selectnbsend(t *chantype, c *hchan, elem unsafe.Pointer) (selected bool) {
   586  	return chansend(t, c, elem, false, getcallerpc(unsafe.Pointer(&t)))
   587  }
   588  
   589  // compiler implements
   590  //
   591  //	select {
   592  //	case v = <-c:
   593  //		... foo
   594  //	default:
   595  //		... bar
   596  //	}
   597  //
   598  // as
   599  //
   600  //	if selectnbrecv(&v, c) {
   601  //		... foo
   602  //	} else {
   603  //		... bar
   604  //	}
   605  //
   606  func selectnbrecv(t *chantype, elem unsafe.Pointer, c *hchan) (selected bool) {
   607  	selected, _ = chanrecv(t, c, elem, false)
   608  	return
   609  }
   610  
   611  // compiler implements
   612  //
   613  //	select {
   614  //	case v, ok = <-c:
   615  //		... foo
   616  //	default:
   617  //		... bar
   618  //	}
   619  //
   620  // as
   621  //
   622  //	if c != nil && selectnbrecv2(&v, &ok, c) {
   623  //		... foo
   624  //	} else {
   625  //		... bar
   626  //	}
   627  //
   628  func selectnbrecv2(t *chantype, elem unsafe.Pointer, received *bool, c *hchan) (selected bool) {
   629  	// TODO(khr): just return 2 values from this function, now that it is in Go.
   630  	selected, *received = chanrecv(t, c, elem, false)
   631  	return
   632  }
   633  
   634  //go:linkname reflect_chansend reflect.chansend
   635  func reflect_chansend(t *chantype, c *hchan, elem unsafe.Pointer, nb bool) (selected bool) {
   636  	return chansend(t, c, elem, !nb, getcallerpc(unsafe.Pointer(&t)))
   637  }
   638  
   639  //go:linkname reflect_chanrecv reflect.chanrecv
   640  func reflect_chanrecv(t *chantype, c *hchan, nb bool, elem unsafe.Pointer) (selected bool, received bool) {
   641  	return chanrecv(t, c, elem, !nb)
   642  }
   643  
   644  //go:linkname reflect_chanlen reflect.chanlen
   645  func reflect_chanlen(c *hchan) int {
   646  	if c == nil {
   647  		return 0
   648  	}
   649  	return int(c.qcount)
   650  }
   651  
   652  //go:linkname reflect_chancap reflect.chancap
   653  func reflect_chancap(c *hchan) int {
   654  	if c == nil {
   655  		return 0
   656  	}
   657  	return int(c.dataqsiz)
   658  }
   659  
   660  //go:linkname reflect_chanclose reflect.chanclose
   661  func reflect_chanclose(c *hchan) {
   662  	closechan(c)
   663  }
   664  
   665  func (q *waitq) enqueue(sgp *sudog) {
   666  	sgp.next = nil
   667  	x := q.last
   668  	if x == nil {
   669  		sgp.prev = nil
   670  		q.first = sgp
   671  		q.last = sgp
   672  		return
   673  	}
   674  	sgp.prev = x
   675  	x.next = sgp
   676  	q.last = sgp
   677  }
   678  
   679  func (q *waitq) dequeue() *sudog {
   680  	for {
   681  		sgp := q.first
   682  		if sgp == nil {
   683  			return nil
   684  		}
   685  		y := sgp.next
   686  		if y == nil {
   687  			q.first = nil
   688  			q.last = nil
   689  		} else {
   690  			y.prev = nil
   691  			q.first = y
   692  			sgp.next = nil // mark as removed (see dequeueSudog)
   693  		}
   694  
   695  		// if sgp participates in a select and is already signaled, ignore it
   696  		if sgp.selectdone != nil {
   697  			// claim the right to signal
   698  			if *sgp.selectdone != 0 || !cas(sgp.selectdone, 0, 1) {
   699  				continue
   700  			}
   701  		}
   702  
   703  		return sgp
   704  	}
   705  }
   706  
   707  func racesync(c *hchan, sg *sudog) {
   708  	racerelease(chanbuf(c, 0))
   709  	raceacquireg(sg.g, chanbuf(c, 0))
   710  	racereleaseg(sg.g, chanbuf(c, 0))
   711  	raceacquire(chanbuf(c, 0))
   712  }