mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-18 19:44:00 +00:00
runtime: wake channel waiters after releasing locks (#5513)
* runtime: wake channel waiters after releasing locks * runtime: add comment explaining next field reuse in chanClose
This commit is contained in:
+57
-25
@@ -196,9 +196,9 @@ func (ch *channel) bufferPop(value unsafe.Pointer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to proceed with this send operation without blocking, and return whether
|
// Try to proceed with this send operation without blocking, and return whether
|
||||||
// the send succeeded. Interrupts must be disabled and the lock must be held
|
// the send succeeded. Schedule a returned task only after releasing all locks.
|
||||||
// when calling this function.
|
// Interrupts must be disabled and the channel lock must be held.
|
||||||
func (ch *channel) trySend(value unsafe.Pointer) bool {
|
func (ch *channel) trySend(value unsafe.Pointer) (sent bool, wake *task.Task) {
|
||||||
// To make sure we send values in the correct order, we can only send
|
// To make sure we send values in the correct order, we can only send
|
||||||
// directly to a receiver when there are no values in the buffer.
|
// directly to a receiver when there are no values in the buffer.
|
||||||
|
|
||||||
@@ -215,8 +215,7 @@ func (ch *channel) trySend(value unsafe.Pointer) bool {
|
|||||||
if ch.bufLen == 0 {
|
if ch.bufLen == 0 {
|
||||||
if receiver := ch.receivers.pop(chanOperationOk); receiver != nil {
|
if receiver := ch.receivers.pop(chanOperationOk); receiver != nil {
|
||||||
memcpy(receiver.task.Ptr, value, ch.elementSize)
|
memcpy(receiver.task.Ptr, value, ch.elementSize)
|
||||||
scheduleTask(receiver.task)
|
return true, receiver.task
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,9 +223,9 @@ func (ch *channel) trySend(value unsafe.Pointer) bool {
|
|||||||
// store the value in the buffer and continue.
|
// store the value in the buffer and continue.
|
||||||
if ch.bufLen < ch.bufCap {
|
if ch.bufLen < ch.bufCap {
|
||||||
ch.bufferPush(value)
|
ch.bufferPush(value)
|
||||||
return true
|
return true, nil
|
||||||
}
|
}
|
||||||
return false
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func chanSend(ch *channel, value unsafe.Pointer, op *channelOp) {
|
func chanSend(ch *channel, value unsafe.Pointer, op *channelOp) {
|
||||||
@@ -239,8 +238,11 @@ func chanSend(ch *channel, value unsafe.Pointer, op *channelOp) {
|
|||||||
ch.lock.Lock()
|
ch.lock.Lock()
|
||||||
|
|
||||||
// See whether we can proceed immediately, and if so, return early.
|
// See whether we can proceed immediately, and if so, return early.
|
||||||
if ch.trySend(value) {
|
if sent, wake := ch.trySend(value); sent {
|
||||||
ch.lock.Unlock()
|
ch.lock.Unlock()
|
||||||
|
if wake != nil {
|
||||||
|
scheduleTask(wake)
|
||||||
|
}
|
||||||
interrupt.Restore(mask)
|
interrupt.Restore(mask)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -269,9 +271,9 @@ func chanSend(ch *channel, value unsafe.Pointer, op *channelOp) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Try to proceed with this receive operation without blocking, and return
|
// Try to proceed with this receive operation without blocking, and return
|
||||||
// whether the receive operation succeeded. Interrupts must be disabled and the
|
// whether it succeeded. Schedule a returned task only after releasing all locks.
|
||||||
// lock must be held when calling this function.
|
// Interrupts must be disabled and the channel lock must be held.
|
||||||
func (ch *channel) tryRecv(value unsafe.Pointer) (received, ok bool) {
|
func (ch *channel) tryRecv(value unsafe.Pointer) (received, ok bool, wake *task.Task) {
|
||||||
// To make sure we keep the values in the channel in the correct order, we
|
// To make sure we keep the values in the channel in the correct order, we
|
||||||
// first have to read values from the buffer before we can look at the
|
// first have to read values from the buffer before we can look at the
|
||||||
// senders.
|
// senders.
|
||||||
@@ -284,27 +286,26 @@ func (ch *channel) tryRecv(value unsafe.Pointer) (received, ok bool) {
|
|||||||
// Check for the next sender available and push it to the buffer.
|
// Check for the next sender available and push it to the buffer.
|
||||||
if sender := ch.senders.pop(chanOperationOk); sender != nil {
|
if sender := ch.senders.pop(chanOperationOk); sender != nil {
|
||||||
ch.bufferPush(sender.value)
|
ch.bufferPush(sender.value)
|
||||||
scheduleTask(sender.task)
|
return true, true, sender.task
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, true
|
return true, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if ch.closed {
|
if ch.closed {
|
||||||
// Channel is closed, so proceed immediately.
|
// Channel is closed, so proceed immediately.
|
||||||
memzero(value, ch.elementSize)
|
memzero(value, ch.elementSize)
|
||||||
return true, false
|
return true, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a sender, we can proceed with the channel operation
|
// If there is a sender, we can proceed with the channel operation
|
||||||
// immediately.
|
// immediately.
|
||||||
if sender := ch.senders.pop(chanOperationOk); sender != nil {
|
if sender := ch.senders.pop(chanOperationOk); sender != nil {
|
||||||
memcpy(value, sender.value, ch.elementSize)
|
memcpy(value, sender.value, ch.elementSize)
|
||||||
scheduleTask(sender.task)
|
return true, true, sender.task
|
||||||
return true, true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, false
|
return false, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func chanRecv(ch *channel, value unsafe.Pointer, op *channelOp) bool {
|
func chanRecv(ch *channel, value unsafe.Pointer, op *channelOp) bool {
|
||||||
@@ -316,8 +317,11 @@ func chanRecv(ch *channel, value unsafe.Pointer, op *channelOp) bool {
|
|||||||
mask := interrupt.Disable()
|
mask := interrupt.Disable()
|
||||||
ch.lock.Lock()
|
ch.lock.Lock()
|
||||||
|
|
||||||
if received, ok := ch.tryRecv(value); received {
|
if received, ok, wake := ch.tryRecv(value); received {
|
||||||
ch.lock.Unlock()
|
ch.lock.Unlock()
|
||||||
|
if wake != nil {
|
||||||
|
scheduleTask(wake)
|
||||||
|
}
|
||||||
interrupt.Restore(mask)
|
interrupt.Restore(mask)
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
@@ -358,6 +362,11 @@ func chanClose(ch *channel) {
|
|||||||
runtimePanic("close of closed channel")
|
runtimePanic("close of closed channel")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Collect waiters and wake them after unlocking.
|
||||||
|
// pop() removes each op from the channel queue, so next can be reused
|
||||||
|
// to link the temporary wake list.
|
||||||
|
var wakeHead, wakeTail *channelOp
|
||||||
|
|
||||||
// Proceed all receiving operations that are blocked.
|
// Proceed all receiving operations that are blocked.
|
||||||
for {
|
for {
|
||||||
receiver := ch.receivers.pop(chanOperationClosed)
|
receiver := ch.receivers.pop(chanOperationClosed)
|
||||||
@@ -369,8 +378,13 @@ func chanClose(ch *channel) {
|
|||||||
// Zero the value that the receiver is getting.
|
// Zero the value that the receiver is getting.
|
||||||
memzero(receiver.task.Ptr, ch.elementSize)
|
memzero(receiver.task.Ptr, ch.elementSize)
|
||||||
|
|
||||||
// Wake up the receiving goroutine.
|
receiver.next = nil
|
||||||
scheduleTask(receiver.task)
|
if wakeTail == nil {
|
||||||
|
wakeHead = receiver
|
||||||
|
} else {
|
||||||
|
wakeTail.next = receiver
|
||||||
|
}
|
||||||
|
wakeTail = receiver
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let all senders panic.
|
// Let all senders panic.
|
||||||
@@ -380,13 +394,25 @@ func chanClose(ch *channel) {
|
|||||||
break // processed all senders
|
break // processed all senders
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake up the sender.
|
sender.next = nil
|
||||||
scheduleTask(sender.task)
|
if wakeTail == nil {
|
||||||
|
wakeHead = sender
|
||||||
|
} else {
|
||||||
|
wakeTail.next = sender
|
||||||
|
}
|
||||||
|
wakeTail = sender
|
||||||
}
|
}
|
||||||
|
|
||||||
ch.closed = true
|
ch.closed = true
|
||||||
|
|
||||||
ch.lock.Unlock()
|
ch.lock.Unlock()
|
||||||
|
|
||||||
|
// Wake waiters only after releasing the channel lock.
|
||||||
|
for wakeHead != nil {
|
||||||
|
wake := wakeHead
|
||||||
|
wakeHead = wake.next
|
||||||
|
scheduleTask(wake.task)
|
||||||
|
}
|
||||||
|
|
||||||
interrupt.Restore(mask)
|
interrupt.Restore(mask)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,6 +465,7 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelO
|
|||||||
const selectNoIndex = ^uint32(0)
|
const selectNoIndex = ^uint32(0)
|
||||||
selectIndex := selectNoIndex
|
selectIndex := selectNoIndex
|
||||||
selectOk := true
|
selectOk := true
|
||||||
|
var wake *task.Task
|
||||||
|
|
||||||
// Iterate over each state, and see if it can proceed.
|
// Iterate over each state, and see if it can proceed.
|
||||||
// TODO: start from a random index.
|
// TODO: start from a random index.
|
||||||
@@ -450,14 +477,16 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelO
|
|||||||
}
|
}
|
||||||
|
|
||||||
if state.value == nil { // chan receive
|
if state.value == nil { // chan receive
|
||||||
if received, ok := state.ch.tryRecv(recvbuf); received {
|
if received, ok, sender := state.ch.tryRecv(recvbuf); received {
|
||||||
selectIndex = uint32(i)
|
selectIndex = uint32(i)
|
||||||
selectOk = ok
|
selectOk = ok
|
||||||
|
wake = sender
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
} else { // chan send
|
} else { // chan send
|
||||||
if state.ch.trySend(state.value) {
|
if sent, receiver := state.ch.trySend(state.value); sent {
|
||||||
selectIndex = uint32(i)
|
selectIndex = uint32(i)
|
||||||
|
wake = receiver
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,6 +498,9 @@ func chanSelect(recvbuf unsafe.Pointer, states []chanSelectState, ops []channelO
|
|||||||
if selectIndex != selectNoIndex || !blocking {
|
if selectIndex != selectNoIndex || !blocking {
|
||||||
unlockAllStates(states)
|
unlockAllStates(states)
|
||||||
chanSelectLock.Unlock()
|
chanSelectLock.Unlock()
|
||||||
|
if wake != nil {
|
||||||
|
scheduleTask(wake)
|
||||||
|
}
|
||||||
interrupt.Restore(mask)
|
interrupt.Restore(mask)
|
||||||
return selectIndex, selectOk
|
return selectIndex, selectOk
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user