Skip to content

Commit 4321923

Browse files
niaowaykevl
authored andcommitted
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.
1 parent 4e4b595 commit 4321923

File tree

2 files changed

+19
-7
lines changed

2 files changed

+19
-7
lines changed

compiler/channel.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,17 @@ func (b *builder) createChanSend(instr *ssa.Send) {
3535
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
3636
b.CreateStore(chanValue, valueAlloca)
3737

38+
// Allocate blockedlist buffer.
39+
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
40+
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
41+
3842
// Do the send.
39-
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast}, "")
43+
b.createRuntimeCall("chanSend", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
4044

41-
// End the lifetime of the alloca.
45+
// End the lifetime of the allocas.
4246
// This also works around a bug in CoroSplit, at least in LLVM 8:
4347
// https://bugs.llvm.org/show_bug.cgi?id=41742
48+
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
4449
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
4550
}
4651

@@ -53,9 +58,14 @@ func (b *builder) createChanRecv(unop *ssa.UnOp) llvm.Value {
5358
// Allocate memory to receive into.
5459
valueAlloca, valueAllocaCast, valueAllocaSize := b.createTemporaryAlloca(valueType, "chan.value")
5560

61+
// Allocate blockedlist buffer.
62+
channelBlockedList := b.mod.GetTypeByName("runtime.channelBlockedList")
63+
channelBlockedListAlloca, channelBlockedListAllocaCast, channelBlockedListAllocaSize := b.createTemporaryAlloca(channelBlockedList, "chan.blockedList")
64+
5665
// Do the receive.
57-
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast}, "")
66+
commaOk := b.createRuntimeCall("chanRecv", []llvm.Value{ch, valueAllocaCast, channelBlockedListAlloca}, "")
5867
received := b.CreateLoad(valueAlloca, "chan.received")
68+
b.emitLifetimeEnd(channelBlockedListAllocaCast, channelBlockedListAllocaSize)
5969
b.emitLifetimeEnd(valueAllocaCast, valueAllocaSize)
6070

6171
if unop.CommaOk {

src/runtime/chan.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -446,7 +446,7 @@ type chanSelectState struct {
446446
// chanSend sends a single value over the channel.
447447
// This operation will block unless a value is immediately available.
448448
// May panic if the channel is closed.
449-
func chanSend(ch *channel, value unsafe.Pointer) {
449+
func chanSend(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList) {
450450
if ch.trySend(value) {
451451
// value immediately sent
452452
chanDebug(ch)
@@ -462,10 +462,11 @@ func chanSend(ch *channel, value unsafe.Pointer) {
462462
sender := task.Current()
463463
ch.state = chanStateSend
464464
sender.Ptr = value
465-
ch.blocked = &channelBlockedList{
465+
*blockedlist = channelBlockedList{
466466
next: ch.blocked,
467467
t: sender,
468468
}
469+
ch.blocked = blockedlist
469470
chanDebug(ch)
470471
task.Pause()
471472
sender.Ptr = nil
@@ -475,7 +476,7 @@ func chanSend(ch *channel, value unsafe.Pointer) {
475476
// It blocks if there is no available value to recieve.
476477
// The recieved value is copied into the value pointer.
477478
// Returns the comma-ok value.
478-
func chanRecv(ch *channel, value unsafe.Pointer) bool {
479+
func chanRecv(ch *channel, value unsafe.Pointer, blockedlist *channelBlockedList) bool {
479480
if rx, ok := ch.tryRecv(value); rx {
480481
// value immediately available
481482
chanDebug(ch)
@@ -491,10 +492,11 @@ func chanRecv(ch *channel, value unsafe.Pointer) bool {
491492
receiver := task.Current()
492493
ch.state = chanStateRecv
493494
receiver.Ptr, receiver.Data = value, 1
494-
ch.blocked = &channelBlockedList{
495+
*blockedlist = channelBlockedList{
495496
next: ch.blocked,
496497
t: receiver,
497498
}
499+
ch.blocked = blockedlist
498500
chanDebug(ch)
499501
task.Pause()
500502
ok := receiver.Data == 1

0 commit comments

Comments
 (0)