VOL-2726 Changed all usages of childTypesSingleton to use the provided getters and setters.
This makes map access thread-safe.
Change-Id: I5cc1c6fa5c074c3020a1e1e37e88d2456f81ad7f
diff --git a/db/model/child_type.go b/db/model/child_type.go
index c238432..a0e15ef 100644
--- a/db/model/child_type.go
+++ b/db/model/child_type.go
@@ -29,48 +29,22 @@
type childTypesSingleton struct {
mutex sync.RWMutex
- Cache map[interface{}]map[string]*ChildType
+ cache map[reflect.Type]map[string]*ChildType
}
-var instanceChildTypes *childTypesSingleton
-var onceChildTypes sync.Once
+var childTypes = &childTypesSingleton{cache: make(map[reflect.Type]map[string]*ChildType)}
-func getChildTypes() *childTypesSingleton {
- onceChildTypes.Do(func() {
- instanceChildTypes = &childTypesSingleton{}
- })
- return instanceChildTypes
-}
-
-func (s *childTypesSingleton) GetCache() map[interface{}]map[string]*ChildType {
+func (s *childTypesSingleton) GetCacheEntry(key reflect.Type) (map[string]*ChildType, bool) {
s.mutex.RLock()
defer s.mutex.RUnlock()
- return s.Cache
-}
-
-func (s *childTypesSingleton) SetCache(cache map[interface{}]map[string]*ChildType) {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- s.Cache = cache
-}
-
-func (s *childTypesSingleton) GetCacheEntry(key interface{}) (map[string]*ChildType, bool) {
- s.mutex.RLock()
- defer s.mutex.RUnlock()
- childTypeMap, exists := s.Cache[key]
+ childTypeMap, exists := s.cache[key]
return childTypeMap, exists
}
-func (s *childTypesSingleton) SetCacheEntry(key interface{}, value map[string]*ChildType) {
+func (s *childTypesSingleton) SetCacheEntry(key reflect.Type, value map[string]*ChildType) {
s.mutex.Lock()
defer s.mutex.Unlock()
- s.Cache[key] = value
-}
-
-func (s *childTypesSingleton) ResetCache() {
- s.mutex.Lock()
- defer s.mutex.Unlock()
- s.Cache = make(map[interface{}]map[string]*ChildType)
+ s.cache[key] = value
}
// ChildType structure contains construct details of an object
@@ -82,88 +56,80 @@
KeyFromStr func(s string) interface{}
}
-// ChildrenFields retrieves list of child objects associated to a given interface
+// ChildrenFields retrieves list of child objects associated to a given type
func ChildrenFields(cls interface{}) map[string]*ChildType {
if cls == nil {
return nil
}
- var names map[string]*ChildType
- var namesExist bool
-
- if getChildTypes().Cache == nil {
- getChildTypes().Cache = make(map[interface{}]map[string]*ChildType)
- }
msgType := reflect.TypeOf(cls)
- inst := getChildTypes()
- if names, namesExist = inst.Cache[msgType.String()]; !namesExist {
- names = make(map[string]*ChildType)
-
- _, md := desc.ForMessage(cls.(desc.Message))
-
- // TODO: Do we need to validate MD for nil, panic or exception?
- for _, field := range md.Field {
- if options := field.GetOptions(); options != nil {
- if proto.HasExtension(options, common.E_ChildNode) {
- isContainer := *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
- meta, _ := proto.GetExtension(options, common.E_ChildNode)
-
- var keyFromStr func(string) interface{}
- var ct ChildType
-
- parentType := FindOwnerType(reflect.ValueOf(cls), field.GetName(), 0, false)
- if meta.(*common.ChildNode).GetKey() != "" {
- keyType := FindKeyOwner(reflect.New(parentType).Elem().Interface(), meta.(*common.ChildNode).GetKey(), 0)
-
- switch keyType.(reflect.Type).Name() {
- case "string":
- keyFromStr = func(s string) interface{} {
- return s
- }
- case "int32":
- keyFromStr = func(s string) interface{} {
- i, _ := strconv.Atoi(s)
- return int32(i)
- }
- case "int64":
- keyFromStr = func(s string) interface{} {
- i, _ := strconv.Atoi(s)
- return int64(i)
- }
- case "uint32":
- keyFromStr = func(s string) interface{} {
- i, _ := strconv.Atoi(s)
- return uint32(i)
- }
- case "uint64":
- keyFromStr = func(s string) interface{} {
- i, _ := strconv.Atoi(s)
- return uint64(i)
- }
- default:
- log.Errorf("Key type not implemented - type: %s\n", keyType.(reflect.Type))
- }
- }
-
- ct = ChildType{
- ClassModule: parentType.String(),
- ClassType: parentType,
- IsContainer: isContainer,
- Key: meta.(*common.ChildNode).GetKey(),
- KeyFromStr: keyFromStr,
- }
-
- names[field.GetName()] = &ct
- }
- }
- }
-
- getChildTypes().Cache[msgType.String()] = names
- } else {
- entry, _ := inst.GetCacheEntry(msgType.String())
- log.Debugf("Cache entry for %s: %+v", msgType.String(), entry)
+ if fields, have := childTypes.GetCacheEntry(msgType); have {
+ return fields
}
- return names
+ fields := make(map[string]*ChildType)
+ _, md := desc.ForMessage(cls.(desc.Message))
+
+ // TODO: Do we need to validate MD for nil, panic or exception?
+ for _, field := range md.Field {
+ if options := field.GetOptions(); options != nil {
+ if proto.HasExtension(options, common.E_ChildNode) {
+ isContainer := *field.Label == descriptor.FieldDescriptorProto_LABEL_REPEATED
+ meta, _ := proto.GetExtension(options, common.E_ChildNode)
+
+ var keyFromStr func(string) interface{}
+ var ct ChildType
+
+ parentType := FindOwnerType(reflect.ValueOf(cls), field.GetName(), 0, false)
+ if meta.(*common.ChildNode).GetKey() != "" {
+ keyType := FindKeyOwner(reflect.New(parentType).Elem().Interface(), meta.(*common.ChildNode).GetKey(), 0)
+
+ switch keyType.(reflect.Type).Name() {
+ case "string":
+ keyFromStr = func(s string) interface{} {
+ return s
+ }
+ case "int32":
+ keyFromStr = func(s string) interface{} {
+ i, _ := strconv.Atoi(s)
+ return int32(i)
+ }
+ case "int64":
+ keyFromStr = func(s string) interface{} {
+ i, _ := strconv.Atoi(s)
+ return int64(i)
+ }
+ case "uint32":
+ keyFromStr = func(s string) interface{} {
+ i, _ := strconv.Atoi(s)
+ return uint32(i)
+ }
+ case "uint64":
+ keyFromStr = func(s string) interface{} {
+ i, _ := strconv.Atoi(s)
+ return uint64(i)
+ }
+ default:
+ log.Errorf("Key type not implemented - type: %s\n", keyType.(reflect.Type))
+ }
+ }
+
+ ct = ChildType{
+ ClassModule: parentType.String(),
+ ClassType: parentType,
+ IsContainer: isContainer,
+ Key: meta.(*common.ChildNode).GetKey(),
+ KeyFromStr: keyFromStr,
+ }
+
+ fields[field.GetName()] = &ct
+ }
+ }
+ }
+
+ // If called multiple times in quick succession w/ the same message types, it is possible for different cache entries to be returned.
+ // This should not be an issue, as the cache is merely for optimization purposes.
+ childTypes.SetCacheEntry(msgType, fields)
+ return fields
}
diff --git a/db/model/child_type_test.go b/db/model/child_type_test.go
index 5a64264..b57d988 100644
--- a/db/model/child_type_test.go
+++ b/db/model/child_type_test.go
@@ -39,10 +39,13 @@
// Verify that the cache contains an entry for types on which ChildrenFields was performed
func TestChildType_02_Cache_Keys(t *testing.T) {
- if _, exists := getChildTypes().Cache[reflect.TypeOf(&voltha.Device{}).String()]; !exists {
- t.Errorf("getChildTypeCache().Cache should have an entry of type: %+v\n", reflect.TypeOf(&voltha.Device{}).String())
+ childTypes.mutex.RLock()
+ defer childTypes.mutex.RUnlock()
+
+ if _, exists := childTypes.cache[reflect.TypeOf(&voltha.Device{})]; !exists {
+ t.Errorf("childTypes.cache should have an entry of type: %+v\n", reflect.TypeOf(&voltha.Device{}).String())
}
- for k := range getChildTypes().Cache {
- t.Logf("getChildTypeCache().Cache Key:%+v\n", k)
+ for k := range childTypes.cache {
+ t.Logf("childTypes.cache Key:%+v\n", k)
}
}