@@ -21,13 +21,10 @@ import (
21
21
"reflect"
22
22
"strings"
23
23
"sync"
24
+ "sync/atomic"
24
25
)
25
26
26
- var (
27
- typeCacheMutex sync.RWMutex
28
- typeCache = make (map [typekey ]* typeinfo )
29
- )
30
-
27
+ // typeinfo is an entry in the type cache.
31
28
type typeinfo struct {
32
29
decoder decoder
33
30
decoderErr error // error from makeDecoder
@@ -65,41 +62,76 @@ type decoder func(*Stream, reflect.Value) error
65
62
66
63
type writer func (reflect.Value , * encbuf ) error
67
64
65
+ var theTC = newTypeCache ()
66
+
67
+ type typeCache struct {
68
+ cur atomic.Value
69
+
70
+ // This lock synchronizes writers.
71
+ mu sync.Mutex
72
+ next map [typekey ]* typeinfo
73
+ }
74
+
75
+ func newTypeCache () * typeCache {
76
+ c := new (typeCache )
77
+ c .cur .Store (make (map [typekey ]* typeinfo ))
78
+ return c
79
+ }
80
+
68
81
func cachedDecoder (typ reflect.Type ) (decoder , error ) {
69
- info := cachedTypeInfo (typ , tags {} )
82
+ info := theTC . info (typ )
70
83
return info .decoder , info .decoderErr
71
84
}
72
85
73
86
func cachedWriter (typ reflect.Type ) (writer , error ) {
74
- info := cachedTypeInfo (typ , tags {} )
87
+ info := theTC . info (typ )
75
88
return info .writer , info .writerErr
76
89
}
77
90
78
- func cachedTypeInfo (typ reflect.Type , tags tags ) * typeinfo {
79
- typeCacheMutex .RLock ()
80
- info := typeCache [typekey {typ , tags }]
81
- typeCacheMutex .RUnlock ()
82
- if info != nil {
91
+ func (c * typeCache ) info (typ reflect.Type ) * typeinfo {
92
+ key := typekey {Type : typ }
93
+ if info := c .cur .Load ().(map [typekey ]* typeinfo )[key ]; info != nil {
83
94
return info
84
95
}
85
- // not in the cache, need to generate info for this type.
86
- typeCacheMutex .Lock ()
87
- defer typeCacheMutex .Unlock ()
88
- return cachedTypeInfo1 (typ , tags )
96
+
97
+ // Not in the cache, need to generate info for this type.
98
+ return c .generate (typ , tags {})
99
+ }
100
+
101
+ func (c * typeCache ) generate (typ reflect.Type , tags tags ) * typeinfo {
102
+ c .mu .Lock ()
103
+ defer c .mu .Unlock ()
104
+
105
+ cur := c .cur .Load ().(map [typekey ]* typeinfo )
106
+ if info := cur [typekey {typ , tags }]; info != nil {
107
+ return info
108
+ }
109
+
110
+ // Copy cur to next.
111
+ c .next = make (map [typekey ]* typeinfo , len (cur )+ 1 )
112
+ for k , v := range cur {
113
+ c .next [k ] = v
114
+ }
115
+
116
+ // Generate.
117
+ info := c .infoWhileGenerating (typ , tags )
118
+
119
+ // next -> cur
120
+ c .cur .Store (c .next )
121
+ c .next = nil
122
+ return info
89
123
}
90
124
91
- func cachedTypeInfo1 (typ reflect.Type , tags tags ) * typeinfo {
125
+ func ( c * typeCache ) infoWhileGenerating (typ reflect.Type , tags tags ) * typeinfo {
92
126
key := typekey {typ , tags }
93
- info := typeCache [key ]
94
- if info != nil {
95
- // another goroutine got the write lock first
127
+ if info := c .next [key ]; info != nil {
96
128
return info
97
129
}
98
- // put a dummy value into the cache before generating.
99
- // if the generator tries to lookup itself, it will get
130
+ // Put a dummy value into the cache before generating.
131
+ // If the generator tries to lookup itself, it will get
100
132
// the dummy value and won't call itself recursively.
101
- info = new (typeinfo )
102
- typeCache [key ] = info
133
+ info : = new (typeinfo )
134
+ c . next [key ] = info
103
135
info .generate (typ , tags )
104
136
return info
105
137
}
@@ -133,7 +165,7 @@ func structFields(typ reflect.Type) (fields []field, err error) {
133
165
} else if anyOptional {
134
166
return nil , fmt .Errorf (`rlp: struct field %v.%s needs "optional" tag` , typ , f .Name )
135
167
}
136
- info := cachedTypeInfo1 (f .Type , tags )
168
+ info := theTC . infoWhileGenerating (f .Type , tags )
137
169
fields = append (fields , field {i , info , tags .optional })
138
170
}
139
171
}
0 commit comments