From: Russ Cox Date: Tue, 26 Mar 2013 18:50:29 +0000 (-0700) Subject: reflect: add garbage collection info in ChanOf, MapOf, PtrTo, SliceOf X-Git-Tag: go1.1rc2~321 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=3660b532ac9f2393629744a9494aadffd8aee4c0;p=gostls13.git 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 --- diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 8b546e2356..d128bf1174 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -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)) { diff --git a/src/pkg/reflect/all_test.go b/src/pkg/reflect/all_test.go index 9a4dd6c315..56cb315ad6 100644 --- a/src/pkg/reflect/all_test.go +++ b/src/pkg/reflect/all_test.go @@ -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 { diff --git a/src/pkg/reflect/type.go b/src/pkg/reflect/type.go index 5ec94f576d..b513fee90b 100644 --- a/src/pkg/reflect/type.go +++ b/src/pkg/reflect/type.go @@ -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) } diff --git a/src/pkg/runtime/hashmap.c b/src/pkg/runtime/hashmap.c index 036af5a249..20087cf495 100644 --- a/src/pkg/runtime/hashmap.c +++ b/src/pkg/runtime/hashmap.c @@ -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) { diff --git a/src/pkg/runtime/mgc0.h b/src/pkg/runtime/mgc0.h index 18f3654b48..d14fb37c20 100644 --- a/src/pkg/runtime/mgc0.h +++ b/src/pkg/runtime/mgc0.h @@ -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)