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