[VOL-4018] openonuAdapterGo - adapter crashes during AniG-OMCI-processing

Change-Id: If7702253d6ef24e0b79f8fb15a6b4a64fc27cfb2
diff --git a/internal/pkg/onuadaptercore/omci_ani_config.go b/internal/pkg/onuadaptercore/omci_ani_config.go
index c7433c8..423db39 100644
--- a/internal/pkg/onuadaptercore/omci_ani_config.go
+++ b/internal/pkg/onuadaptercore/omci_ani_config.go
@@ -125,6 +125,7 @@
 	tcont0ID                 uint16
 	alloc0ID                 uint16
 	gemPortAttribsSlice      []ponAniGemPortAttribs
+	mutexPLastTxMeInstance   sync.RWMutex
 	pLastTxMeInstance        *me.ManagedEntity
 	requestEventOffset       uint8 //used to indicate ConfigDone or Removed using successor (enum)
 	isWaitingForFlowDelete   bool
@@ -483,6 +484,8 @@
 		"EntitytId": strconv.FormatInt(int64(oFsm.mapperSP0ID), 16),
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
 	oFsm.requestEventOffset = 0 //0 offset for last config request activity
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
 	meInstance := oFsm.pOmciCC.sendCreateDot1PMapper(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 		oFsm.mapperSP0ID, oFsm.pAdaptFsm.commChan)
 	//accept also nil as (error) return value for writing to LastTx
@@ -505,6 +508,8 @@
 			"TpPointer":       oFsm.mapperSP0ID,
 		},
 	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
 	meInstance := oFsm.pOmciCC.sendCreateMBPConfigDataVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 		oFsm.pAdaptFsm.commChan, meParams)
 	//accept also nil as (error) return value for writing to LastTx
@@ -523,6 +528,8 @@
 			"AllocId": oFsm.alloc0ID,
 		},
 	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
 	meInstance := oFsm.pOmciCC.sendSetTcontVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 		oFsm.pAdaptFsm.commChan, meParams)
 	//accept also nil as (error) return value for writing to LastTx
@@ -647,6 +654,8 @@
 			}(pConfigAniStateAFsm)
 		}
 	} else {
+		oFsm.mutexPLastTxMeInstance.Lock()
+		defer oFsm.mutexPLastTxMeInstance.Unlock()
 		meInstance := oFsm.pOmciCC.sendSetDot1PMapperVar(context.TODO(), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 			oFsm.pAdaptFsm.commChan, meParams)
 		//accept also nil as (error) return value for writing to LastTx
@@ -715,6 +724,8 @@
 	oFsm.requestEventOffset = 1 //offset 1 to indicate last activity = remove
 
 	// this state entry is only expected in a suitable state (checked outside in onu_uni_tp)
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
 	meInstance := oFsm.pOmciCC.sendDeleteGemIWTP(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 		oFsm.pAdaptFsm.commChan, loGemPortID)
 	oFsm.pLastTxMeInstance = meInstance
@@ -798,9 +809,11 @@
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID,
 		"GemNCTP-entity-id": loGemPortID})
 	// this state entry is only expected in a suitable state (checked outside in onu_uni_tp)
+	oFsm.mutexPLastTxMeInstance.Lock()
 	meInstance := oFsm.pOmciCC.sendDeleteGemNCTP(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 		oFsm.pAdaptFsm.commChan, loGemPortID)
 	oFsm.pLastTxMeInstance = meInstance
+	oFsm.mutexPLastTxMeInstance.Unlock()
 	// Mark the gem port to be removed for Performance History monitoring
 	if oFsm.pDeviceHandler.pOnuMetricsMgr != nil {
 		oFsm.pDeviceHandler.pOnuMetricsMgr.RemoveGemPortForPerfMonitoring(loGemPortID)
@@ -819,6 +832,8 @@
 			"AllocId": unusedTcontAllocID,
 		},
 	}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
 	meInstance := oFsm.pOmciCC.sendSetTcontVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 		oFsm.pAdaptFsm.commChan, meParams)
 	oFsm.pLastTxMeInstance = meInstance
@@ -828,6 +843,8 @@
 	logger.Debugw(ctx, "uniPonAniConfigFsm - start deleting the .1pMapper", log.Fields{
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
 
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
 	meInstance := oFsm.pOmciCC.sendDeleteDot1PMapper(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 		oFsm.pAdaptFsm.commChan, oFsm.mapperSP0ID)
 	oFsm.pLastTxMeInstance = meInstance
@@ -837,6 +854,8 @@
 	logger.Debugw(ctx, "uniPonAniConfigFsm - start deleting the ANI MBCD", log.Fields{
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
 
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
 	meInstance := oFsm.pOmciCC.sendDeleteMBPConfigData(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 		oFsm.pAdaptFsm.commChan, oFsm.macBPCD0ID)
 	oFsm.pLastTxMeInstance = meInstance
@@ -894,6 +913,8 @@
 func (oFsm *uniPonAniConfigFsm) enterDisabledState(ctx context.Context, e *fsm.Event) {
 	logger.Debugw(ctx, "uniPonAniConfigFsm enters disabled state", log.Fields{
 		"device-id": oFsm.deviceID, "uni-id": oFsm.pOnuUniPort.uniID})
+	oFsm.mutexPLastTxMeInstance.Lock()
+	defer oFsm.mutexPLastTxMeInstance.Unlock()
 	oFsm.pLastTxMeInstance = nil
 }
 
@@ -949,27 +970,45 @@
 	logger.Debugw(ctx, "CreateResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
 	if msgObj.Result == me.Success || msgObj.Result == me.InstanceExists {
 		//if the result is ok or Instance already exists (latest needed at least as long as we do not clear the OMCI techProfile data)
-		if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
-			msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
-			// maybe we can use just the same eventName for different state transitions like "forward"
-			//   - might be checked, but so far I go for sure and have to inspect the concrete state events ...
-			switch oFsm.pLastTxMeInstance.GetName() {
-			case "Ieee8021PMapperServiceProfile":
-				{ // let the FSM proceed ...
-					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapCResp)
+		oFsm.mutexPLastTxMeInstance.RLock()
+		if oFsm.pLastTxMeInstance != nil {
+			if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+				msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+				// maybe we can use just the same eventName for different state transitions like "forward"
+				//   - might be checked, but so far I go for sure and have to inspect the concrete state events ...
+				switch oFsm.pLastTxMeInstance.GetName() {
+				case "Ieee8021PMapperServiceProfile":
+					{ // let the FSM proceed ...
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapCResp)
+					}
+				case "MacBridgePortConfigurationData":
+					{ // let the FSM proceed ...
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxMbpcdResp)
+					}
+				case "GemPortNetworkCtp", "GemInterworkingTerminationPoint", "MulticastGemInterworkingTerminationPoint":
+					{ // let aniConfig Multi-Id processing proceed by stopping the wait function
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						oFsm.omciMIdsResponseReceived <- true
+					}
+				default:
+					{
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						logger.Warnw(ctx, "Unsupported ME name received!",
+							log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+					}
 				}
-			case "MacBridgePortConfigurationData":
-				{ // let the FSM proceed ...
-					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxMbpcdResp)
-				}
-			case "GemPortNetworkCtp", "GemInterworkingTerminationPoint", "MulticastGemInterworkingTerminationPoint":
-				{ // let aniConfig Multi-Id processing proceed by stopping the wait function
-					oFsm.omciMIdsResponseReceived <- true
-				}
+			} else {
+				oFsm.mutexPLastTxMeInstance.RUnlock()
 			}
+		} else {
+			oFsm.mutexPLastTxMeInstance.RUnlock()
+			logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
 		}
 	} else {
-		logger.Errorw(ctx, "Omci CreateResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+		logger.Errorw(ctx, "Omci CreateResponse Error - later: drive FSM to abort state ?",
+			log.Fields{"Error": msgObj.Result, "device-id": oFsm.deviceID})
 		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
 		return
 	}
@@ -995,30 +1034,47 @@
 		// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
 		return
 	}
-	if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
-		msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
-		//store the created ME into DB //TODO??? obviously the Python code does not store the config ...
-		// if, then something like:
-		//oFsm.pOnuDB.StoreMe(msgObj)
+	oFsm.mutexPLastTxMeInstance.RLock()
+	if oFsm.pLastTxMeInstance != nil {
+		if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+			msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+			//store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+			// if, then something like:
+			//oFsm.pOnuDB.StoreMe(msgObj)
 
-		switch oFsm.pLastTxMeInstance.GetName() {
-		case "TCont":
-			{ // let the FSM proceed ...
-				if oFsm.requestEventOffset == 0 { //from TCont config request
-					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxTcontsResp)
-				} else { // from T-Cont reset request
-					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxResetTcontResp)
+			switch oFsm.pLastTxMeInstance.GetName() {
+			case "TCont":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					if oFsm.requestEventOffset == 0 { //from TCont config request
+						_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxTcontsResp)
+					} else { // from T-Cont reset request
+						_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxResetTcontResp)
+					}
+				}
+			case "PriorityQueue", "MulticastGemInterworkingTerminationPoint":
+				{ // let the PrioQueue init proceed by stopping the wait function
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					oFsm.omciMIdsResponseReceived <- true
+				}
+			case "Ieee8021PMapperServiceProfile":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapSResp)
+				}
+			default:
+				{
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					logger.Warnw(ctx, "Unsupported ME name received!",
+						log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
 				}
 			}
-		case "PriorityQueue", "MulticastGemInterworkingTerminationPoint":
-			{ // let the PrioQueue init proceed by stopping the wait function
-				oFsm.omciMIdsResponseReceived <- true
-			}
-		case "Ieee8021PMapperServiceProfile":
-			{ // let the FSM proceed ...
-				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxDot1pmapSResp)
-			}
+		} else {
+			oFsm.mutexPLastTxMeInstance.RUnlock()
 		}
+	} else {
+		oFsm.mutexPLastTxMeInstance.RUnlock()
+		logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
 	}
 }
 
@@ -1043,29 +1099,47 @@
 		//         store error for mgmt display?
 		return
 	}
-	if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
-		msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
-		//remove ME from DB //TODO??? obviously the Python code does not store/remove the config ...
-		// if, then something like: oFsm.pOnuDB.XyyMe(msgObj)
+	oFsm.mutexPLastTxMeInstance.RLock()
+	if oFsm.pLastTxMeInstance != nil {
+		if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+			msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+			//remove ME from DB //TODO??? obviously the Python code does not store/remove the config ...
+			// if, then something like: oFsm.pOnuDB.XyyMe(msgObj)
 
-		switch oFsm.pLastTxMeInstance.GetName() {
-		case "GemInterworkingTerminationPoint":
-			{ // let the FSM proceed ...
-				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxRemGemiwResp)
+			switch oFsm.pLastTxMeInstance.GetName() {
+			case "GemInterworkingTerminationPoint":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxRemGemiwResp)
+				}
+			case "GemPortNetworkCtp":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxRemGemntpResp)
+				}
+			case "Ieee8021PMapperServiceProfile":
+				{ // let the FSM proceed ...
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxRem1pMapperResp)
+				}
+			case "MacBridgePortConfigurationData":
+				{ // this is the last event of the T-Cont cleanup procedure, FSM may be reset here
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxRemAniBPCDResp)
+				}
+			default:
+				{
+					oFsm.mutexPLastTxMeInstance.RUnlock()
+					logger.Warnw(ctx, "Unsupported ME name received!",
+						log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+				}
 			}
-		case "GemPortNetworkCtp":
-			{ // let the FSM proceed ...
-				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxRemGemntpResp)
-			}
-		case "Ieee8021PMapperServiceProfile":
-			{ // let the FSM proceed ...
-				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxRem1pMapperResp)
-			}
-		case "MacBridgePortConfigurationData":
-			{ // this is the last event of the T-Cont cleanup procedure, FSM may be reset here
-				_ = oFsm.pAdaptFsm.pFsm.Event(aniEvRxRemAniBPCDResp)
-			}
+		} else {
+			oFsm.mutexPLastTxMeInstance.RUnlock()
 		}
+	} else {
+		oFsm.mutexPLastTxMeInstance.RUnlock()
+		logger.Warnw(ctx, "Pointer to last Tx MeInstance is nil!", log.Fields{"device-id": oFsm.deviceID})
 	}
 }
 
@@ -1117,11 +1191,13 @@
 				"PriorityQueuePointerForDownStream":   gemPortAttribs.downQueueID,
 			},
 		}
+		oFsm.mutexPLastTxMeInstance.Lock()
 		meInstance := oFsm.pOmciCC.sendCreateGemNCTPVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 			oFsm.pAdaptFsm.commChan, meParams)
 		//accept also nil as (error) return value for writing to LastTx
 		//  - this avoids misinterpretation of new received OMCI messages
 		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
 
 		//verify response
 		err := oFsm.waitforOmciResponse(ctx)
@@ -1162,9 +1238,11 @@
 					"GalProfilePointer":                    galEthernetEID,
 				},
 			}
+			oFsm.mutexPLastTxMeInstance.Lock()
 			meInstance := oFsm.pOmciCC.sendCreateMulticastGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout,
 				true, oFsm.pAdaptFsm.commChan, meParams)
 			oFsm.pLastTxMeInstance = meInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
 			//verify response
 			err := oFsm.waitforOmciResponse(ctx)
 			if err != nil {
@@ -1189,9 +1267,11 @@
 					"Ipv4MulticastAddressTable": ipv4MulticastTable,
 				},
 			}
+			oFsm.mutexPLastTxMeInstance.Lock()
 			meIPV4MCTableInstance := oFsm.pOmciCC.sendSetMulticastGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout,
 				true, oFsm.pAdaptFsm.commChan, meIPV4MCTableParams)
 			oFsm.pLastTxMeInstance = meIPV4MCTableInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
 
 		} else {
 			meParams := me.ParamData{
@@ -1204,11 +1284,13 @@
 					"GalProfilePointer":                    galEthernetEID,
 				},
 			}
+			oFsm.mutexPLastTxMeInstance.Lock()
 			meInstance := oFsm.pOmciCC.sendCreateGemIWTPVar(context.TODO(), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 				oFsm.pAdaptFsm.commChan, meParams)
 			//accept also nil as (error) return value for writing to LastTx
 			//  - this avoids misinterpretation of new received OMCI messages
 			oFsm.pLastTxMeInstance = meInstance
+			oFsm.mutexPLastTxMeInstance.Unlock()
 		}
 		//verify response
 		err := oFsm.waitforOmciResponse(ctx)
@@ -1278,11 +1360,13 @@
 			meParams.Attributes["TrafficSchedulerPointer"] = loTrafficSchedulerEID //ensure assignment of the relevant trafficScheduler
 			meParams.Attributes["Weight"] = uint8(kv.Value.(uint16))
 		}
+		oFsm.mutexPLastTxMeInstance.Lock()
 		meInstance := oFsm.pOmciCC.sendSetPrioQueueVar(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.pOpenOnuAc.omciTimeout, true,
 			oFsm.pAdaptFsm.commChan, meParams)
 		//accept also nil as (error) return value for writing to LastTx
 		//  - this avoids misinterpretation of new received OMCI messages
 		oFsm.pLastTxMeInstance = meInstance
+		oFsm.mutexPLastTxMeInstance.Unlock()
 
 		//verify response
 		err := oFsm.waitforOmciResponse(ctx)