github.com/aykevl/tinygo@v0.5.0/src/runtime/chan.go (about)

     1  package runtime
     2  
     3  // This file implements the 'chan' type and send/receive/select operations.
     4  
     5  // A channel can be in one of the following states:
     6  //     empty:
     7  //       No goroutine is waiting on a send or receive operation. The 'blocked'
     8  //       member is nil.
     9  //     recv:
    10  //       A goroutine tries to receive from the channel. This goroutine is stored
    11  //       in the 'blocked' member.
    12  //     send:
    13  //       The reverse of send. A goroutine tries to send to the channel. This
    14  //       goroutine is stored in the 'blocked' member.
    15  //     closed:
    16  //       The channel is closed. Sends will panic, receives will get a zero value
    17  //       plus optionally the indication that the channel is zero (with the
    18  //       commao-ok value in the coroutine).
    19  //
    20  // A send/recv transmission is completed by copying from the data element of the
    21  // sending coroutine to the data element of the receiving coroutine, and setting
    22  // the 'comma-ok' value to true.
    23  // A receive operation on a closed channel is completed by zeroing the data
    24  // element of the receiving coroutine and setting the 'comma-ok' value to false.
    25  
    26  import (
    27  	"unsafe"
    28  )
    29  
    30  type channel struct {
    31  	state   uint8
    32  	blocked *coroutine
    33  }
    34  
    35  const (
    36  	chanStateEmpty = iota
    37  	chanStateRecv
    38  	chanStateSend
    39  	chanStateClosed
    40  )
    41  
    42  func chanSendStub(caller *coroutine, ch *channel, _ unsafe.Pointer, size uintptr)
    43  func chanRecvStub(caller *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size uintptr)
    44  func deadlockStub()
    45  
    46  // chanSend sends a single value over the channel. If this operation can
    47  // complete immediately (there is a goroutine waiting for a value), it sends the
    48  // value and re-activates both goroutines. If not, it sets itself as waiting on
    49  // a value.
    50  //
    51  // The unsafe.Pointer value is used during lowering. During IR generation, it
    52  // points to the to-be-received value. During coroutine lowering, this value is
    53  // replaced with a read from the coroutine promise.
    54  func chanSend(sender *coroutine, ch *channel, _ unsafe.Pointer, size uintptr) {
    55  	if ch == nil {
    56  		// A nil channel blocks forever. Do not scheduler this goroutine again.
    57  		return
    58  	}
    59  	switch ch.state {
    60  	case chanStateEmpty:
    61  		ch.state = chanStateSend
    62  		ch.blocked = sender
    63  	case chanStateRecv:
    64  		receiver := ch.blocked
    65  		receiverPromise := receiver.promise()
    66  		senderPromise := sender.promise()
    67  		memcpy(unsafe.Pointer(&receiverPromise.data), unsafe.Pointer(&senderPromise.data), size)
    68  		receiverPromise.commaOk = true
    69  		ch.blocked = receiverPromise.next
    70  		receiverPromise.next = nil
    71  		activateTask(receiver)
    72  		activateTask(sender)
    73  		if ch.blocked == nil {
    74  			ch.state = chanStateEmpty
    75  		}
    76  	case chanStateClosed:
    77  		runtimePanic("send on closed channel")
    78  	case chanStateSend:
    79  		sender.promise().next = ch.blocked
    80  		ch.blocked = sender
    81  	}
    82  }
    83  
    84  // chanRecv receives a single value over a channel. If there is an available
    85  // sender, it receives the value immediately and re-activates both coroutines.
    86  // If not, it sets itself as available for receiving. If the channel is closed,
    87  // it immediately activates itself with a zero value as the result.
    88  //
    89  // The two unnamed values exist to help during lowering. The unsafe.Pointer
    90  // points to the value, and the *bool points to the comma-ok value. Both are
    91  // replaced by reads from the coroutine promise.
    92  func chanRecv(receiver *coroutine, ch *channel, _ unsafe.Pointer, _ *bool, size uintptr) {
    93  	if ch == nil {
    94  		// A nil channel blocks forever. Do not scheduler this goroutine again.
    95  		return
    96  	}
    97  	switch ch.state {
    98  	case chanStateSend:
    99  		sender := ch.blocked
   100  		receiverPromise := receiver.promise()
   101  		senderPromise := sender.promise()
   102  		memcpy(unsafe.Pointer(&receiverPromise.data), unsafe.Pointer(&senderPromise.data), size)
   103  		receiverPromise.commaOk = true
   104  		ch.blocked = senderPromise.next
   105  		senderPromise.next = nil
   106  		activateTask(receiver)
   107  		activateTask(sender)
   108  		if ch.blocked == nil {
   109  			ch.state = chanStateEmpty
   110  		}
   111  	case chanStateEmpty:
   112  		ch.state = chanStateRecv
   113  		ch.blocked = receiver
   114  	case chanStateClosed:
   115  		receiverPromise := receiver.promise()
   116  		memzero(unsafe.Pointer(&receiverPromise.data), size)
   117  		receiverPromise.commaOk = false
   118  		activateTask(receiver)
   119  	case chanStateRecv:
   120  		receiver.promise().next = ch.blocked
   121  		ch.blocked = receiver
   122  	}
   123  }
   124  
   125  // chanClose closes the given channel. If this channel has a receiver or is
   126  // empty, it closes the channel. Else, it panics.
   127  func chanClose(ch *channel, size uintptr) {
   128  	if ch == nil {
   129  		// Not allowed by the language spec.
   130  		runtimePanic("close of nil channel")
   131  	}
   132  	switch ch.state {
   133  	case chanStateClosed:
   134  		// Not allowed by the language spec.
   135  		runtimePanic("close of closed channel")
   136  	case chanStateSend:
   137  		// This panic should ideally on the sending side, not in this goroutine.
   138  		// But when a goroutine tries to send while the channel is being closed,
   139  		// that is clearly invalid: the send should have been completed already
   140  		// before the close.
   141  		runtimePanic("close channel during send")
   142  	case chanStateRecv:
   143  		// The receiver must be re-activated with a zero value.
   144  		receiverPromise := ch.blocked.promise()
   145  		memzero(unsafe.Pointer(&receiverPromise.data), size)
   146  		receiverPromise.commaOk = false
   147  		activateTask(ch.blocked)
   148  		ch.state = chanStateClosed
   149  		ch.blocked = nil
   150  	case chanStateEmpty:
   151  		// Easy case. No available sender or receiver.
   152  		ch.state = chanStateClosed
   153  	}
   154  }