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