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)
 	}
 }