mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-11 14:33:41 +00:00
compiler,runtime: implement stack-based scheduler
This scheduler is intended to live along the (stackless) coroutine based scheduler which is needed for WebAssembly and unsupported platforms. The stack based scheduler is somewhat simpler in implementation as it does not require full program transform passes and supports things like function pointers and interface methods out of the box with no changes. Code size is reduced in most cases, even in the case where no scheduler scheduler is used at all. I'm not exactly sure why but these changes likely allowed some further optimizations somewhere. Even RAM is slightly reduced, perhaps some global was elminated in the process as well.
This commit is contained in:
committed by
Ron Evans
parent
61f711ef26
commit
542135c357
+48
-37
@@ -30,7 +30,7 @@ import (
|
||||
type channel struct {
|
||||
elementSize uint16 // the size of one value in this channel
|
||||
state chanState
|
||||
blocked *coroutine
|
||||
blocked *task
|
||||
}
|
||||
|
||||
type chanState uint8
|
||||
@@ -50,40 +50,44 @@ type chanSelectState struct {
|
||||
value unsafe.Pointer
|
||||
}
|
||||
|
||||
func deadlockStub()
|
||||
|
||||
// chanSend sends a single value over the channel. If this operation can
|
||||
// complete immediately (there is a goroutine waiting for a value), it sends the
|
||||
// value and re-activates both goroutines. If not, it sets itself as waiting on
|
||||
// a value.
|
||||
func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer) {
|
||||
func chanSend(sender *task, ch *channel, value unsafe.Pointer) {
|
||||
if ch == nil {
|
||||
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
||||
chanYield()
|
||||
return
|
||||
}
|
||||
switch ch.state {
|
||||
case chanStateEmpty:
|
||||
sender.promise().ptr = value
|
||||
scheduleLogChan(" send: chan is empty ", ch, sender)
|
||||
sender.state().ptr = value
|
||||
ch.state = chanStateSend
|
||||
ch.blocked = sender
|
||||
chanYield()
|
||||
case chanStateRecv:
|
||||
scheduleLogChan(" send: chan in recv mode", ch, sender)
|
||||
receiver := ch.blocked
|
||||
receiverPromise := receiver.promise()
|
||||
memcpy(receiverPromise.ptr, value, uintptr(ch.elementSize))
|
||||
receiverPromise.data = 1 // commaOk = true
|
||||
ch.blocked = receiverPromise.next
|
||||
receiverPromise.next = nil
|
||||
receiverState := receiver.state()
|
||||
memcpy(receiverState.ptr, value, uintptr(ch.elementSize))
|
||||
receiverState.data = 1 // commaOk = true
|
||||
ch.blocked = receiverState.next
|
||||
receiverState.next = nil
|
||||
activateTask(receiver)
|
||||
activateTask(sender)
|
||||
reactivateParent(sender)
|
||||
if ch.blocked == nil {
|
||||
ch.state = chanStateEmpty
|
||||
}
|
||||
case chanStateClosed:
|
||||
runtimePanic("send on closed channel")
|
||||
case chanStateSend:
|
||||
sender.promise().ptr = value
|
||||
sender.promise().next = ch.blocked
|
||||
scheduleLogChan(" send: chan in send mode", ch, sender)
|
||||
sender.state().ptr = value
|
||||
sender.state().next = ch.blocked
|
||||
ch.blocked = sender
|
||||
chanYield()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,36 +95,43 @@ func chanSend(sender *coroutine, ch *channel, value unsafe.Pointer) {
|
||||
// sender, it receives the value immediately and re-activates both coroutines.
|
||||
// If not, it sets itself as available for receiving. If the channel is closed,
|
||||
// it immediately activates itself with a zero value as the result.
|
||||
func chanRecv(receiver *coroutine, ch *channel, value unsafe.Pointer) {
|
||||
func chanRecv(receiver *task, ch *channel, value unsafe.Pointer) {
|
||||
if ch == nil {
|
||||
// A nil channel blocks forever. Do not scheduler this goroutine again.
|
||||
chanYield()
|
||||
return
|
||||
}
|
||||
switch ch.state {
|
||||
case chanStateSend:
|
||||
scheduleLogChan(" recv: chan in send mode", ch, receiver)
|
||||
sender := ch.blocked
|
||||
senderPromise := sender.promise()
|
||||
memcpy(value, senderPromise.ptr, uintptr(ch.elementSize))
|
||||
receiver.promise().data = 1 // commaOk = true
|
||||
ch.blocked = senderPromise.next
|
||||
senderPromise.next = nil
|
||||
activateTask(receiver)
|
||||
senderState := sender.state()
|
||||
memcpy(value, senderState.ptr, uintptr(ch.elementSize))
|
||||
receiver.state().data = 1 // commaOk = true
|
||||
ch.blocked = senderState.next
|
||||
senderState.next = nil
|
||||
reactivateParent(receiver)
|
||||
activateTask(sender)
|
||||
if ch.blocked == nil {
|
||||
ch.state = chanStateEmpty
|
||||
}
|
||||
case chanStateEmpty:
|
||||
receiver.promise().ptr = value
|
||||
scheduleLogChan(" recv: chan is empty ", ch, receiver)
|
||||
receiver.state().ptr = value
|
||||
ch.state = chanStateRecv
|
||||
ch.blocked = receiver
|
||||
chanYield()
|
||||
case chanStateClosed:
|
||||
scheduleLogChan(" recv: chan is closed ", ch, receiver)
|
||||
memzero(value, uintptr(ch.elementSize))
|
||||
receiver.promise().data = 0 // commaOk = false
|
||||
activateTask(receiver)
|
||||
receiver.state().data = 0 // commaOk = false
|
||||
reactivateParent(receiver)
|
||||
case chanStateRecv:
|
||||
receiver.promise().ptr = value
|
||||
receiver.promise().next = ch.blocked
|
||||
scheduleLogChan(" recv: chan in recv mode", ch, receiver)
|
||||
receiver.state().ptr = value
|
||||
receiver.state().next = ch.blocked
|
||||
ch.blocked = receiver
|
||||
chanYield()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,9 +154,9 @@ func chanClose(ch *channel) {
|
||||
runtimePanic("close channel during send")
|
||||
case chanStateRecv:
|
||||
// The receiver must be re-activated with a zero value.
|
||||
receiverPromise := ch.blocked.promise()
|
||||
memzero(receiverPromise.ptr, uintptr(ch.elementSize))
|
||||
receiverPromise.data = 0 // commaOk = false
|
||||
receiverState := ch.blocked.state()
|
||||
memzero(receiverState.ptr, uintptr(ch.elementSize))
|
||||
receiverState.data = 0 // commaOk = false
|
||||
activateTask(ch.blocked)
|
||||
ch.state = chanStateClosed
|
||||
ch.blocked = nil
|
||||
@@ -174,10 +185,10 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, blocking bool)
|
||||
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
|
||||
senderState := sender.state()
|
||||
memcpy(recvbuf, senderState.ptr, uintptr(state.ch.elementSize))
|
||||
state.ch.blocked = senderState.next
|
||||
senderState.next = nil
|
||||
activateTask(sender)
|
||||
if state.ch.blocked == nil {
|
||||
state.ch.state = chanStateEmpty
|
||||
@@ -193,11 +204,11 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, blocking bool)
|
||||
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
|
||||
receiverState := receiver.state()
|
||||
memcpy(receiverState.ptr, state.value, uintptr(state.ch.elementSize))
|
||||
receiverState.data = 1 // commaOk = true
|
||||
state.ch.blocked = receiverState.next
|
||||
receiverState.next = nil
|
||||
activateTask(receiver)
|
||||
if state.ch.blocked == nil {
|
||||
state.ch.state = chanStateEmpty
|
||||
|
||||
Reference in New Issue
Block a user