Commit 8c3533c8 authored by Russ Cox's avatar Russ Cox

runtime: add memory barrier for sync send in select

Missed select case when adding the barrier last time.
All the more reason to refactor this code in Go 1.6.

Fixes #11643.

Change-Id: Ib0d19d6e0939296c0a3e06dda5e9b76f813bbc7e
Reviewed-on: https://go-review.googlesource.com/12086Reviewed-by: default avatarAustin Clements <austin@google.com>
parent 242ec168
......@@ -165,17 +165,7 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
recvg := sg.g
if sg.elem != nil {
// This is the only place in the entire runtime where one goroutine
// writes to the stack of another goroutine. The GC assumes that
// stack writes only happen when the goroutine is running and are
// only done by that goroutine. Using a write barrier is sufficient to
// make up for violating that assumption, but the write barrier has to work.
// typedmemmove will call heapBitsBulkBarrier, but the target bytes
// are not in the heap, so that will not help. We arrange to call
// memmove and typeBitsBulkBarrier instead.
memmove(sg.elem, ep, c.elemtype.size)
typeBitsBulkBarrier(c.elemtype, uintptr(sg.elem), c.elemtype.size)
sg.elem = nil
syncsend(c, sg, ep)
}
recvg.param = unsafe.Pointer(sg)
if sg.releasetime != 0 {
......@@ -287,6 +277,21 @@ func chansend(t *chantype, c *hchan, ep unsafe.Pointer, block bool, callerpc uin
return true
}
func syncsend(c *hchan, sg *sudog, elem unsafe.Pointer) {
// Send on unbuffered channel is the only operation
// in the entire runtime where one goroutine
// writes to the stack of another goroutine. The GC assumes that
// stack writes only happen when the goroutine is running and are
// only done by that goroutine. Using a write barrier is sufficient to
// make up for violating that assumption, but the write barrier has to work.
// typedmemmove will call heapBitsBulkBarrier, but the target bytes
// are not in the heap, so that will not help. We arrange to call
// memmove and typeBitsBulkBarrier instead.
memmove(sg.elem, elem, c.elemtype.size)
typeBitsBulkBarrier(c.elemtype, uintptr(sg.elem), c.elemtype.size)
sg.elem = nil
}
func closechan(c *hchan) {
if c == nil {
panic("close of nil channel")
......
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime_test
import (
"runtime"
"sync"
"testing"
)
type response struct {
}
type myError struct {
}
func (myError) Error() string { return "" }
func doRequest(useSelect bool) (*response, error) {
type async struct {
resp *response
err error
}
ch := make(chan *async, 0)
done := make(chan struct{}, 0)
if useSelect {
go func() {
select {
case ch <- &async{resp: nil, err: myError{}}:
case <-done:
}
}()
} else {
go func() {
ch <- &async{resp: nil, err: myError{}}
}()
}
r := <-ch
runtime.Gosched()
return r.resp, r.err
}
func TestChanSendSelectBarrier(t *testing.T) {
testChanSendBarrier(true)
}
func TestChanSendBarrier(t *testing.T) {
testChanSendBarrier(false)
}
func testChanSendBarrier(useSelect bool) {
var wg sync.WaitGroup
outer := 100
inner := 100000
if testing.Short() {
outer = 10
inner = 1000
}
for i := 0; i < outer; i++ {
wg.Add(1)
go func() {
defer wg.Done()
var garbage []byte
for j := 0; j < inner; j++ {
_, err := doRequest(useSelect)
_, ok := err.(myError)
if !ok {
panic(1)
}
garbage = make([]byte, 1<<10)
}
global = garbage
}()
}
wg.Wait()
}
......@@ -575,7 +575,7 @@ syncsend:
print("syncsend: sel=", sel, " c=", c, "\n")
}
if sg.elem != nil {
typedmemmove(c.elemtype, sg.elem, cas.elem)
syncsend(c, sg, cas.elem)
}
sg.elem = nil
gp = sg.g
......
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