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 }