]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: add garbage collection info in ChanOf, MapOf, PtrTo, SliceOf
authorRuss Cox <rsc@golang.org>
Tue, 26 Mar 2013 18:50:29 +0000 (11:50 -0700)
committerRob Pike <r@golang.org>
Tue, 26 Mar 2013 18:50:29 +0000 (11:50 -0700)
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

src/cmd/gc/reflect.c
src/pkg/reflect/all_test.go
src/pkg/reflect/type.go
src/pkg/runtime/hashmap.c
src/pkg/runtime/mgc0.h

index 8b546e2356ecf0c0b6656ad89d78d7c8a0a4c9d9..d128bf1174931df85da3c236a53149a9acb0dd09 100644 (file)
@@ -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)) {
index 9a4dd6c3154e4aae6eb63762a396b5fc3b779f13..56cb315ad6d08013cb8017b8432e901de9b777ae 100644 (file)
@@ -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 {
index 5ec94f576d6883794c009dd139a23645fc3ac0a1..b513fee90bd17896c65c8381fe14767c4d4099fc 100644 (file)
@@ -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)
 }
 
index 036af5a249c18f398141216293d2f0851f678c5a..20087cf49533c0002f0412199e892b2cd576d482 100644 (file)
@@ -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)
 {
index 18f3654b48ddbebe14bf54337fd88155b7b341bf..d14fb37c2095bd7eb96a5e2fe58d50e34cc1681a 100644 (file)
@@ -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)