compiler,runtime: implement non-blocking selects

Blocking selects are much more complicated, so let's do non-blocking
ones first.
This commit is contained in:
Ayke van Laethem
2019-06-08 19:21:29 +02:00
committed by Ron Evans
parent 8890a0f3c8
commit b7197bcaae
5 changed files with 291 additions and 25 deletions
+72 -2
View File
@@ -29,17 +29,27 @@ import (
type channel struct {
elementSize uint16 // the size of one value in this channel
state uint8
state chanState
blocked *coroutine
}
type chanState uint8
const (
chanStateEmpty = iota
chanStateEmpty chanState = iota
chanStateRecv
chanStateSend
chanStateClosed
)
// chanSelectState is a single channel operation (send/recv) in a select
// statement. The value pointer is either nil (for receives) or points to the
// value to send (for sends).
type chanSelectState struct {
ch *channel
value unsafe.Pointer
}
func deadlockStub()
// chanSend sends a single value over the channel. If this operation can
@@ -144,3 +154,63 @@ func chanClose(ch *channel) {
ch.state = chanStateClosed
}
}
// chanSelect is the runtime implementation of the select statement. This is
// perhaps the most complicated statement in the Go spec. It returns the
// selected index and the 'comma-ok' value.
//
// TODO: do this in a round-robin fashion (as specified in the Go spec) instead
// of picking the first one that can proceed.
func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, blocking bool) (uintptr, bool) {
// See whether we can receive from one of the channels.
for i, state := range states {
if state.ch == nil {
// A nil channel blocks forever, so don't consider it here.
continue
}
if state.value == nil {
// A receive operation.
switch state.ch.state {
case chanStateSend:
// We can receive immediately.
sender := state.ch.blocked
senderPromise := sender.promise()
memcpy(recvbuf, senderPromise.ptr, uintptr(state.ch.elementSize))
state.ch.blocked = senderPromise.next
senderPromise.next = nil
activateTask(sender)
if state.ch.blocked == nil {
state.ch.state = chanStateEmpty
}
return uintptr(i), true // commaOk = true
case chanStateClosed:
// Receive the zero value.
memzero(recvbuf, uintptr(state.ch.elementSize))
return uintptr(i), false // commaOk = false
}
} else {
// A send operation: state.value is not nil.
switch state.ch.state {
case chanStateRecv:
receiver := state.ch.blocked
receiverPromise := receiver.promise()
memcpy(receiverPromise.ptr, state.value, uintptr(state.ch.elementSize))
receiverPromise.data = 1 // commaOk = true
state.ch.blocked = receiverPromise.next
receiverPromise.next = nil
activateTask(receiver)
if state.ch.blocked == nil {
state.ch.state = chanStateEmpty
}
return uintptr(i), false
case chanStateClosed:
runtimePanic("send on closed channel")
}
}
}
if !blocking {
return ^uintptr(0), false
}
panic("unimplemented: blocking select")
}