compiler/runtime: move the channel blocked list onto the stack
Previously, chansend and chanrecv allocated a heap object before blocking on a channel. This object was used to implement a linked list of goroutines blocked on the channel. The chansend and chanrecv now instead accept a buffer to store this object in as an argument. The compiler now creates a stack allocation for this object and passes it in.
Этот коммит содержится в:
родитель
4e4b5955db
коммит
4321923641
2 изменённых файлов: 20 добавлений и 8 удалений
|
@ -35,12 +35,17 @@ func (b *builder) createChanSend(instr *ssa.Send) {
|
||||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
b.CreateStore(chanValue, valueAlloca)
|
b.CreateStore(chanValue, valueAlloca)
|
||||||
|
|
||||||
// Do the send.
|
// Allocate blockedlist buffer.
|
||||||
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "")
|
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||||
|
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||||
|
|
||||||
// End the lifetime of the alloca.
|
// Do the send.
|
||||||
|
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||||
|
|
||||||
|
// End the lifetime of the allocas.
|
||||||
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
// This also works around a bug in CoroSplit, at least in LLVM 8:
|
||||||
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
// https://bugs.llvm.org/show_bug.cgi?id=41742
|
||||||
|
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,9 +58,14 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
|
||||||
// Allocate memory to receive into.
|
// Allocate memory to receive into.
|
||||||
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
|
||||||
|
|
||||||
|
// Allocate blockedlist buffer.
|
||||||
|
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
|
||||||
|
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
|
||||||
|
|
||||||
// Do the receive.
|
// Do the receive.
|
||||||
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "")
|
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
|
||||||
received := b.CreateLoad(valueAlloca, "chan.received")
|
received := b.CreateLoad(valueAlloca, "chan.received")
|
||||||
|
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
|
||||||
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
|
||||||
|
|
||||||
if unop.CommaOk {
|
if unop.CommaOk {
|
||||||
|
|
|
@ -446,7 +446,7 @@ type chanSelectState struct {
|
||||||
// chanSend sends a single value over the channel.
|
// chanSend sends a single value over the channel.
|
||||||
// This operation will block unless a value is immediately available.
|
// This operation will block unless a value is immediately available.
|
||||||
// May panic if the channel is closed.
|
// May panic if the channel is closed.
|
||||||
func chanSend(ch *channel, value unsafe.Pointer) {
|
func chanSend(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList) {
|
||||||
if ch.trySend(value) {
|
if ch.trySend(value) {
|
||||||
// value immediately sent
|
// value immediately sent
|
||||||
chanDebug(ch)
|
chanDebug(ch)
|
||||||
|
@ -462,10 +462,11 @@ func chanSend(ch *channel, value unsafe.Pointer) {
|
||||||
sender := task.Current()
|
sender := task.Current()
|
||||||
ch.state = chanStateSend
|
ch.state = chanStateSend
|
||||||
sender.Ptr = value
|
sender.Ptr = value
|
||||||
ch.blocked = &channelBlockedList{
|
*blockedlist = channelBlockedList{
|
||||||
next: ch.blocked,
|
next: ch.blocked,
|
||||||
t: sender,
|
t: sender,
|
||||||
}
|
}
|
||||||
|
ch.blocked = blockedlist
|
||||||
chanDebug(ch)
|
chanDebug(ch)
|
||||||
task.Pause()
|
task.Pause()
|
||||||
sender.Ptr = nil
|
sender.Ptr = nil
|
||||||
|
@ -475,7 +476,7 @@ func chanSend(ch *channel, value unsafe.Pointer) {
|
||||||
// It blocks if there is no available value to recieve.
|
// It blocks if there is no available value to recieve.
|
||||||
// The recieved value is copied into the value pointer.
|
// The recieved value is copied into the value pointer.
|
||||||
// Returns the comma-ok value.
|
// Returns the comma-ok value.
|
||||||
func chanRecv(ch *channel, value unsafe.Pointer) bool {
|
func chanRecv(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList) bool {
|
||||||
if rx, ok := ch.tryRecv(value); rx {
|
if rx, ok := ch.tryRecv(value); rx {
|
||||||
// value immediately available
|
// value immediately available
|
||||||
chanDebug(ch)
|
chanDebug(ch)
|
||||||
|
@ -491,10 +492,11 @@ func chanRecv(ch *channel, value unsafe.Pointer) bool {
|
||||||
receiver := task.Current()
|
receiver := task.Current()
|
||||||
ch.state = chanStateRecv
|
ch.state = chanStateRecv
|
||||||
receiver.Ptr, receiver.Data = value, 1
|
receiver.Ptr, receiver.Data = value, 1
|
||||||
ch.blocked = &channelBlockedList{
|
*blockedlist = channelBlockedList{
|
||||||
next: ch.blocked,
|
next: ch.blocked,
|
||||||
t: receiver,
|
t: receiver,
|
||||||
}
|
}
|
||||||
|
ch.blocked = blockedlist
|
||||||
chanDebug(ch)
|
chanDebug(ch)
|
||||||
task.Pause()
|
task.Pause()
|
||||||
ok := receiver.Data == 1
|
ok := receiver.Data == 1
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче