@ -21,13 +21,10 @@ import (
"reflect"
"reflect"
"strings"
"strings"
"sync"
"sync"
"sync/atomic"
)
)
var (
// typeinfo is an entry in the type cache.
typeCacheMutex sync . RWMutex
typeCache = make ( map [ typekey ] * typeinfo )
)
type typeinfo struct {
type typeinfo struct {
decoder decoder
decoder decoder
decoderErr error // error from makeDecoder
decoderErr error // error from makeDecoder
@ -65,41 +62,76 @@ type decoder func(*Stream, reflect.Value) error
type writer func ( reflect . Value , * encbuf ) error
type writer func ( reflect . Value , * encbuf ) error
var theTC = newTypeCache ( )
type typeCache struct {
cur atomic . Value
// This lock synchronizes writers.
mu sync . Mutex
next map [ typekey ] * typeinfo
}
func newTypeCache ( ) * typeCache {
c := new ( typeCache )
c . cur . Store ( make ( map [ typekey ] * typeinfo ) )
return c
}
func cachedDecoder ( typ reflect . Type ) ( decoder , error ) {
func cachedDecoder ( typ reflect . Type ) ( decoder , error ) {
info := cachedTypeInfo ( typ , tags { } )
info := theTC . info ( typ )
return info . decoder , info . decoderErr
return info . decoder , info . decoderErr
}
}
func cachedWriter ( typ reflect . Type ) ( writer , error ) {
func cachedWriter ( typ reflect . Type ) ( writer , error ) {
info := cachedTypeInfo ( typ , tags { } )
info := theTC . info ( typ )
return info . writer , info . writerErr
return info . writer , info . writerErr
}
}
func cachedTypeInfo ( typ reflect . Type , tags tags ) * typeinfo {
func ( c * typeCache ) info ( typ reflect . Type ) * typeinfo {
typeCacheMutex . RLock ( )
key := typekey { Type : typ }
info := typeCache [ typekey { typ , tags } ]
if info := c . cur . Load ( ) . ( map [ typekey ] * typeinfo ) [ key ] ; info != nil {
typeCacheMutex . RUnlock ( )
if info != nil {
return info
return info
}
}
// not in the cache, need to generate info for this type.
typeCacheMutex . Lock ( )
// Not in the cache, need to generate info for this type.
defer typeCacheMutex . Unlock ( )
return c . generate ( typ , tags { } )
return cachedTypeInfo1 ( typ , tags )
}
func ( c * typeCache ) generate ( typ reflect . Type , tags tags ) * typeinfo {
c . mu . Lock ( )
defer c . mu . Unlock ( )
cur := c . cur . Load ( ) . ( map [ typekey ] * typeinfo )
if info := cur [ typekey { typ , tags } ] ; info != nil {
return info
}
// Copy cur to next.
c . next = make ( map [ typekey ] * typeinfo , len ( cur ) + 1 )
for k , v := range cur {
c . next [ k ] = v
}
// Generate.
info := c . infoWhileGenerating ( typ , tags )
// next -> cur
c . cur . Store ( c . next )
c . next = nil
return info
}
}
func cachedTypeInfo1 ( typ reflect . Type , tags tags ) * typeinfo {
func ( c * typeCache ) infoWhileGenerating ( typ reflect . Type , tags tags ) * typeinfo {
key := typekey { typ , tags }
key := typekey { typ , tags }
info := typeCache [ key ]
if info := c . next [ key ] ; info != nil {
if info != nil {
// another goroutine got the write lock first
return info
return info
}
}
// put a dummy value into the cache before generating.
// P ut a dummy value into the cache before generating.
// if the generator tries to lookup itself, it will get
// I f the generator tries to lookup itself, it will get
// the dummy value and won't call itself recursively.
// the dummy value and won't call itself recursively.
info = new ( typeinfo )
info : = new ( typeinfo )
typeCache [ key ] = info
c . next [ key ] = info
info . generate ( typ , tags )
info . generate ( typ , tags )
return info
return info
}
}
@ -133,7 +165,7 @@ func structFields(typ reflect.Type) (fields []field, err error) {
} else if anyOptional {
} else if anyOptional {
return nil , fmt . Errorf ( ` rlp: struct field %v.%s needs "optional" tag ` , typ , f . Name )
return nil , fmt . Errorf ( ` rlp: struct field %v.%s needs "optional" tag ` , typ , f . Name )
}
}
info := cachedTypeInfo1 ( f . Type , tags )
info := theTC . infoWhileGenerating ( f . Type , tags )
fields = append ( fields , field { i , info , tags . optional } )
fields = append ( fields , field { i , info , tags . optional } )
}
}
}
}