diff --git a/compiler/channel.go b/compiler/channel.go index a2532e48..6e2d4319 100644 --- a/compiler/channel.go +++ b/compiler/channel.go @@ -35,12 +35,17 @@ func (b *builder) createChanSend(instr *ssa.Send) { valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value") b.CreateStore(chanValue, valueAlloca) - // Do the send. - b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "") + // Allocate blockedlist buffer. + 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: // https://bugs.llvm.org/show_bug.cgi?id=41742 + b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize) b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) } @@ -53,9 +58,14 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value { // Allocate memory to receive into. 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. - commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "") + commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "") received := b.CreateLoad(valueAlloca, "chan.received") + b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize) b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize) if unop.CommaOk { diff --git a/src/runtime/chan.go b/src/runtime/chan.go index 6563d455..37ad154a 100644 --- a/src/runtime/chan.go +++ b/src/runtime/chan.go @@ -446,7 +446,7 @@ type chanSelectState struct { // chanSend sends a single value over the channel. // This operation will block unless a value is immediately available. // 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) { // value immediately sent chanDebug(ch) @@ -462,10 +462,11 @@ func chanSend(ch *channel, value unsafe.Pointer) { sender := task.Current() ch.state = chanStateSend sender.Ptr = value - ch.blocked = &channelBlockedList{ + *blockedlist = channelBlockedList{ next: ch.blocked, t: sender, } + ch.blocked = blockedlist chanDebug(ch) task.Pause() sender.Ptr = nil @@ -475,7 +476,7 @@ func chanSend(ch *channel, value unsafe.Pointer) { // It blocks if there is no available value to recieve. // The recieved value is copied into the value pointer. // 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 { // value immediately available chanDebug(ch) @@ -491,10 +492,11 @@ func chanRecv(ch *channel, value unsafe.Pointer) bool { receiver := task.Current() ch.state = chanStateRecv receiver.Ptr, receiver.Data = value, 1 - ch.blocked = &channelBlockedList{ + *blockedlist = channelBlockedList{ next: ch.blocked, t: receiver, } + ch.blocked = blockedlist chanDebug(ch) task.Pause() ok := receiver.Data == 1