Added a helper type to more safely handle async request completion.

Fixes VOL-2286

Change-Id: Ifcbbfdf64c3614838adbbaa11ca69d3d49c44861
diff --git a/rw_core/core/device_manager.go b/rw_core/core/device_manager.go
index 56db2b1..ce7ecd9 100755
--- a/rw_core/core/device_manager.go
+++ b/rw_core/core/device_manager.go
@@ -594,13 +594,13 @@
 		return nil
 	}
 
-	chnlsList := make([]chan interface{}, 0)
+	responses := make([]utils.Response, 0)
 	for _, rootDeviceId := range rootDeviceIds {
 		if rootDevice, _ := dMgr.getDeviceFromModel(rootDeviceId); rootDevice != nil {
 			if rootDevice.Adapter == adapter.Id {
 				if isOkToReconcile(rootDevice) {
 					log.Debugw("reconciling-root-device", log.Fields{"rootId": rootDevice.Id})
-					chnlsList = dMgr.sendReconcileDeviceRequest(rootDevice, chnlsList)
+					responses = append(responses, dMgr.sendReconcileDeviceRequest(rootDevice))
 				} else {
 					log.Debugw("not-reconciling-root-device", log.Fields{"rootId": rootDevice.Id, "state": rootDevice.AdminState})
 				}
@@ -612,7 +612,7 @@
 							if childDevice.Adapter == adapter.Id {
 								if isOkToReconcile(childDevice) {
 									log.Debugw("reconciling-child-device", log.Fields{"childId": childDevice.Id})
-									chnlsList = dMgr.sendReconcileDeviceRequest(childDevice, chnlsList)
+									responses = append(responses, dMgr.sendReconcileDeviceRequest(childDevice))
 								} else {
 									log.Debugw("not-reconciling-child-device", log.Fields{"childId": childDevice.Id, "state": childDevice.AdminState})
 								}
@@ -627,9 +627,9 @@
 			}
 		}
 	}
-	if len(chnlsList) > 0 {
+	if len(responses) > 0 {
 		// Wait for completion
-		if res := utils.WaitForNilOrErrorResponses(dMgr.defaultTimeout, chnlsList...); res != nil {
+		if res := utils.WaitForNilOrErrorResponses(dMgr.defaultTimeout, responses...); res != nil {
 			return status.Errorf(codes.Aborted, "errors-%s", res)
 		}
 	} else {
@@ -638,36 +638,35 @@
 	return nil
 }
 
-func (dMgr *DeviceManager) sendReconcileDeviceRequest(device *voltha.Device, chnlsList []chan interface{}) []chan interface{} {
+func (dMgr *DeviceManager) sendReconcileDeviceRequest(device *voltha.Device) utils.Response {
 	// Send a reconcile request to the adapter. Since this Core may not be managing this device then there is no
 	// point of creating a device agent (if the device is not being managed by this Core) before sending the request
 	// to the adapter.   We will therefore bypass the adapter adapter and send the request directly to teh adapter via
 	// the adapter_proxy.
-	ch := make(chan interface{})
-	chnlsList = append(chnlsList, ch)
+	response := utils.NewResponse()
 	go func(device *voltha.Device) {
 		if err := dMgr.adapterProxy.ReconcileDevice(context.Background(), device); err != nil {
 			log.Errorw("reconcile-request-failed", log.Fields{"deviceId": device.Id, "error": err})
-			ch <- status.Errorf(codes.Internal, "device: %s", device.Id)
+			response.Error(status.Errorf(codes.Internal, "device: %s", device.Id))
 		}
-		ch <- nil
+		response.Done()
 	}(device)
 
-	return chnlsList
+	return response
 }
 
 func (dMgr *DeviceManager) reconcileChildDevices(parentDeviceId string) error {
 	if parentDevice, _ := dMgr.getDeviceFromModel(parentDeviceId); parentDevice != nil {
-		chnlsList := make([]chan interface{}, 0)
+		responses := make([]utils.Response, 0)
 		for _, port := range parentDevice.Ports {
 			for _, peer := range port.Peers {
 				if childDevice, _ := dMgr.getDeviceFromModel(peer.DeviceId); childDevice != nil {
-					chnlsList = dMgr.sendReconcileDeviceRequest(childDevice, chnlsList)
+					responses = append(responses, dMgr.sendReconcileDeviceRequest(childDevice))
 				}
 			}
 		}
 		// Wait for completion
-		if res := utils.WaitForNilOrErrorResponses(dMgr.defaultTimeout, chnlsList...); res != nil {
+		if res := utils.WaitForNilOrErrorResponses(dMgr.defaultTimeout, responses...); res != nil {
 			return status.Errorf(codes.Aborted, "errors-%s", res)
 		}
 	}