Commit 3660b532 authored by Russ Cox's avatar Russ Cox Committed by Rob Pike

reflect: add garbage collection info in ChanOf, MapOf, PtrTo, SliceOf

ArrayOf will remain unexported (and therefore unused) for Go 1.1.

Fixes #4375.

R=0xe2.0x9a.0x9b, r, iant
CC=golang-dev
https://golang.org/cl/7716048
parent a101bfc6
......@@ -1069,6 +1069,7 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
case TPTR32:
case TPTR64:
// NOTE: Any changes here need to be made to reflect.PtrTo as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
if(!haspointers(t->type) || t->type->etype == TUINT8) {
......@@ -1093,6 +1094,7 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
// struct Hchan*
case TCHAN:
// NOTE: Any changes here need to be made to reflect.ChanOf as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
ot = duintptr(s, ot, GC_CHAN_PTR);
......@@ -1103,6 +1105,7 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
// struct Hmap*
case TMAP:
// NOTE: Any changes here need to be made to reflect.MapOf as well.
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
ot = duintptr(s, ot, GC_MAP_PTR);
......@@ -1139,6 +1142,7 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
if(t->bound < -1)
fatal("dgcsym1: invalid bound, %T", t);
if(isslice(t)) {
// NOTE: Any changes here need to be made to reflect.SliceOf as well.
// struct { byte* array; uint32 len; uint32 cap; }
if(*off % widthptr != 0)
fatal("dgcsym1: invalid alignment, %T", t);
......@@ -1152,6 +1156,9 @@ dgcsym1(Sym *s, int ot, Type *t, vlong *off, int stack_size)
}
*off += t->width;
} else {
// NOTE: Any changes here need to be made to reflect.ArrayOf as well,
// at least once ArrayOf's gc info is implemented and ArrayOf is exported.
// struct { byte* array; uint32 len; uint32 cap; }
if(t->bound < 1 || !haspointers(t->type)) {
*off += t->width;
} else if(gcinline(t)) {
......
......@@ -14,6 +14,7 @@ import (
"os"
. "reflect"
"runtime"
"sort"
"sync"
"testing"
"time"
......@@ -2199,6 +2200,30 @@ func TestPtrTo(t *testing.T) {
}
}
func TestPtrToGC(t *testing.T) {
type T *uintptr
tt := TypeOf(T(nil))
pt := PtrTo(tt)
const n = 100
var x []interface{}
for i := 0; i < n; i++ {
v := New(pt)
p := new(*uintptr)
*p = new(uintptr)
**p = uintptr(i)
v.Elem().Set(ValueOf(p).Convert(pt))
x = append(x, v.Interface())
}
runtime.GC()
for i, xi := range x {
k := ValueOf(xi).Elem().Elem().Elem().Interface().(uintptr)
if k != uintptr(i) {
t.Errorf("lost x[%d] = %d, want %d", i, k, i)
}
}
}
func TestAddr(t *testing.T) {
var p struct {
X, Y int
......@@ -2991,8 +3016,10 @@ func TestSliceOf(t *testing.T) {
type T int
st := SliceOf(TypeOf(T(1)))
v := MakeSlice(st, 10, 10)
runtime.GC()
for i := 0; i < v.Len(); i++ {
v.Index(i).Set(ValueOf(T(i)))
runtime.GC()
}
s := fmt.Sprint(v.Interface())
want := "[0 1 2 3 4 5 6 7 8 9]"
......@@ -3005,13 +3032,44 @@ func TestSliceOf(t *testing.T) {
checkSameType(t, Zero(SliceOf(TypeOf(T1(1)))).Interface(), []T1{})
}
func TestSliceOfGC(t *testing.T) {
type T *uintptr
tt := TypeOf(T(nil))
st := SliceOf(tt)
const n = 100
var x []interface{}
for i := 0; i < n; i++ {
v := MakeSlice(st, n, n)
for j := 0; j < v.Len(); j++ {
p := new(uintptr)
*p = uintptr(i*n + j)
v.Index(j).Set(ValueOf(p).Convert(tt))
}
x = append(x, v.Interface())
}
runtime.GC()
for i, xi := range x {
v := ValueOf(xi)
for j := 0; j < v.Len(); j++ {
k := v.Index(j).Elem().Interface()
if k != uintptr(i*n+j) {
t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
}
}
}
}
func TestChanOf(t *testing.T) {
// check construction and use of type not in binary
type T string
ct := ChanOf(BothDir, TypeOf(T("")))
v := MakeChan(ct, 2)
runtime.GC()
v.Send(ValueOf(T("hello")))
runtime.GC()
v.Send(ValueOf(T("world")))
runtime.GC()
sv1, _ := v.Recv()
sv2, _ := v.Recv()
......@@ -3026,13 +3084,63 @@ func TestChanOf(t *testing.T) {
checkSameType(t, Zero(ChanOf(BothDir, TypeOf(T1(1)))).Interface(), (chan T1)(nil))
}
func TestChanOfGC(t *testing.T) {
done := make(chan bool, 1)
go func() {
select {
case <-done:
case <-time.After(5 * time.Second):
panic("deadlock in TestChanOfGC")
}
}()
defer func() {
done <- true
}()
type T *uintptr
tt := TypeOf(T(nil))
ct := ChanOf(BothDir, tt)
// NOTE: The garbage collector handles allocated channels specially,
// so we have to save pointers to channels in x; the pointer code will
// use the gc info in the newly constructed chan type.
const n = 100
var x []interface{}
for i := 0; i < n; i++ {
v := MakeChan(ct, n)
for j := 0; j < n; j++ {
p := new(uintptr)
*p = uintptr(i*n + j)
v.Send(ValueOf(p).Convert(tt))
}
pv := New(ct)
pv.Elem().Set(v)
x = append(x, pv.Interface())
}
runtime.GC()
for i, xi := range x {
v := ValueOf(xi).Elem()
for j := 0; j < n; j++ {
pv, _ := v.Recv()
k := pv.Elem().Interface()
if k != uintptr(i*n+j) {
t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
}
}
}
}
func TestMapOf(t *testing.T) {
// check construction and use of type not in binary
type K string
type V float64
v := MakeMap(MapOf(TypeOf(K("")), TypeOf(V(0))))
runtime.GC()
v.SetMapIndex(ValueOf(K("a")), ValueOf(V(1)))
runtime.GC()
s := fmt.Sprint(v.Interface())
want := "map[a:1]"
......@@ -3042,6 +3150,81 @@ func TestMapOf(t *testing.T) {
// check that type already in binary is found
checkSameType(t, Zero(MapOf(TypeOf(V(0)), TypeOf(K("")))).Interface(), map[V]K(nil))
// check that invalid key type panics
shouldPanic(func() { MapOf(TypeOf((func())(nil)), TypeOf(false)) })
}
func TestMapOfGCKeys(t *testing.T) {
type T *uintptr
tt := TypeOf(T(nil))
mt := MapOf(tt, TypeOf(false))
// NOTE: The garbage collector handles allocated maps specially,
// so we have to save pointers to maps in x; the pointer code will
// use the gc info in the newly constructed map type.
const n = 100
var x []interface{}
for i := 0; i < n; i++ {
v := MakeMap(mt)
for j := 0; j < n; j++ {
p := new(uintptr)
*p = uintptr(i*n + j)
v.SetMapIndex(ValueOf(p).Convert(tt), ValueOf(true))
}
pv := New(mt)
pv.Elem().Set(v)
x = append(x, pv.Interface())
}
runtime.GC()
for i, xi := range x {
v := ValueOf(xi).Elem()
var out []int
for _, kv := range v.MapKeys() {
out = append(out, int(kv.Elem().Interface().(uintptr)))
}
sort.Ints(out)
for j, k := range out {
if k != i*n+j {
t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
}
}
}
}
func TestMapOfGCValues(t *testing.T) {
type T *uintptr
tt := TypeOf(T(nil))
mt := MapOf(TypeOf(1), tt)
// NOTE: The garbage collector handles allocated maps specially,
// so we have to save pointers to maps in x; the pointer code will
// use the gc info in the newly constructed map type.
const n = 100
var x []interface{}
for i := 0; i < n; i++ {
v := MakeMap(mt)
for j := 0; j < n; j++ {
p := new(uintptr)
*p = uintptr(i*n + j)
v.SetMapIndex(ValueOf(j), ValueOf(p).Convert(tt))
}
pv := New(mt)
pv.Elem().Set(v)
x = append(x, pv.Interface())
}
runtime.GC()
for i, xi := range x {
v := ValueOf(xi).Elem()
for j := 0; j < n; j++ {
k := v.MapIndex(ValueOf(j)).Elem().Interface().(uintptr)
if k != uintptr(i*n+j) {
t.Errorf("lost x[%d][%d] = %d, want %d", i, j, k, i*n+j)
}
}
}
}
type B1 struct {
......
......@@ -233,17 +233,17 @@ const (
// with a unique tag like `reflect:"array"` or `reflect:"ptr"`
// so that code cannot convert from, say, *arrayType to *ptrType.
type rtype struct {
size uintptr // size in bytes
hash uint32 // hash of type; avoids computation in hash tables
_ uint8 // unused/padding
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *uintptr // algorithm table (../runtime/runtime.h:/Alg)
gc uintptr // garbage collection data
string *string // string form; unnecessary but undeniably useful
*uncommonType // (relatively) uncommon fields
ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
size uintptr // size in bytes
hash uint32 // hash of type; avoids computation in hash tables
_ uint8 // unused/padding
align uint8 // alignment of variable with this type
fieldAlign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *uintptr // algorithm table (../runtime/runtime.h:/Alg)
gc unsafe.Pointer // garbage collection data
string *string // string form; unnecessary but undeniably useful
*uncommonType // (relatively) uncommon fields
ptrToThis *rtype // type for pointer to this type, if used in binary or has methods
}
// Method on non-interface type
......@@ -345,6 +345,25 @@ type structType struct {
fields []structField // sorted by offset
}
// NOTE: These are copied from ../runtime/mgc0.h.
// They must be kept in sync.
const (
_GC_END = iota
_GC_PTR
_GC_APTR
_GC_ARRAY_START
_GC_ARRAY_NEXT
_GC_CALL
_GC_MAP_PTR
_GC_CHAN_PTR
_GC_STRING
_GC_EFACE
_GC_IFACE
_GC_SLICE
_GC_REGION
_GC_NUM_INSTR
)
/*
* The compiler knows the exact layout of all the data structures above.
* The compiler does not know about the data structures and methods below.
......@@ -368,7 +387,10 @@ type Method struct {
// High bit says whether type has
// embedded pointers,to help garbage collector.
const kindMask = 0x7f
const (
kindMask = 0x7f
kindNoPointers = 0x80
)
func (k Kind) String() string {
if int(k) < len(kindNames) {
......@@ -978,6 +1000,32 @@ var ptrMap struct {
m map[*rtype]*ptrType
}
// garbage collection bytecode program for pointer to memory without pointers.
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
type ptrDataGC struct {
width uintptr // sizeof(ptr)
op uintptr // _GC_APTR
off uintptr // 0
end uintptr // _GC_END
}
var ptrDataGCProg = ptrDataGC{
width: unsafe.Sizeof((*byte)(nil)),
op: _GC_APTR,
off: 0,
end: _GC_END,
}
// garbage collection bytecode program for pointer to memory with pointers.
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
type ptrGC struct {
width uintptr // sizeof(ptr)
op uintptr // _GC_PTR
off uintptr // 0
elemgc unsafe.Pointer // element gc type
end uintptr // _GC_END
}
// PtrTo returns the pointer type with element t.
// For example, if t represents type Foo, PtrTo(t) represents *Foo.
func PtrTo(t Type) Type {
......@@ -1034,6 +1082,20 @@ func (t *rtype) ptrTo() *rtype {
p.ptrToThis = nil
p.elem = t
if t.kind&kindNoPointers != 0 {
p.gc = unsafe.Pointer(&ptrDataGCProg)
} else {
p.gc = unsafe.Pointer(&ptrGC{
width: p.size,
op: _GC_PTR,
off: 0,
elemgc: t.gc,
end: _GC_END,
})
}
// INCORRECT. Uncomment to check that TestPtrToGC fails when p.gc is wrong.
//p.gc = unsafe.Pointer(&badGC{width: p.size, end: _GC_END})
ptrMap.m[t] = p
ptrMap.Unlock()
return &p.rtype
......@@ -1338,6 +1400,21 @@ func cachePut(k cacheKey, t *rtype) Type {
return t
}
// garbage collection bytecode program for chan or map.
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
type chanMapGC struct {
width uintptr // sizeof(map)
op uintptr // _GC_MAP_PTR or _GC_CHAN_PTR
off uintptr // 0
typ *rtype // map type
end uintptr // _GC_END
}
type badGC struct {
width uintptr
end uintptr
}
// ChanOf returns the channel type with the given direction and element type.
// For example, if t represents int, ChanOf(RecvDir, t) represents <-chan int.
//
......@@ -1390,20 +1467,35 @@ func ChanOf(dir ChanDir, t Type) Type {
ch.uncommonType = nil
ch.ptrToThis = nil
ch.gc = unsafe.Pointer(&chanMapGC{
width: ch.size,
op: _GC_CHAN_PTR,
off: 0,
typ: &ch.rtype,
end: _GC_END,
})
// INCORRECT. Uncomment to check that TestChanOfGC fails when ch.gc is wrong.
//ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END})
return cachePut(ckey, &ch.rtype)
}
func ismapkey(*rtype) bool // implemented in runtime
// MapOf returns the map type with the given key and element types.
// For example, if k represents int and e represents string,
// MapOf(k, e) represents map[int]string.
//
// If the key type is not a valid map key type (that is, if it does
// not implement Go's == operator), MapOf panics. TODO(rsc).
// not implement Go's == operator), MapOf panics.
func MapOf(key, elem Type) Type {
ktyp := key.(*rtype)
etyp := elem.(*rtype)
// TODO: Check for invalid key types.
if !ismapkey(ktyp) {
panic("reflect.MapOf: invalid key type " + ktyp.String())
}
// Look in cache.
ckey := cacheKey{Map, ktyp, etyp, 0}
......@@ -1432,9 +1524,47 @@ func MapOf(key, elem Type) Type {
mt.uncommonType = nil
mt.ptrToThis = nil
mt.gc = unsafe.Pointer(&chanMapGC{
width: mt.size,
op: _GC_MAP_PTR,
off: 0,
typ: &mt.rtype,
end: _GC_END,
})
// INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues
// fail when mt.gc is wrong.
//mt.gc = unsafe.Pointer(&badGC{width: mt.size, end: _GC_END})
return cachePut(ckey, &mt.rtype)
}
// garbage collection bytecode program for slice of non-zero-length values.
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
type sliceGC struct {
width uintptr // sizeof(slice)
op uintptr // _GC_SLICE
off uintptr // 0
elemgc unsafe.Pointer // element gc program
end uintptr // _GC_END
}
// garbage collection bytecode program for slice of zero-length values.
// See ../../cmd/gc/reflect.c:/^dgcsym1 and :/^dgcsym.
type sliceEmptyGC struct {
width uintptr // sizeof(slice)
op uintptr // _GC_APTR
off uintptr // 0
end uintptr // _GC_END
}
var sliceEmptyGCProg = sliceEmptyGC{
width: unsafe.Sizeof([]byte(nil)),
op: _GC_APTR,
off: 0,
end: _GC_END,
}
// SliceOf returns the slice type with element type t.
// For example, if t represents int, SliceOf(t) represents []int.
func SliceOf(t Type) Type {
......@@ -1466,6 +1596,21 @@ func SliceOf(t Type) Type {
slice.uncommonType = nil
slice.ptrToThis = nil
if typ.size == 0 {
slice.gc = unsafe.Pointer(&sliceEmptyGCProg)
} else {
slice.gc = unsafe.Pointer(&sliceGC{
width: slice.size,
op: _GC_SLICE,
off: 0,
elemgc: typ.gc,
end: _GC_END,
})
}
// INCORRECT. Uncomment to check that TestSliceOfOfGC fails when slice.gc is wrong.
//slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END})
return cachePut(ckey, &slice.rtype)
}
......
......@@ -991,6 +991,13 @@ next:
/// interfaces to go runtime
//
void
reflect·ismapkey(Type *typ, bool ret)
{
ret = typ != nil && typ->alg->hash != runtime·nohash;
FLUSH(&ret);
}
Hmap*
runtime·makemap_c(MapType *typ, int64 hint)
{
......
......@@ -16,6 +16,9 @@
// len Length of an array
// elemsize Size (in bytes) of an element
// size Size (in bytes)
//
// NOTE: There is a copy of these in ../reflect/type.go.
// They must be kept in sync.
enum {
GC_END, // End of object, loop or subroutine. Args: none
GC_PTR, // A typed pointer. Args: (off, objgc)
......
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