Commit e9f90ba2 authored by Keith Randall's avatar Keith Randall

Revert "runtime: simplify buffered channels."

Revert for now until #13169 is understood.

This reverts commit 8e496f1d.

Change-Id: Ib3eb2588824ef47a2b6eb9e377a24e5c817fcc81
Reviewed-on: https://go-review.googlesource.com/16716Reviewed-by: default avatarKeith Randall <khr@golang.org>
parent 26263354
...@@ -6,11 +6,6 @@ package runtime ...@@ -6,11 +6,6 @@ package runtime
// This file contains the implementation of Go channels. // This file contains the implementation of Go channels.
// Invariants:
// At least one of c.sendq and c.recvq is empty.
// For buffered channels, also:
// c.qcount > 0 implies that c.recvq is empty.
// c.qcount < c.dataqsiz implies that c.sendq is empty.
import "unsafe" import "unsafe"
const ( const (
...@@ -158,117 +153,135 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin ...@@ -158,117 +153,135 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
} }
lock(&c.lock) lock(&c.lock)
if c.closed != 0 { if c.closed != 0 {
unlock(&c.lock) unlock(&c.lock)
panic("send on closed channel") panic("send on closed channel")
} }
if sg := c.recvq.dequeue(); sg != nil { if c.dataqsiz == 0 { // synchronous channel
// Found a waiting receiver. We pass the value we want to send sg := c.recvq.dequeue()
// directly to the receiver, bypassing the channel buffer (if any). if sg != nil { // found a waiting receiver
send(c, sg, ep, func() { unlock(&c.lock) }) if raceenabled {
return true racesync(c, sg)
} }
unlock(&c.lock)
if c.qcount < c.dataqsiz { recvg := sg.g
// Space is available in the channel buffer. Enqueue the element to send. if sg.elem != nil {
qp := chanbuf(c, c.sendx) syncsend(c, sg, ep)
if raceenabled { }
raceacquire(qp) recvg.param = unsafe.Pointer(sg)
racerelease(qp) if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
goready(recvg, 3)
return true
} }
typedmemmove(c.elemtype, qp, ep)
c.sendx++ if !block {
if c.sendx == c.dataqsiz { unlock(&c.lock)
c.sendx = 0 return false
} }
c.qcount++
unlock(&c.lock) // no receiver available: block on this channel.
gp := getg()
mysg := acquireSudog()
mysg.releasetime = 0
if t0 != 0 {
mysg.releasetime = -1
}
mysg.elem = ep
mysg.waitlink = nil
gp.waiting = mysg
mysg.g = gp
mysg.selectdone = nil
gp.param = nil
c.sendq.enqueue(mysg)
goparkunlock(&c.lock, "chan send", traceEvGoBlockSend, 3)
// someone woke us up.
if mysg != gp.waiting {
throw("G waiting list is corrupted!")
}
gp.waiting = nil
if gp.param == nil {
if c.closed == 0 {
throw("chansend: spurious wakeup")
}
panic("send on closed channel")
}
gp.param = nil
if mysg.releasetime > 0 {
blockevent(int64(mysg.releasetime)-t0, 2)
}
releaseSudog(mysg)
return true return true
} }
if !block { // asynchronous channel
unlock(&c.lock) // wait for some space to write our data
return false var t1 int64
for futile := byte(0); c.qcount >= c.dataqsiz; futile = traceFutileWakeup {
if !block {
unlock(&c.lock)
return false
}
gp := getg()
mysg := acquireSudog()
mysg.releasetime = 0
if t0 != 0 {
mysg.releasetime = -1
}
mysg.g = gp
mysg.elem = nil
mysg.selectdone = nil
c.sendq.enqueue(mysg)
goparkunlock(&c.lock, "chan send", traceEvGoBlockSend|futile, 3)
// someone woke us up - try again
if mysg.releasetime > 0 {
t1 = mysg.releasetime
}
releaseSudog(mysg)
lock(&c.lock)
if c.closed != 0 {
unlock(&c.lock)
panic("send on closed channel")
}
} }
// Block on the channel. Some receiver will complete our operation for us. // write our data into the channel buffer
gp := getg() if raceenabled {
mysg := acquireSudog() raceacquire(chanbuf(c, c.sendx))
mysg.releasetime = 0 racerelease(chanbuf(c, c.sendx))
if t0 != 0 {
mysg.releasetime = -1
}
mysg.elem = ep
mysg.waitlink = nil
mysg.g = gp
mysg.selectdone = nil
gp.waiting = mysg
gp.param = nil
c.sendq.enqueue(mysg)
goparkunlock(&c.lock, "chan send", traceEvGoBlockSend, 3)
// someone woke us up.
if mysg != gp.waiting {
throw("G waiting list is corrupted")
}
gp.waiting = nil
if gp.param == nil {
if c.closed == 0 {
throw("chansend: spurious wakeup")
}
panic("send on closed channel")
} }
gp.param = nil typedmemmove(c.elemtype, chanbuf(c, c.sendx), ep)
if mysg.releasetime > 0 { c.sendx++
blockevent(int64(mysg.releasetime)-t0, 2) if c.sendx == c.dataqsiz {
c.sendx = 0
} }
releaseSudog(mysg) c.qcount++
return true
}
// send processes a send operation on an empty channel c. // wake up a waiting receiver
// The value ep sent by the sender is copied to the receiver sg. sg := c.recvq.dequeue()
// The receiver is then woken up to go on its merry way. if sg != nil {
// Channel c must be empty and locked. send unlocks c with unlockf. recvg := sg.g
// sg must already be dequeued from c. unlock(&c.lock)
// ep must be non-nil and point to the heap or the caller's stack. if sg.releasetime != 0 {
func send(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func()) { sg.releasetime = cputicks()
if raceenabled {
if c.dataqsiz == 0 {
racesync(c, sg)
} else {
// Pretend we go through the buffer, even though
// we copy directly. Note that we need to increment
// the head/tail locations only when raceenabled.
qp := chanbuf(c, c.recvx)
raceacquire(qp)
racerelease(qp)
raceacquireg(sg.g, qp)
racereleaseg(sg.g, qp)
c.recvx++
if c.recvx == c.dataqsiz {
c.recvx = 0
}
c.sendx = c.recvx // c.sendx = (c.sendx+1) % c.dataqsiz
} }
goready(recvg, 3)
} else {
unlock(&c.lock)
} }
unlockf() if t1 > 0 {
if sg.elem != nil { blockevent(t1-t0, 2)
sendDirect(c.elemtype, sg.elem, ep)
sg.elem = nil
}
gp := sg.g
gp.param = unsafe.Pointer(sg)
if sg.releasetime != 0 {
sg.releasetime = cputicks()
} }
goready(gp, 4) return true
} }
func sendDirect(t *_type, dst, src unsafe.Pointer) { func syncsend(c *hchan, sg *sudog, elem unsafe.Pointer) {
// Send on an unbuffered or empty-buffered channel is the only operation // Send on unbuffered channel is the only operation
// in the entire runtime where one goroutine // in the entire runtime where one goroutine
// writes to the stack of another goroutine. The GC assumes that // writes to the stack of another goroutine. The GC assumes that
// stack writes only happen when the goroutine is running and are // stack writes only happen when the goroutine is running and are
...@@ -277,8 +290,9 @@ func sendDirect(t *_type, dst, src unsafe.Pointer) { ...@@ -277,8 +290,9 @@ func sendDirect(t *_type, dst, src unsafe.Pointer) {
// typedmemmove will call heapBitsBulkBarrier, but the target bytes // typedmemmove will call heapBitsBulkBarrier, but the target bytes
// are not in the heap, so that will not help. We arrange to call // are not in the heap, so that will not help. We arrange to call
// memmove and typeBitsBulkBarrier instead. // memmove and typeBitsBulkBarrier instead.
memmove(dst, src, t.size) memmove(sg.elem, elem, c.elemtype.size)
typeBitsBulkBarrier(t, uintptr(dst), t.size) typeBitsBulkBarrier(c.elemtype, uintptr(sg.elem), c.elemtype.size)
sg.elem = nil
} }
func closechan(c *hchan) { func closechan(c *hchan) {
...@@ -306,36 +320,27 @@ func closechan(c *hchan) { ...@@ -306,36 +320,27 @@ func closechan(c *hchan) {
if sg == nil { if sg == nil {
break break
} }
if sg.elem != nil {
memclr(sg.elem, uintptr(c.elemsize))
sg.elem = nil
}
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
gp := sg.g gp := sg.g
sg.elem = nil
gp.param = nil gp.param = nil
if raceenabled { if sg.releasetime != 0 {
raceacquireg(gp, unsafe.Pointer(c)) sg.releasetime = cputicks()
} }
goready(gp, 3) goready(gp, 3)
} }
// release all writers (they will panic) // release all writers
for { for {
sg := c.sendq.dequeue() sg := c.sendq.dequeue()
if sg == nil { if sg == nil {
break break
} }
gp := sg.g
sg.elem = nil sg.elem = nil
gp.param = nil
if sg.releasetime != 0 { if sg.releasetime != 0 {
sg.releasetime = cputicks() sg.releasetime = cputicks()
} }
gp := sg.g
gp.param = nil
if raceenabled {
raceacquireg(gp, unsafe.Pointer(c))
}
goready(gp, 3) goready(gp, 3)
} }
unlock(&c.lock) unlock(&c.lock)
...@@ -358,10 +363,8 @@ func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) { ...@@ -358,10 +363,8 @@ func chanrecv2(t *chantype, c *hchan, elem unsafe.Pointer) (received bool) {
// If block == false and no elements are available, returns (false, false). // If block == false and no elements are available, returns (false, false).
// Otherwise, if c is closed, zeros *ep and returns (true, false). // Otherwise, if c is closed, zeros *ep and returns (true, false).
// Otherwise, fills in *ep with an element and returns (true, true). // Otherwise, fills in *ep with an element and returns (true, true).
// A non-nil ep must point to the heap or the caller's stack.
func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) {
// raceenabled: don't need to check ep, as it is always on the stack // raceenabled: don't need to check ep, as it is always on the stack.
// or is new memory allocated by reflect.
if debugChan { if debugChan {
print("chanrecv: chan=", c, "\n") print("chanrecv: chan=", c, "\n")
...@@ -399,139 +402,167 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r ...@@ -399,139 +402,167 @@ func chanrecv(t *chantype, c *hchan, ep unsafe.Pointer, block bool) (selected, r
} }
lock(&c.lock) lock(&c.lock)
if c.dataqsiz == 0 { // synchronous channel
if c.closed != 0 {
return recvclosed(c, ep)
}
sg := c.sendq.dequeue()
if sg != nil {
if raceenabled {
racesync(c, sg)
}
unlock(&c.lock)
if c.closed != 0 && c.qcount == 0 { if ep != nil {
if raceenabled { typedmemmove(c.elemtype, ep, sg.elem)
raceacquire(unsafe.Pointer(c)) }
sg.elem = nil
gp := sg.g
gp.param = unsafe.Pointer(sg)
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
goready(gp, 3)
selected = true
received = true
return
} }
unlock(&c.lock)
if ep != nil { if !block {
memclr(ep, uintptr(c.elemsize)) unlock(&c.lock)
return
} }
return true, false
}
if sg := c.sendq.dequeue(); sg != nil { // no sender available: block on this channel.
// Found a waiting sender. If buffer is size 0, receive value gp := getg()
// directly from sender. Otherwise, recieve from head of queue mysg := acquireSudog()
// and add sender's value to the tail of the queue (both map to mysg.releasetime = 0
// the same buffer slot because the queue is full). if t0 != 0 {
recv(c, sg, ep, func() { unlock(&c.lock) }) mysg.releasetime = -1
return true, true }
mysg.elem = ep
mysg.waitlink = nil
gp.waiting = mysg
mysg.g = gp
mysg.selectdone = nil
gp.param = nil
c.recvq.enqueue(mysg)
goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3)
// someone woke us up
if mysg != gp.waiting {
throw("G waiting list is corrupted!")
}
gp.waiting = nil
if mysg.releasetime > 0 {
blockevent(mysg.releasetime-t0, 2)
}
haveData := gp.param != nil
gp.param = nil
releaseSudog(mysg)
if haveData {
// a sender sent us some data. It already wrote to ep.
selected = true
received = true
return
}
lock(&c.lock)
if c.closed == 0 {
throw("chanrecv: spurious wakeup")
}
return recvclosed(c, ep)
} }
if c.qcount > 0 { // asynchronous channel
// Receive directly from queue // wait for some data to appear
qp := chanbuf(c, c.recvx) var t1 int64
if raceenabled { for futile := byte(0); c.qcount <= 0; futile = traceFutileWakeup {
raceacquire(qp) if c.closed != 0 {
racerelease(qp) selected, received = recvclosed(c, ep)
if t1 > 0 {
blockevent(t1-t0, 2)
}
return
}
if !block {
unlock(&c.lock)
return
} }
if ep != nil {
typedmemmove(c.elemtype, ep, qp) // wait for someone to send an element
gp := getg()
mysg := acquireSudog()
mysg.releasetime = 0
if t0 != 0 {
mysg.releasetime = -1
} }
memclr(qp, uintptr(c.elemsize)) mysg.elem = nil
c.recvx++ mysg.g = gp
if c.recvx == c.dataqsiz { mysg.selectdone = nil
c.recvx = 0
c.recvq.enqueue(mysg)
goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv|futile, 3)
// someone woke us up - try again
if mysg.releasetime > 0 {
t1 = mysg.releasetime
} }
c.qcount-- releaseSudog(mysg)
unlock(&c.lock) lock(&c.lock)
return true, true
} }
if !block { if raceenabled {
unlock(&c.lock) raceacquire(chanbuf(c, c.recvx))
return false, false racerelease(chanbuf(c, c.recvx))
} }
if ep != nil {
// no sender available: block on this channel. typedmemmove(c.elemtype, ep, chanbuf(c, c.recvx))
gp := getg() }
mysg := acquireSudog() memclr(chanbuf(c, c.recvx), uintptr(c.elemsize))
mysg.releasetime = 0
if t0 != 0 {
mysg.releasetime = -1
}
mysg.elem = ep
mysg.waitlink = nil
gp.waiting = mysg
mysg.g = gp
mysg.selectdone = nil
gp.param = nil
c.recvq.enqueue(mysg)
goparkunlock(&c.lock, "chan receive", traceEvGoBlockRecv, 3)
// someone woke us up
if mysg != gp.waiting {
throw("G waiting list is corrupted")
}
gp.waiting = nil
if mysg.releasetime > 0 {
blockevent(mysg.releasetime-t0, 2)
}
closed := gp.param == nil
gp.param = nil
releaseSudog(mysg)
return true, !closed
}
// recv processes a receive operation on a full channel c. c.recvx++
// There are 2 parts: if c.recvx == c.dataqsiz {
// 1) The value sent by the sender sg is put into the channel c.recvx = 0
// and the sender is woken up to go on its merry way. }
// 2) The value received by the receiver (the current G) is c.qcount--
// written to ep.
// For synchronous channels, both values are the same. // ping a sender now that there is space
// For asynchronous channels, the receiver gets its data from sg := c.sendq.dequeue()
// the channel buffer and the sender's data is put in the if sg != nil {
// channel buffer. gp := sg.g
// Channel c must be full and locked. recv unlocks c with unlockf. unlock(&c.lock)
// sg must already be dequeued from c. if sg.releasetime != 0 {
// A non-nil ep must point to the heap or the caller's stack. sg.releasetime = cputicks()
func recv(c *hchan, sg *sudog, ep unsafe.Pointer, unlockf func()) {
if c.dataqsiz == 0 {
if raceenabled {
racesync(c, sg)
}
unlockf()
if ep != nil {
// copy data from sender
// ep points to our own stack or heap, so nothing
// special (ala sendDirect) needed here.
typedmemmove(c.elemtype, ep, sg.elem)
} }
goready(gp, 3)
} else { } else {
// Queue is full. Take the item at the unlock(&c.lock)
// head of the queue. Make the sender enqueue
// its item at the tail of the queue. Since the
// queue is full, those are both the same slot.
qp := chanbuf(c, c.recvx)
if raceenabled {
raceacquire(qp)
racerelease(qp)
raceacquireg(sg.g, qp)
racereleaseg(sg.g, qp)
}
// copy data from queue to receiver
if ep != nil {
typedmemmove(c.elemtype, ep, qp)
}
// copy data from sender to queue
typedmemmove(c.elemtype, qp, sg.elem)
c.recvx++
if c.recvx == c.dataqsiz {
c.recvx = 0
}
c.sendx = c.recvx // c.sendx = (c.sendx+1) % c.dataqsiz
unlockf()
} }
sg.elem = nil
gp := sg.g if t1 > 0 {
gp.param = unsafe.Pointer(sg) blockevent(t1-t0, 2)
if sg.releasetime != 0 { }
sg.releasetime = cputicks() selected = true
received = true
return
}
// recvclosed is a helper function for chanrecv. Handles cleanup
// when the receiver encounters a closed channel.
// Caller must hold c.lock, recvclosed will release the lock.
func recvclosed(c *hchan, ep unsafe.Pointer) (selected, recevied bool) {
if raceenabled {
raceacquire(unsafe.Pointer(c))
}
unlock(&c.lock)
if ep != nil {
memclr(ep, uintptr(c.elemsize))
} }
goready(gp, 4) return true, false
} }
// compiler implements // compiler implements
......
...@@ -304,7 +304,7 @@ func selectgoImpl(sel *hselect) (uintptr, uint16) { ...@@ -304,7 +304,7 @@ func selectgoImpl(sel *hselect) (uintptr, uint16) {
k *scase k *scase
sglist *sudog sglist *sudog
sgnext *sudog sgnext *sudog
qp unsafe.Pointer futile byte
) )
loop: loop:
...@@ -317,12 +317,15 @@ loop: ...@@ -317,12 +317,15 @@ loop:
switch cas.kind { switch cas.kind {
case caseRecv: case caseRecv:
sg = c.sendq.dequeue() if c.dataqsiz > 0 {
if sg != nil { if c.qcount > 0 {
goto recv goto asyncrecv
} }
if c.qcount > 0 { } else {
goto bufrecv sg = c.sendq.dequeue()
if sg != nil {
goto syncrecv
}
} }
if c.closed != 0 { if c.closed != 0 {
goto rclose goto rclose
...@@ -335,12 +338,15 @@ loop: ...@@ -335,12 +338,15 @@ loop:
if c.closed != 0 { if c.closed != 0 {
goto sclose goto sclose
} }
sg = c.recvq.dequeue() if c.dataqsiz > 0 {
if sg != nil { if c.qcount < c.dataqsiz {
goto send goto asyncsend
} }
if c.qcount < c.dataqsiz { } else {
goto bufsend sg = c.recvq.dequeue()
if sg != nil {
goto syncsend
}
} }
case caseDefault: case caseDefault:
...@@ -357,9 +363,6 @@ loop: ...@@ -357,9 +363,6 @@ loop:
// pass 2 - enqueue on all chans // pass 2 - enqueue on all chans
gp = getg() gp = getg()
done = 0 done = 0
if gp.waiting != nil {
throw("gp.waiting != nil")
}
for i := 0; i < int(sel.ncase); i++ { for i := 0; i < int(sel.ncase); i++ {
cas = &scases[pollorder[i]] cas = &scases[pollorder[i]]
c = cas.c c = cas.c
...@@ -386,7 +389,7 @@ loop: ...@@ -386,7 +389,7 @@ loop:
// wait for someone to wake us up // wait for someone to wake us up
gp.param = nil gp.param = nil
gopark(selparkcommit, unsafe.Pointer(sel), "select", traceEvGoBlockSelect, 2) gopark(selparkcommit, unsafe.Pointer(sel), "select", traceEvGoBlockSelect|futile, 2)
// someone woke us up // someone woke us up
sellock(sel) sellock(sel)
...@@ -429,13 +432,16 @@ loop: ...@@ -429,13 +432,16 @@ loop:
} }
if cas == nil { if cas == nil {
// This can happen if we were woken up by a close(). futile = traceFutileWakeup
// TODO: figure that out explicitly so we don't need this loop.
goto loop goto loop
} }
c = cas.c c = cas.c
if c.dataqsiz > 0 {
throw("selectgo: shouldn't happen")
}
if debugSelect { if debugSelect {
print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n") print("wait-return: sel=", sel, " c=", c, " cas=", cas, " kind=", cas.kind, "\n")
} }
...@@ -464,7 +470,7 @@ loop: ...@@ -464,7 +470,7 @@ loop:
selunlock(sel) selunlock(sel)
goto retc goto retc
bufrecv: asyncrecv:
// can receive from buffer // can receive from buffer
if raceenabled { if raceenabled {
if cas.elem != nil { if cas.elem != nil {
...@@ -479,20 +485,29 @@ bufrecv: ...@@ -479,20 +485,29 @@ bufrecv:
if cas.receivedp != nil { if cas.receivedp != nil {
*cas.receivedp = true *cas.receivedp = true
} }
qp = chanbuf(c, c.recvx)
if cas.elem != nil { if cas.elem != nil {
typedmemmove(c.elemtype, cas.elem, qp) typedmemmove(c.elemtype, cas.elem, chanbuf(c, c.recvx))
} }
memclr(qp, uintptr(c.elemsize)) memclr(chanbuf(c, c.recvx), uintptr(c.elemsize))
c.recvx++ c.recvx++
if c.recvx == c.dataqsiz { if c.recvx == c.dataqsiz {
c.recvx = 0 c.recvx = 0
} }
c.qcount-- c.qcount--
selunlock(sel) sg = c.sendq.dequeue()
if sg != nil {
gp = sg.g
selunlock(sel)
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
goready(gp, 3)
} else {
selunlock(sel)
}
goto retc goto retc
bufsend: asyncsend:
// can send to buffer // can send to buffer
if raceenabled { if raceenabled {
raceacquire(chanbuf(c, c.sendx)) raceacquire(chanbuf(c, c.sendx))
...@@ -508,18 +523,47 @@ bufsend: ...@@ -508,18 +523,47 @@ bufsend:
c.sendx = 0 c.sendx = 0
} }
c.qcount++ c.qcount++
selunlock(sel) sg = c.recvq.dequeue()
if sg != nil {
gp = sg.g
selunlock(sel)
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
goready(gp, 3)
} else {
selunlock(sel)
}
goto retc goto retc
recv: syncrecv:
// can receive from sleeping sender (sg) // can receive from sleeping sender (sg)
recv(c, sg, cas.elem, func() { selunlock(sel) }) if raceenabled {
if cas.elem != nil {
raceWriteObjectPC(c.elemtype, cas.elem, cas.pc, chanrecvpc)
}
racesync(c, sg)
}
if msanenabled && cas.elem != nil {
msanwrite(cas.elem, c.elemtype.size)
}
selunlock(sel)
if debugSelect { if debugSelect {
print("syncrecv: sel=", sel, " c=", c, "\n") print("syncrecv: sel=", sel, " c=", c, "\n")
} }
if cas.receivedp != nil { if cas.receivedp != nil {
*cas.receivedp = true *cas.receivedp = true
} }
if cas.elem != nil {
typedmemmove(c.elemtype, cas.elem, sg.elem)
}
sg.elem = nil
gp = sg.g
gp.param = unsafe.Pointer(sg)
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
goready(gp, 3)
goto retc goto retc
rclose: rclose:
...@@ -536,19 +580,29 @@ rclose: ...@@ -536,19 +580,29 @@ rclose:
} }
goto retc goto retc
send: syncsend:
// can send to a sleeping receiver (sg) // can send to sleeping receiver (sg)
if raceenabled { if raceenabled {
raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc) raceReadObjectPC(c.elemtype, cas.elem, cas.pc, chansendpc)
racesync(c, sg)
} }
if msanenabled { if msanenabled {
msanread(cas.elem, c.elemtype.size) msanread(cas.elem, c.elemtype.size)
} }
send(c, sg, cas.elem, func() { selunlock(sel) }) selunlock(sel)
if debugSelect { if debugSelect {
print("syncsend: sel=", sel, " c=", c, "\n") print("syncsend: sel=", sel, " c=", c, "\n")
} }
goto retc if sg.elem != nil {
syncsend(c, sg, cas.elem)
}
sg.elem = nil
gp = sg.g
gp.param = unsafe.Pointer(sg)
if sg.releasetime != 0 {
sg.releasetime = cputicks()
}
goready(gp, 3)
retc: retc:
if cas.releasetime > 0 { if cas.releasetime > 0 {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment