diff --git a/modules/tbl/CMakeLists.txt b/modules/tbl/CMakeLists.txt index b47e7c045..5e7427118 100644 --- a/modules/tbl/CMakeLists.txt +++ b/modules/tbl/CMakeLists.txt @@ -10,6 +10,8 @@ project(CFE_TBL C) set(tbl_SOURCES fsw/src/cfe_tbl_api.c fsw/src/cfe_tbl_internal.c + fsw/src/cfe_tbl_resource.c + fsw/src/cfe_tbl_transaction.c fsw/src/cfe_tbl_task.c fsw/src/cfe_tbl_task_cmds.c fsw/src/cfe_tbl_dispatch.c diff --git a/modules/tbl/fsw/src/cfe_tbl_api.c b/modules/tbl/fsw/src/cfe_tbl_api.c index 5333557b7..013f2e036 100644 --- a/modules/tbl/fsw/src/cfe_tbl_api.c +++ b/modules/tbl/fsw/src/cfe_tbl_api.c @@ -47,10 +47,10 @@ CFE_Status_t CFE_TBL_Register(CFE_TBL_Handle_t *TblHandlePtr, const char *Name, size_t Size, uint16 TblOptionFlags, CFE_TBL_CallbackFuncPtr_t TblValidationFuncPtr) { + CFE_TBL_TxnState_t Txn; CFE_TBL_RegistryRec_t *RegRecPtr = NULL; CFE_TBL_CritRegRec_t * CritRegRecPtr = NULL; CFE_Status_t Status; - int16 RegIndx; CFE_ES_AppId_t ThisAppId; char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; char TblName[CFE_TBL_MAX_FULL_NAME_LEN] = {""}; @@ -61,14 +61,13 @@ CFE_Status_t CFE_TBL_Register(CFE_TBL_Handle_t *TblHandlePtr, const char *Name, } /* Check to make sure calling application is legit */ - Status = CFE_ES_GetAppID(&ThisAppId); + Status = CFE_TBL_TxnInit(&Txn, true); + + ThisAppId = CFE_TBL_TxnAppId(&Txn); - /* Validate table input parameters (Name, Size and Options) */ if (Status == CFE_SUCCESS) { - /* Assume we can't make a table and return a bad handle for now */ - *TblHandlePtr = CFE_TBL_BAD_TABLE_HANDLE; - + /* Validate table input parameters (Name, Size and Options) */ Status = CFE_TBL_ValidateTableName(Name); if (Status == CFE_SUCCESS) @@ -94,29 +93,22 @@ CFE_Status_t CFE_TBL_Register(CFE_TBL_Handle_t *TblHandlePtr, const char *Name, { /* Lock Registry for update. This prevents two applications from */ /* trying to register/share tables at the same location at the same time */ - CFE_TBL_LockRegistry(); + CFE_TBL_TxnLockRegistry(&Txn); - Status = CFE_TBL_CheckForDuplicateRegistration(&RegIndx, TblName, RegRecPtr, ThisAppId, Size, TblHandlePtr); + Status = CFE_TBL_TxnCheckDuplicateRegistration(&Txn, TblName, Size); /* In error conditions or if this is a duplicate registration, no further work is required */ if (Status == CFE_SUCCESS) { /* Search Access Descriptor Array for free Descriptor */ - *TblHandlePtr = CFE_TBL_FindFreeHandle(); - - /* Check to make sure there was a handle available */ - if (*TblHandlePtr == CFE_TBL_END_OF_LIST) - { - Status = CFE_TBL_ERR_HANDLES_FULL; - CFE_ES_WriteToSysLog("%s: No more free handles\n", __func__); - } + Status = CFE_TBL_TxnAllocateHandle(&Txn); } /* If no errors, initialize the table registry entry and return the index to the caller as the handle */ if (Status == CFE_SUCCESS) { /* Get pointer to Registry Record Entry to speed up processing */ - RegRecPtr = &CFE_TBL_Global.Registry[RegIndx]; + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); /* Initialize Registry Record to default settings */ CFE_TBL_InitRegistryRecord(RegRecPtr); @@ -150,7 +142,7 @@ CFE_Status_t CFE_TBL_Register(CFE_TBL_Handle_t *TblHandlePtr, const char *Name, { CFE_TBL_InitTableRegistryEntry(RegRecPtr, Size, TblValidationFuncPtr, TblName, TblOptionFlags); - CFE_TBL_InitTableAccessDescriptor(TblHandlePtr, ThisAppId, RegRecPtr, RegIndx); + CFE_TBL_TxnConnectAccessDescriptor(&Txn); /* If the table is a critical table, allocate space for it in the Critical Data Store */ /* OR locate its previous incarnation there and extract its previous contents */ @@ -189,7 +181,7 @@ CFE_Status_t CFE_TBL_Register(CFE_TBL_Handle_t *TblHandlePtr, const char *Name, } /* Unlock Registry for update */ - CFE_TBL_UnlockRegistry(); + CFE_TBL_TxnUnlockRegistry(&Txn); } /* On Error conditions, notify ground of screw up */ @@ -202,6 +194,10 @@ CFE_Status_t CFE_TBL_Register(CFE_TBL_Handle_t *TblHandlePtr, const char *Name, CFE_EVS_SendEventWithAppID(CFE_TBL_REGISTER_ERR_EID, CFE_EVS_EventType_ERROR, CFE_TBL_Global.TableTaskAppId, "%s Failed to Register '%s', Status=0x%08X", AppName, TblName, (unsigned int)Status); } + else + { + *TblHandlePtr = CFE_TBL_TxnHandle(&Txn); + } return Status; } @@ -214,9 +210,9 @@ CFE_Status_t CFE_TBL_Register(CFE_TBL_Handle_t *TblHandlePtr, const char *Name, *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_Share(CFE_TBL_Handle_t *TblHandlePtr, const char *TblName) { + CFE_TBL_TxnState_t Txn; int32 Status; CFE_ES_AppId_t ThisAppId; - int16 RegIndx; CFE_TBL_AccessDescriptor_t *AccessDescPtr = NULL; CFE_TBL_RegistryRec_t * RegRecPtr = NULL; char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; @@ -226,83 +222,69 @@ CFE_Status_t CFE_TBL_Share(CFE_TBL_Handle_t *TblHandlePtr, const char *TblName) return CFE_TBL_BAD_ARGUMENT; } - /* Get a valid Application ID for calling App */ - Status = CFE_ES_GetAppID(&ThisAppId); + Status = CFE_TBL_TxnStartFromName(&Txn, TblName, CFE_TBL_TxnContext_OTHER_APP); + + ThisAppId = CFE_TBL_TxnAppId(&Txn); if (Status == CFE_SUCCESS) { - /* Lock Registry for update. This prevents two applications from */ - /* trying to register/share tables at the same location at the same time */ - CFE_TBL_LockRegistry(); + /* Search Access Descriptor Array for free Descriptor */ + Status = CFE_TBL_TxnAllocateHandle(&Txn); - RegIndx = CFE_TBL_FindTableInRegistry(TblName); - - /* If we found the table, then get a new Access Descriptor and initialize it */ - if (RegIndx != CFE_TBL_NOT_FOUND) + /* Check to make sure there was a handle available */ + if (Status == CFE_SUCCESS) { - /* Get pointer to Registry Record Entry to speed up processing */ - RegRecPtr = &CFE_TBL_Global.Registry[RegIndx]; + /* Initialize the Table Access Descriptor */ + AccessDescPtr = CFE_TBL_TxnAccDesc(&Txn); + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); - /* Search Access Descriptor Array for free Descriptor */ - *TblHandlePtr = CFE_TBL_FindFreeHandle(); + AccessDescPtr->AppId = ThisAppId; + AccessDescPtr->LockFlag = false; + AccessDescPtr->Updated = false; - /* Check to make sure there was a handle available */ - if (*TblHandlePtr == CFE_TBL_END_OF_LIST) + /* Check current state of table in order to set Notification flags properly */ + if (RegRecPtr->TableLoadedOnce) { - Status = CFE_TBL_ERR_HANDLES_FULL; - CFE_ES_WriteToSysLog("%s: No more free handles\n", __func__); + AccessDescPtr->Updated = true; } - else - { - /* Initialize the Table Access Descriptor */ - AccessDescPtr = &CFE_TBL_Global.Handles[*TblHandlePtr]; - - AccessDescPtr->AppId = ThisAppId; - AccessDescPtr->LockFlag = false; - AccessDescPtr->Updated = false; - - /* Check current state of table in order to set Notification flags properly */ - if (RegRecPtr->TableLoadedOnce) - { - AccessDescPtr->Updated = true; - } - AccessDescPtr->RegIndex = RegIndx; - AccessDescPtr->UsedFlag = true; + AccessDescPtr->RegIndex = CFE_TBL_TxnRegId(&Txn); + AccessDescPtr->UsedFlag = true; - AccessDescPtr->PrevLink = CFE_TBL_END_OF_LIST; /* We are the new head of the list */ - AccessDescPtr->NextLink = RegRecPtr->HeadOfAccessList; + AccessDescPtr->PrevLink = CFE_TBL_END_OF_LIST; /* We are the new head of the list */ + AccessDescPtr->NextLink = RegRecPtr->HeadOfAccessList; - /* Make sure the old head of the list now sees this as the head */ - CFE_TBL_Global.Handles[RegRecPtr->HeadOfAccessList].PrevLink = *TblHandlePtr; - - /* Make sure the Registry Record see this as the head of the list */ - RegRecPtr->HeadOfAccessList = *TblHandlePtr; - } - } - else /* Table could not be found in registry */ - { - Status = CFE_TBL_ERR_INVALID_NAME; + /* Make sure the old head of the list now sees this as the head */ + CFE_TBL_Global.Handles[RegRecPtr->HeadOfAccessList].PrevLink = CFE_TBL_TxnHandle(&Txn); - CFE_ES_WriteToSysLog("%s: Table '%s' not found in Registry\n", __func__, TblName); + /* Make sure the Registry Record see this as the head of the list */ + RegRecPtr->HeadOfAccessList = CFE_TBL_TxnHandle(&Txn); } - CFE_TBL_UnlockRegistry(); - } - else /* Application ID was invalid */ - { - CFE_ES_WriteToSysLog("%s: Bad AppId(%lu)\n", __func__, CFE_RESOURCEID_TO_ULONG(ThisAppId)); + CFE_TBL_TxnFinish(&Txn); } /* On Error conditions, notify ground of screw up */ if (Status < 0) { + *TblHandlePtr = CFE_TBL_BAD_TABLE_HANDLE; + + if (Status == CFE_TBL_ERR_INVALID_NAME) + { + CFE_ES_WriteToSysLog("%s: Table '%s' not found in Registry\n", __func__, TblName); + } + /* Translate AppID of caller into App Name */ CFE_ES_GetAppName(AppName, ThisAppId, sizeof(AppName)); CFE_EVS_SendEventWithAppID(CFE_TBL_SHARE_ERR_EID, CFE_EVS_EventType_ERROR, CFE_TBL_Global.TableTaskAppId, "%s Failed to Share '%s', Status=0x%08X", AppName, TblName, (unsigned int)Status); } + else + { + /* Export handle to caller */ + *TblHandlePtr = CFE_TBL_TxnHandle(&Txn); + } return Status; } @@ -315,22 +297,20 @@ CFE_Status_t CFE_TBL_Share(CFE_TBL_Handle_t *TblHandlePtr, const char *TblName) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_Unregister(CFE_TBL_Handle_t TblHandle) { - int32 Status; - CFE_ES_AppId_t ThisAppId; - CFE_TBL_RegistryRec_t * RegRecPtr = NULL; - CFE_TBL_AccessDescriptor_t *AccessDescPtr = NULL; - char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_ES_AppId_t ThisAppId; + CFE_TBL_RegistryRec_t *RegRecPtr = NULL; + char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; /* Verify that this application has the right to perform operation */ - Status = CFE_TBL_ValidateAccess(TblHandle, &ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_ACCESSOR_APP); if (Status == CFE_SUCCESS) { - /* Get a pointer to the relevant Access Descriptor */ - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - /* Get a pointer to the relevant entry in the registry */ - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); + ThisAppId = CFE_TBL_TxnAppId(&Txn); /* Verify that the application unregistering the table owns the table */ if (CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, ThisAppId)) @@ -349,7 +329,9 @@ CFE_Status_t CFE_TBL_Unregister(CFE_TBL_Handle_t TblHandle) /* Remove the Access Descriptor Link from linked list */ /* NOTE: If this removes the last access link, then */ /* memory buffers are set free as well. */ - CFE_TBL_RemoveAccessLink(TblHandle); + CFE_TBL_TxnRemoveAccessLink(&Txn); + + CFE_TBL_TxnFinish(&Txn); } else { @@ -378,6 +360,7 @@ CFE_Status_t CFE_TBL_Unregister(CFE_TBL_Handle_t TblHandle) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_Load(CFE_TBL_Handle_t TblHandle, CFE_TBL_SrcEnum_t SrcType, const void *SrcDataPtr) { + CFE_TBL_TxnState_t Txn; int32 Status; CFE_ES_AppId_t ThisAppId; CFE_TBL_LoadBuff_t * WorkingBufferPtr; @@ -392,7 +375,7 @@ CFE_Status_t CFE_TBL_Load(CFE_TBL_Handle_t TblHandle, CFE_TBL_SrcEnum_t SrcType, } /* Verify access rights and get a valid Application ID for calling App */ - Status = CFE_TBL_ValidateAccess(TblHandle, &ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_OWNER_APP); if (Status != CFE_SUCCESS) { @@ -403,8 +386,17 @@ CFE_Status_t CFE_TBL_Load(CFE_TBL_Handle_t TblHandle, CFE_TBL_SrcEnum_t SrcType, return Status; } - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + AccessDescPtr = CFE_TBL_TxnAccDesc(&Txn); + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); + + /* + * This is not the end of the transaction - this is just put here for now + * until the many inline "return" statements in this function can be cleaned up. + * + * This means nearly everything is subject to race conditions, but it is no worse + * than it had been before. + */ + CFE_TBL_TxnFinish(&Txn); /* Translate AppID of caller into App Name */ CFE_ES_GetAppName(AppName, ThisAppId, sizeof(AppName)); @@ -607,6 +599,7 @@ CFE_Status_t CFE_TBL_Load(CFE_TBL_Handle_t TblHandle, CFE_TBL_SrcEnum_t SrcType, *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_Update(CFE_TBL_Handle_t TblHandle) { + CFE_TBL_TxnState_t Txn; int32 Status; CFE_ES_AppId_t ThisAppId; CFE_TBL_RegistryRec_t * RegRecPtr = NULL; @@ -614,16 +607,18 @@ CFE_Status_t CFE_TBL_Update(CFE_TBL_Handle_t TblHandle) char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; /* Verify access rights and get a valid Application ID for calling App */ - Status = CFE_TBL_ValidateAccess(TblHandle, &ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_OWNER_APP); if (Status == CFE_SUCCESS) { /* Get pointers to pertinent records in registry and handles */ - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + AccessDescPtr = CFE_TBL_TxnAccDesc(&Txn); + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); Status = CFE_TBL_UpdateInternal(TblHandle, RegRecPtr, AccessDescPtr); + CFE_TBL_TxnFinish(&Txn); + if (Status != CFE_SUCCESS) { CFE_ES_WriteToSysLog("%s: App(%lu) fail to update Tbl '%s' (Stat=0x%08X)\n", __func__, @@ -678,8 +673,9 @@ CFE_Status_t CFE_TBL_Update(CFE_TBL_Handle_t TblHandle) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_GetAddress(void **TblPtr, CFE_TBL_Handle_t TblHandle) { - int32 Status; - CFE_ES_AppId_t ThisAppId; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_ES_AppId_t ThisAppId; if (TblPtr == NULL) { @@ -689,19 +685,22 @@ CFE_Status_t CFE_TBL_GetAddress(void **TblPtr, CFE_TBL_Handle_t TblHandle) /* Assume failure at returning the table address */ *TblPtr = NULL; - /* Validate the calling application's AppID */ - Status = CFE_ES_GetAppID(&ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_ACCESSOR_APP); if (Status == CFE_SUCCESS) { - Status = CFE_TBL_GetAddressInternal(TblPtr, TblHandle, ThisAppId); + Status = CFE_TBL_TxnGetTableAddress(&Txn, TblPtr); /* NOTE: GetAddressInternal calls GetNextNotification which may not */ /* be equal to CFE_SUCCESS and still not be an error. */ /* Therefore, a write to the SysLog is unnecessary. */ + + CFE_TBL_TxnFinish(&Txn); } else { + ThisAppId = CFE_TBL_TxnAppId(&Txn); + CFE_ES_WriteToSysLog("%s: Bad AppId=%lu\n", __func__, CFE_RESOURCEID_TO_ULONG(ThisAppId)); } @@ -716,11 +715,12 @@ CFE_Status_t CFE_TBL_GetAddress(void **TblPtr, CFE_TBL_Handle_t TblHandle) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_ReleaseAddress(CFE_TBL_Handle_t TblHandle) { - int32 Status; - CFE_ES_AppId_t ThisAppId; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_ES_AppId_t ThisAppId; /* Verify that this application has the right to perform operation */ - Status = CFE_TBL_ValidateAccess(TblHandle, &ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_ACCESSOR_APP); if (Status == CFE_SUCCESS) { @@ -728,14 +728,18 @@ CFE_Status_t CFE_TBL_ReleaseAddress(CFE_TBL_Handle_t TblHandle) CFE_TBL_Global.Handles[TblHandle].LockFlag = false; /* Return any pending warning or info status indicators */ - Status = CFE_TBL_GetNextNotification(TblHandle); + Status = CFE_TBL_TxnGetNextNotification(&Txn); /* NOTE: GetNextNotification may not return CFE_SUCCESS */ /* and still not be an error. */ /* Therefore, a write to the SysLog is unnecessary.*/ + + CFE_TBL_TxnFinish(&Txn); } else { + ThisAppId = CFE_TBL_TxnAppId(&Txn); + CFE_ES_WriteToSysLog("%s: App(%lu) does not have access to Tbl Handle=%u\n", __func__, CFE_RESOURCEID_TO_ULONG(ThisAppId), (unsigned int)TblHandle); } @@ -751,9 +755,11 @@ CFE_Status_t CFE_TBL_ReleaseAddress(CFE_TBL_Handle_t TblHandle) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_GetAddresses(void **TblPtrs[], uint16 NumTables, const CFE_TBL_Handle_t TblHandles[]) { - uint16 i; - int32 Status; - CFE_ES_AppId_t ThisAppId; + CFE_TBL_TxnState_t Txn; + CFE_Status_t FinalStatus; + uint16 i; + int32 Status; + CFE_ES_AppId_t ThisAppId; if (TblPtrs == NULL || TblHandles == NULL) { @@ -766,32 +772,31 @@ CFE_Status_t CFE_TBL_GetAddresses(void **TblPtrs[], uint16 NumTables, const CFE_ *TblPtrs[i] = NULL; } - /* Validate the calling application's AppID */ - Status = CFE_ES_GetAppID(&ThisAppId); - - if (Status == CFE_SUCCESS) + FinalStatus = CFE_SUCCESS; + for (i = 0; i < NumTables; i++) { - for (i = 0; i < NumTables; i++) + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandles[i], CFE_TBL_TxnContext_ACCESSOR_APP); + if (Status == CFE_SUCCESS) { - /* Continue to get the return status until one returns something other than CFE_SUCCESS */ - if (Status == CFE_SUCCESS) - { - Status = CFE_TBL_GetAddressInternal(TblPtrs[i], TblHandles[i], ThisAppId); - } - else - { - /* Don't bother getting the status of other tables once one has returned */ - /* a non CFE_SUCCESS value. */ - CFE_TBL_GetAddressInternal(TblPtrs[i], TblHandles[i], ThisAppId); - } + Status = CFE_TBL_TxnGetTableAddress(&Txn, TblPtrs[i]); + + CFE_TBL_TxnFinish(&Txn); + } + + if (FinalStatus == CFE_SUCCESS) + { + FinalStatus = Status; + } + + if (Status == CFE_ES_ERR_RESOURCEID_NOT_VALID) + { + ThisAppId = CFE_TBL_TxnAppId(&Txn); + CFE_ES_WriteToSysLog("%s: Bad AppId=%lu\n", __func__, CFE_RESOURCEID_TO_ULONG(ThisAppId)); + break; } - } - else - { - CFE_ES_WriteToSysLog("%s: Bad AppId=%lu\n", __func__, CFE_RESOURCEID_TO_ULONG(ThisAppId)); } - return Status; + return FinalStatus; } /*---------------------------------------------------------------- @@ -836,20 +841,21 @@ CFE_Status_t CFE_TBL_ReleaseAddresses(uint16 NumTables, const CFE_TBL_Handle_t T *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_Validate(CFE_TBL_Handle_t TblHandle) { - int32 Status; - CFE_ES_AppId_t ThisAppId; - CFE_TBL_RegistryRec_t * RegRecPtr; - CFE_TBL_AccessDescriptor_t *AccessDescPtr; - char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_ES_AppId_t ThisAppId; + CFE_TBL_RegistryRec_t *RegRecPtr; + char AppName[OS_MAX_API_NAME] = {"UNKNOWN"}; /* Verify that this application has the right to perform operation */ - Status = CFE_TBL_ValidateAccess(TblHandle, &ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_OWNER_APP); if (Status == CFE_SUCCESS) { /* Get pointers to pertinent records in registry and handles */ - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); + + CFE_TBL_TxnFinish(&Txn); CFE_ES_GetAppName(AppName, ThisAppId, sizeof(AppName)); @@ -1040,37 +1046,23 @@ CFE_Status_t CFE_TBL_Manage(CFE_TBL_Handle_t TblHandle) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_GetStatus(CFE_TBL_Handle_t TblHandle) { - int32 Status; - CFE_ES_AppId_t ThisAppId; - CFE_TBL_RegistryRec_t * RegRecPtr; - CFE_TBL_AccessDescriptor_t *AccessDescPtr; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_ES_AppId_t ThisAppId; /* Verify that this application has the right to perform operation */ - Status = CFE_TBL_ValidateAccess(TblHandle, &ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_ACCESSOR_APP); if (Status == CFE_SUCCESS) { - /* Get pointers to pertinent records in registry and handles */ - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + Status = CFE_TBL_TxnGetTableStatus(&Txn); - /* Perform validations prior to performing any updates */ - if (RegRecPtr->LoadPending) - { - Status = CFE_TBL_INFO_UPDATE_PENDING; - } - else if ((RegRecPtr->ValidateActiveIndex != CFE_TBL_NO_VALIDATION_PENDING) || - (RegRecPtr->ValidateInactiveIndex != CFE_TBL_NO_VALIDATION_PENDING)) - { - Status = CFE_TBL_INFO_VALIDATION_PENDING; - } - else if (RegRecPtr->DumpControlIndex != CFE_TBL_NO_DUMP_PENDING) - { - Status = CFE_TBL_INFO_DUMP_PENDING; - } + CFE_TBL_TxnFinish(&Txn); } else { + ThisAppId = CFE_TBL_TxnAppId(&Txn); + CFE_ES_WriteToSysLog("%s: App(%lu) does not have access to Tbl Handle=%d\n", __func__, CFE_RESOURCEID_TO_ULONG(ThisAppId), (int)TblHandle); } @@ -1086,8 +1078,8 @@ CFE_Status_t CFE_TBL_GetStatus(CFE_TBL_Handle_t TblHandle) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_GetInfo(CFE_TBL_Info_t *TblInfoPtr, const char *TblName) { - int32 Status = CFE_SUCCESS; - int16 RegIndx; + CFE_TBL_TxnState_t Txn; + int32 Status = CFE_SUCCESS; int32 NumAccessDescriptors = 0; CFE_TBL_RegistryRec_t *RegRecPtr; CFE_TBL_Handle_t HandleIterator; @@ -1097,13 +1089,13 @@ CFE_Status_t CFE_TBL_GetInfo(CFE_TBL_Info_t *TblInfoPtr, const char *TblName) return CFE_TBL_BAD_ARGUMENT; } - RegIndx = CFE_TBL_FindTableInRegistry(TblName); + Status = CFE_TBL_TxnStartFromName(&Txn, TblName, CFE_TBL_TxnContext_UNDEFINED); /* If we found the table, then extract the information from the Registry */ - if (RegIndx != CFE_TBL_NOT_FOUND) + if (Status == CFE_SUCCESS) { /* Get pointer to Registry Record Entry to speed up processing */ - RegRecPtr = &CFE_TBL_Global.Registry[RegIndx]; + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); /* Return table characteristics */ TblInfoPtr->Size = RegRecPtr->Size; @@ -1131,10 +1123,8 @@ CFE_Status_t CFE_TBL_GetInfo(CFE_TBL_Info_t *TblInfoPtr, const char *TblName) TblInfoPtr->NumUsers = NumAccessDescriptors; TblInfoPtr->Critical = RegRecPtr->CriticalTable; - } - else - { - Status = CFE_TBL_ERR_INVALID_NAME; + + CFE_TBL_TxnFinish(&Txn); } return Status; @@ -1148,19 +1138,35 @@ CFE_Status_t CFE_TBL_GetInfo(CFE_TBL_Info_t *TblInfoPtr, const char *TblName) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_DumpToBuffer(CFE_TBL_Handle_t TblHandle) { - int32 Status; - CFE_TBL_AccessDescriptor_t *AccessDescPtr = NULL; - CFE_TBL_RegistryRec_t * RegRecPtr = NULL; - CFE_TBL_DumpControl_t * DumpCtrlPtr = NULL; - CFE_TIME_SysTime_t DumpTime; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_TBL_RegistryRec_t *RegRecPtr = NULL; + CFE_TBL_DumpControl_t *DumpCtrlPtr = NULL; + CFE_TIME_SysTime_t DumpTime; + CFE_ES_AppId_t ThisAppId; + + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_ACCESSOR_APP); + + if (Status == CFE_SUCCESS) + { + Status = CFE_TBL_TxnGetTableStatus(&Txn); + + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); + + CFE_TBL_TxnFinish(&Txn); + } + else + { + ThisAppId = CFE_TBL_TxnAppId(&Txn); + + CFE_ES_WriteToSysLog("%s: App(%lu) does not have access to Tbl Handle=%d\n", __func__, + CFE_RESOURCEID_TO_ULONG(ThisAppId), (int)TblHandle); + } /* Make sure the table has been requested to be dumped */ - Status = CFE_TBL_GetStatus(TblHandle); if (Status == CFE_TBL_INFO_DUMP_PENDING) { - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; - DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[RegRecPtr->DumpControlIndex]; + DumpCtrlPtr = &CFE_TBL_Global.DumpControlBlocks[RegRecPtr->DumpControlIndex]; /* Copy the contents of the active buffer to the assigned dump buffer */ memcpy(DumpCtrlPtr->DumpBufferPtr->BufferPtr, RegRecPtr->Buffers[0].BufferPtr, DumpCtrlPtr->Size); @@ -1190,21 +1196,23 @@ CFE_Status_t CFE_TBL_DumpToBuffer(CFE_TBL_Handle_t TblHandle) *-----------------------------------------------------------------*/ CFE_Status_t CFE_TBL_Modified(CFE_TBL_Handle_t TblHandle) { - int32 Status; - CFE_TBL_AccessDescriptor_t *AccessDescPtr = NULL; - CFE_TBL_RegistryRec_t * RegRecPtr = NULL; - CFE_TBL_Handle_t AccessIterator; - CFE_ES_AppId_t ThisAppId; - size_t FilenameLen; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_TBL_RegistryRec_t *RegRecPtr = NULL; + CFE_TBL_Handle_t AccessIterator; + CFE_ES_AppId_t ThisAppId; + size_t FilenameLen; /* Verify that this application has the right to perform operation */ - Status = CFE_TBL_ValidateAccess(TblHandle, &ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_ACCESSOR_APP); if (Status == CFE_SUCCESS) { /* Get pointers to pertinent records in registry and handles */ - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); + ThisAppId = CFE_TBL_TxnAppId(&Txn); + + CFE_TBL_TxnFinish(&Txn); /* If the table is a critical table, update the appropriate CDS with the new data */ if (RegRecPtr->CriticalTable == true) @@ -1260,19 +1268,21 @@ CFE_Status_t CFE_TBL_Modified(CFE_TBL_Handle_t TblHandle) CFE_Status_t CFE_TBL_NotifyByMessage(CFE_TBL_Handle_t TblHandle, CFE_SB_MsgId_t MsgId, CFE_MSG_FcnCode_t CommandCode, uint32 Parameter) { - int32 Status; - CFE_TBL_AccessDescriptor_t *AccessDescPtr = NULL; - CFE_TBL_RegistryRec_t * RegRecPtr = NULL; - CFE_ES_AppId_t ThisAppId; + CFE_TBL_TxnState_t Txn; + int32 Status; + CFE_TBL_RegistryRec_t *RegRecPtr = NULL; + CFE_ES_AppId_t ThisAppId; /* Verify that this application has the right to perform operation */ - Status = CFE_TBL_ValidateAccess(TblHandle, &ThisAppId); + Status = CFE_TBL_TxnStartFromHandle(&Txn, TblHandle, CFE_TBL_TxnContext_ACCESSOR_APP); if (Status == CFE_SUCCESS) { /* Get pointers to pertinent records in registry and handles */ - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + RegRecPtr = CFE_TBL_TxnRegRec(&Txn); + ThisAppId = CFE_TBL_TxnAppId(&Txn); + + CFE_TBL_TxnFinish(&Txn); /* Verify that the calling application is the table owner */ if (CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, ThisAppId)) diff --git a/modules/tbl/fsw/src/cfe_tbl_internal.c b/modules/tbl/fsw/src/cfe_tbl_internal.c index 03a1ede1e..269173705 100644 --- a/modules/tbl/fsw/src/cfe_tbl_internal.c +++ b/modules/tbl/fsw/src/cfe_tbl_internal.c @@ -209,272 +209,6 @@ void CFE_TBL_InitRegistryRecord(CFE_TBL_RegistryRec_t *RegRecPtr) RegRecPtr->DumpControlIndex = CFE_TBL_NO_DUMP_PENDING; } -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_TBL_ValidateHandle(CFE_TBL_Handle_t TblHandle) -{ - /* Is the handle out of range? */ - if (TblHandle >= CFE_PLATFORM_TBL_MAX_NUM_HANDLES) - { - return CFE_TBL_ERR_INVALID_HANDLE; - } - else - { - /* Check to see if the Handle is no longer valid for this Table */ - if (CFE_TBL_Global.Handles[TblHandle].UsedFlag == false) - { - return CFE_TBL_ERR_INVALID_HANDLE; - } - } - return CFE_SUCCESS; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_TBL_ValidateAccess(CFE_TBL_Handle_t TblHandle, CFE_ES_AppId_t *AppIdPtr) -{ - int32 Status; - - /* Check to make sure App ID is legit */ - Status = CFE_ES_GetAppID(AppIdPtr); - - if (Status != CFE_SUCCESS) - { - return Status; - } - - /* Check table handle validity */ - Status = CFE_TBL_ValidateHandle(TblHandle); - - if (Status != CFE_SUCCESS) - { - return Status; - } - - Status = CFE_TBL_CheckAccessRights(TblHandle, *AppIdPtr); - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_TBL_CheckAccessRights(CFE_TBL_Handle_t TblHandle, CFE_ES_AppId_t ThisAppId) -{ - int32 Status = CFE_SUCCESS; - - if (!CFE_RESOURCEID_TEST_EQUAL(ThisAppId, CFE_TBL_Global.Handles[TblHandle].AppId)) - { - /* The Table Service Task always has access rights so that tables */ - /* can be manipulated via ground command */ - if (!CFE_RESOURCEID_TEST_EQUAL(ThisAppId, CFE_TBL_Global.TableTaskAppId)) - { - Status = CFE_TBL_ERR_NO_ACCESS; - } - } - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_TBL_RemoveAccessLink(CFE_TBL_Handle_t TblHandle) -{ - int32 Status = CFE_SUCCESS; - CFE_TBL_AccessDescriptor_t *AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - CFE_TBL_RegistryRec_t * RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; - - /* Lock Access to the table while we modify the linked list */ - CFE_TBL_LockRegistry(); - - /* If we are removing the head of the linked list, then point */ - /* the head pointer to the link after this one */ - if (AccessDescPtr->PrevLink == CFE_TBL_END_OF_LIST) - { - RegRecPtr->HeadOfAccessList = AccessDescPtr->NextLink; - - /* Update the next link, if there is one, to be the new head of the list */ - if (AccessDescPtr->NextLink != CFE_TBL_END_OF_LIST) - { - CFE_TBL_Global.Handles[AccessDescPtr->NextLink].PrevLink = CFE_TBL_END_OF_LIST; - } - } - else /* Access Descriptor is not the head of the list */ - { - /* Set the next link on the previous link to the next link of the link being removed */ - CFE_TBL_Global.Handles[AccessDescPtr->PrevLink].NextLink = AccessDescPtr->NextLink; - - /* If this link is not the end of the list, then complete two way linkage */ - /* by setting the next link's previous link to the previous link of the link being removed */ - if (AccessDescPtr->NextLink != CFE_TBL_END_OF_LIST) - { - CFE_TBL_Global.Handles[AccessDescPtr->NextLink].PrevLink = AccessDescPtr->PrevLink; - } - } - - /* Return the Access Descriptor to the pool */ - AccessDescPtr->UsedFlag = false; - - /* If this was the last Access Descriptor for this table, we can free the memory buffers as well */ - if (RegRecPtr->HeadOfAccessList == CFE_TBL_END_OF_LIST) - { - /* Only free memory that we have allocated. If the image is User Defined, then don't bother */ - if (RegRecPtr->UserDefAddr == false) - { - /* Free memory allocated to buffers */ - Status = CFE_ES_PutPoolBuf(CFE_TBL_Global.Buf.PoolHdl, RegRecPtr->Buffers[0].BufferPtr); - RegRecPtr->Buffers[0].BufferPtr = NULL; - - if (Status < 0) - { - CFE_ES_WriteToSysLog("%s: PutPoolBuf[0] Fail Stat=0x%08X, Hndl=0x%08lX, Buf=0x%08lX\n", __func__, - (unsigned int)Status, CFE_RESOURCEID_TO_ULONG(CFE_TBL_Global.Buf.PoolHdl), - (unsigned long)RegRecPtr->Buffers[0].BufferPtr); - } - - /* If a double buffered table, then free the second buffer as well */ - if (RegRecPtr->DoubleBuffered) - { - Status = CFE_ES_PutPoolBuf(CFE_TBL_Global.Buf.PoolHdl, RegRecPtr->Buffers[1].BufferPtr); - RegRecPtr->Buffers[1].BufferPtr = NULL; - - if (Status < 0) - { - CFE_ES_WriteToSysLog("%s: PutPoolBuf[1] Fail Stat=0x%08X, Hndl=0x%08lX, Buf=0x%08lX\n", __func__, - (unsigned int)Status, CFE_RESOURCEID_TO_ULONG(CFE_TBL_Global.Buf.PoolHdl), - (unsigned long)RegRecPtr->Buffers[1].BufferPtr); - } - } - else - { - /* If a shared buffer has been allocated to the table, then release it as well */ - if (RegRecPtr->LoadInProgress != CFE_TBL_NO_LOAD_IN_PROGRESS) - { - /* Free the working buffer */ - CFE_TBL_Global.LoadBuffs[RegRecPtr->LoadInProgress].Taken = false; - RegRecPtr->LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS; - } - } - } - } - - /* Unlock the registry to allow others to modify it */ - CFE_TBL_UnlockRegistry(); - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_TBL_GetAddressInternal(void **TblPtr, CFE_TBL_Handle_t TblHandle, CFE_ES_AppId_t ThisAppId) -{ - int32 Status; - CFE_TBL_AccessDescriptor_t *AccessDescPtr; - CFE_TBL_RegistryRec_t * RegRecPtr; - - /* Check table handle validity */ - Status = CFE_TBL_ValidateHandle(TblHandle); - - if (Status == CFE_SUCCESS) - { - /* Get a pointer to the Access Descriptor */ - AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - - /* Verify that we are allowed access to the table */ - Status = CFE_TBL_CheckAccessRights(TblHandle, ThisAppId); - - if (Status == CFE_SUCCESS) - { - /* Get a pointer to the Table Registry entry */ - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; - - /* If table is unowned, then owner must have unregistered it when we weren't looking */ - if (CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, CFE_TBL_NOT_OWNED)) - { - Status = CFE_TBL_ERR_UNREGISTERED; - - CFE_ES_WriteToSysLog("%s: App(%lu) attempt to access unowned Tbl Handle=%d\n", __func__, - CFE_RESOURCEID_TO_ULONG(ThisAppId), (int)TblHandle); - } - else /* Table Registry Entry is valid */ - { - /* Lock the table and return the current pointer */ - AccessDescPtr->LockFlag = true; - - /* Save the buffer we are using in the access descriptor */ - /* This is used to ensure that if the buffer becomes inactive while */ - /* we are using it, no one will modify it until we are done */ - AccessDescPtr->BufferIndex = RegRecPtr->ActiveBufferIndex; - - *TblPtr = RegRecPtr->Buffers[AccessDescPtr->BufferIndex].BufferPtr; - - /* Return any pending warning or info status indicators */ - Status = CFE_TBL_GetNextNotification(TblHandle); - - /* Clear Table Updated Notify Bit so that caller only gets it once */ - AccessDescPtr->Updated = false; - } - } - else - { - CFE_ES_WriteToSysLog("%s: App(%lu) does not have access to Tbl Handle=%d\n", __func__, - CFE_RESOURCEID_TO_ULONG(ThisAppId), (int)TblHandle); - } - } - else - { - CFE_ES_WriteToSysLog("%s: App(%lu) using invalid Tbl Handle=%d\n", __func__, CFE_RESOURCEID_TO_ULONG(ThisAppId), - (int)TblHandle); - } - - return Status; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int32 CFE_TBL_GetNextNotification(CFE_TBL_Handle_t TblHandle) -{ - int32 Status = CFE_SUCCESS; - CFE_TBL_AccessDescriptor_t *AccessDescPtr = &CFE_TBL_Global.Handles[TblHandle]; - CFE_TBL_RegistryRec_t * RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; - - if (!RegRecPtr->TableLoadedOnce) - { - /* If the table has never been loaded, return an error code for the address */ - Status = CFE_TBL_ERR_NEVER_LOADED; - } - else if (AccessDescPtr->Updated) - { - /* If the table has been updated recently, return the update status */ - Status = CFE_TBL_INFO_UPDATED; - } - - return Status; -} - /*---------------------------------------------------------------- * * Application-scope internal function @@ -483,82 +217,15 @@ int32 CFE_TBL_GetNextNotification(CFE_TBL_Handle_t TblHandle) *-----------------------------------------------------------------*/ int16 CFE_TBL_FindTableInRegistry(const char *TblName) { - int16 RegIndx = CFE_TBL_NOT_FOUND; - int16 i = -1; - - do - { - /* Point to next record in the Table Registry */ - i++; + CFE_TBL_TxnState_t Txn; - /* Check to see if the record is currently being used */ - if (!CFE_RESOURCEID_TEST_EQUAL(CFE_TBL_Global.Registry[i].OwnerAppId, CFE_TBL_NOT_OWNED)) - { - /* Perform a case sensitive name comparison */ - if (strcmp(TblName, CFE_TBL_Global.Registry[i].Name) == 0) - { - /* If the names match, then return the index */ - RegIndx = i; - } - } - } while ((RegIndx == CFE_TBL_NOT_FOUND) && (i < (CFE_PLATFORM_TBL_MAX_NUM_TABLES - 1))); + /* Note: there is no way for transaction setup to fail when passing false for context check */ + CFE_TBL_TxnInit(&Txn, false); + CFE_TBL_TxnFindRegByName(&Txn, TblName); + CFE_TBL_TxnFinish(&Txn); - return RegIndx; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -int16 CFE_TBL_FindFreeRegistryEntry(void) -{ - int16 RegIndx = CFE_TBL_NOT_FOUND; - int16 i = 0; - - while ((RegIndx == CFE_TBL_NOT_FOUND) && (i < CFE_PLATFORM_TBL_MAX_NUM_TABLES)) - { - /* A Table Registry is only "Free" when there isn't an owner AND */ - /* all other applications are not sharing or locking the table */ - if (CFE_RESOURCEID_TEST_EQUAL(CFE_TBL_Global.Registry[i].OwnerAppId, CFE_TBL_NOT_OWNED) && - (CFE_TBL_Global.Registry[i].HeadOfAccessList == CFE_TBL_END_OF_LIST)) - { - RegIndx = i; - } - else - { - i++; - } - } - - return RegIndx; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -CFE_TBL_Handle_t CFE_TBL_FindFreeHandle(void) -{ - CFE_TBL_Handle_t HandleIndx = CFE_TBL_END_OF_LIST; - int16 i = 0; - - while ((HandleIndx == CFE_TBL_END_OF_LIST) && (i < CFE_PLATFORM_TBL_MAX_NUM_HANDLES)) - { - if (CFE_TBL_Global.Handles[i].UsedFlag == false) - { - HandleIndx = i; - } - else - { - i++; - } - } - - return HandleIndx; + /* The transaction mechanism will set this to CFE_TBL_NOT_FOUND if anything failed */ + return CFE_TBL_TxnRegId(&Txn); } /*---------------------------------------------------------------- @@ -1222,9 +889,8 @@ void CFE_TBL_ByteSwapUint32(uint32 *Uint32ToSwapPtr) *-----------------------------------------------------------------*/ int32 CFE_TBL_CleanUpApp(CFE_ES_AppId_t AppId) { - uint32 i; - CFE_TBL_RegistryRec_t * RegRecPtr = NULL; - CFE_TBL_AccessDescriptor_t *AccessDescPtr = NULL; + uint32 i; + CFE_TBL_TxnState_t Txn; /* Scan Dump Requests to determine if any of the tables that */ /* were to be dumped will be deleted */ @@ -1246,33 +912,30 @@ int32 CFE_TBL_CleanUpApp(CFE_ES_AppId_t AppId) if (CFE_RESOURCEID_TEST_EQUAL(CFE_TBL_Global.Handles[i].AppId, AppId) && CFE_TBL_Global.Handles[i].UsedFlag == true) { - /* Delete the handle (and the table, if the App owned it) */ - /* Get a pointer to the relevant Access Descriptor */ - AccessDescPtr = &CFE_TBL_Global.Handles[i]; - - /* Get a pointer to the relevant entry in the registry */ - RegRecPtr = &CFE_TBL_Global.Registry[AccessDescPtr->RegIndex]; + CFE_TBL_TxnStartFromHandle(&Txn, i, CFE_TBL_TxnContext_UNDEFINED); /* Determine if the Application owned this particular table */ - if (CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, AppId)) + if (CFE_RESOURCEID_TEST_EQUAL(Txn.RegRecPtr->OwnerAppId, AppId)) { /* Mark table as free, although, technically, it isn't free until the */ /* linked list of Access Descriptors has no links in it. */ /* NOTE: Allocated memory is freed when all Access Links have been */ /* removed. This allows Applications to continue to use the */ /* data until they acknowledge that the table has been removed. */ - RegRecPtr->OwnerAppId = CFE_TBL_NOT_OWNED; + Txn.RegRecPtr->OwnerAppId = CFE_TBL_NOT_OWNED; /* Remove Table Name */ - RegRecPtr->Name[0] = '\0'; + Txn.RegRecPtr->Name[0] = '\0'; } /* Remove the Access Descriptor Link from linked list */ /* NOTE: If this removes the last access link, then */ /* memory buffers are set free as well. */ - CFE_TBL_RemoveAccessLink(i); + CFE_TBL_TxnRemoveAccessLink(&Txn); CFE_TBL_Global.Handles[i].AppId = CFE_TBL_NOT_OWNED; + + CFE_TBL_TxnFinish(&Txn); } } @@ -1491,90 +1154,6 @@ CFE_Status_t CFE_TBL_ValidateTableOptions(const char *Name, uint16 TblOptionFlag return Status; } -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ -CFE_Status_t CFE_TBL_CheckForDuplicateRegistration(int16 *RegIndxPtr, const char *TblName, - CFE_TBL_RegistryRec_t *RegRecPtr, CFE_ES_AppId_t ThisAppId, - size_t Size, CFE_TBL_Handle_t *TblHandlePtr) -{ - CFE_Status_t Status = CFE_SUCCESS; - CFE_TBL_Handle_t AccessIndex; - - /* Check for duplicate table name */ - *RegIndxPtr = CFE_TBL_FindTableInRegistry(TblName); - - /* Check to see if table is already in the registry */ - if (*RegIndxPtr != CFE_TBL_NOT_FOUND) - { - /* Get pointer to Registry Record Entry to speed up processing */ - RegRecPtr = &CFE_TBL_Global.Registry[*RegIndxPtr]; - - /* If this app previously owned the table, then allow them to re-register */ - if (CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, ThisAppId)) - { - /* If the new table is the same size as the old, then no need to reallocate memory */ - if (Size != RegRecPtr->Size) - { - /* If the new size is different, the old table must be deleted but this */ - /* function can't do that because it is probably shared and is probably */ - /* still being accessed. Someone else will need to clean up this mess. */ - Status = CFE_TBL_ERR_DUPLICATE_DIFF_SIZE; - - CFE_ES_WriteToSysLog("%s: Attempt to register existing table ('%s') with different size(%d!=%d)\n", - __func__, TblName, (int)Size, (int)RegRecPtr->Size); - } - else - { - /* Warn calling application that this is a duplicate registration */ - Status = CFE_TBL_WARN_DUPLICATE; - - /* Find the existing access descriptor for the table */ - /* and return the same handle that was returned previously */ - AccessIndex = RegRecPtr->HeadOfAccessList; - while ((AccessIndex != CFE_TBL_END_OF_LIST) && (*TblHandlePtr == CFE_TBL_BAD_TABLE_HANDLE)) - { - if ((CFE_TBL_Global.Handles[AccessIndex].UsedFlag == true) && - CFE_RESOURCEID_TEST_EQUAL(CFE_TBL_Global.Handles[AccessIndex].AppId, ThisAppId) && - (CFE_TBL_Global.Handles[AccessIndex].RegIndex == *RegIndxPtr)) - { - *TblHandlePtr = AccessIndex; - } - else - { - AccessIndex = CFE_TBL_Global.Handles[AccessIndex].NextLink; - } - } - } - } - else /* Duplicate named table owned by another Application */ - { - Status = CFE_TBL_ERR_DUPLICATE_NOT_OWNED; - - CFE_ES_WriteToSysLog("%s: App(%lu) Registering Duplicate Table '%s' owned by App(%lu)\n", __func__, - CFE_RESOURCEID_TO_ULONG(ThisAppId), TblName, - CFE_RESOURCEID_TO_ULONG(RegRecPtr->OwnerAppId)); - } - } - else /* Table not already in registry */ - { - /* Locate empty slot in table registry */ - *RegIndxPtr = CFE_TBL_FindFreeRegistryEntry(); - - /* Check if the registry was full and set error status if it was */ - if (*RegIndxPtr == CFE_TBL_NOT_FOUND) - { - Status = CFE_TBL_ERR_REGISTRY_FULL; - CFE_ES_WriteToSysLog("CFE_TBL:Register-Registry full\n"); - } - } - - return Status; -} - /*---------------------------------------------------------------- * * Application-scope internal function @@ -1666,43 +1245,6 @@ void CFE_TBL_InitTableRegistryEntry(CFE_TBL_RegistryRec_t *RegRecPtr, size_t Siz * See description in header file for argument/return detail * *-----------------------------------------------------------------*/ -void CFE_TBL_InitTableAccessDescriptor(CFE_TBL_Handle_t *TblHandlePtr, CFE_ES_AppId_t ThisAppId, - CFE_TBL_RegistryRec_t *RegRecPtr, int16 RegIndx) -{ - CFE_TBL_AccessDescriptor_t *AccessDescPtr = NULL; - - /* Initialize the Table Access Descriptor */ - AccessDescPtr = &CFE_TBL_Global.Handles[*TblHandlePtr]; - - AccessDescPtr->AppId = ThisAppId; - AccessDescPtr->LockFlag = false; - AccessDescPtr->Updated = false; - - if ((RegRecPtr->DumpOnly) && (!RegRecPtr->UserDefAddr)) - { - /* Dump Only Tables are assumed to be loaded at all times unless the address is specified */ - /* by the application. In that case, it isn't loaded until the address is specified */ - RegRecPtr->TableLoadedOnce = true; - } - - AccessDescPtr->RegIndex = RegIndx; - - AccessDescPtr->PrevLink = CFE_TBL_END_OF_LIST; /* We are the head of the list */ - AccessDescPtr->NextLink = CFE_TBL_END_OF_LIST; /* We are the end of the list */ - - AccessDescPtr->UsedFlag = true; - - /* Make sure the Table Registry entry points to First Access Descriptor */ - RegRecPtr->HeadOfAccessList = *TblHandlePtr; -} - -/*---------------------------------------------------------------- - * - * Application-scope internal function - * See description in header file for argument/return detail - * - *-----------------------------------------------------------------*/ - CFE_Status_t CFE_TBL_RestoreTableDataFromCDS(CFE_TBL_RegistryRec_t *RegRecPtr, const char *AppName, const char *Name, CFE_TBL_CritRegRec_t *CritRegRecPtr) { @@ -1818,4 +1360,4 @@ void CFE_TBL_RegisterWithCriticalTableRegistry(CFE_TBL_CritRegRec_t *CritRegRecP /* Mark the table as critical for future reference */ RegRecPtr->CriticalTable = true; -} \ No newline at end of file +} diff --git a/modules/tbl/fsw/src/cfe_tbl_internal.h b/modules/tbl/fsw/src/cfe_tbl_internal.h index 8f96b12e1..cf1ab059f 100644 --- a/modules/tbl/fsw/src/cfe_tbl_internal.h +++ b/modules/tbl/fsw/src/cfe_tbl_internal.h @@ -50,146 +50,6 @@ /***************************** Function Prototypes **********************************/ -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Validates specified handle to ensure legality -** -** \par Description -** Validates handle given by calling App to Table API. Validation -** includes ensuring the value is within an acceptable range and -** the Access Descriptor that it identifies is being "used". -** -** \par Assumptions, External Events, and Notes: -** None -** -** \param[in] TblHandle - Handle to be validated -** -** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS -** \retval #CFE_TBL_ERR_INVALID_HANDLE \copydoc CFE_TBL_ERR_INVALID_HANDLE -** -*/ -int32 CFE_TBL_ValidateHandle(CFE_TBL_Handle_t TblHandle); - -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Determines whether handle is associated with calling Application -** -** \par Description -** Validates whether the calling application has the right to -** access the table identified with the given TblHandle. Validation -** consists of verifying the calling Application's AppID, verifying -** the legitimacy of the given TblHandle, and checking to make sure -** the Access Descriptor identified by the TblHandle is associated -** with the calling Application. -** -** \par Assumptions, External Events, and Notes: -** None -** -** \param[in] TblHandle Handle of table whose access is desired. -** -** \param[in, out] AppIdPtr Pointer to value that will hold AppID on return. *AppIdPtr is the AppID as obtained from -*#CFE_ES_GetAppID -** -** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS -** \retval #CFE_ES_ERR_RESOURCEID_NOT_VALID \copydoc CFE_ES_ERR_RESOURCEID_NOT_VALID -** \retval #CFE_TBL_ERR_INVALID_HANDLE \copydoc CFE_TBL_ERR_INVALID_HANDLE -** \retval #CFE_TBL_ERR_NO_ACCESS \copydoc CFE_TBL_ERR_NO_ACCESS -** -*/ -int32 CFE_TBL_ValidateAccess(CFE_TBL_Handle_t TblHandle, CFE_ES_AppId_t *AppIdPtr); - -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Determines if calling application has the right to access specified table -** -** \par Description -** Validates whether the calling application has the right to -** access the table identified with the given TblHandle. Validation -** consists of checking to make sure the Access Descriptor identified -** by the TblHandle is associated with the calling Application. -** -** \par Assumptions, External Events, and Notes: -** Note: The TblHandle and ThisAppId parameters are assumed to be valid. -** -** \param[in] TblHandle Handle of table whose access is desired. -** -** \param[in] ThisAppId Application ID of Application making the call -** -** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS -** \retval #CFE_TBL_ERR_NO_ACCESS \copydoc CFE_TBL_ERR_NO_ACCESS -** -*/ -int32 CFE_TBL_CheckAccessRights(CFE_TBL_Handle_t TblHandle, CFE_ES_AppId_t ThisAppId); - -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Removes Access Descriptor from Table's linked list of Access Descriptors -** -** \par Description -** Removes the given Access Descriptor from the Linked List -** of Access Descriptors associated with the table specified -** in the Access Descriptor itself. -** -** \par Assumptions, External Events, and Notes: -** -# This function CAN block and should not be called by ISRs. -** -# This function assumes the Access Descriptor is completely -** filled out and the TblHandle has been validated. -** -** \param[in] TblHandle Handle of Access Descriptor to be removed. -** -** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS -** -*/ -int32 CFE_TBL_RemoveAccessLink(CFE_TBL_Handle_t TblHandle); - -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Obtains the data address for the specified table -** -** \par Description -** Validates the given TblHandle, finds the location of the -** Table data and returns the address to the data to the caller. -** -** \par Assumptions, External Events, and Notes: -** -# It is possible that an Application that was sharing a table -** would discover, upon making this call, that the table has -** been unregistered by another Application. In this situation, -** this function would return #CFE_TBL_ERR_UNREGISTERED. -** -# ThisAppId parameter is assumed to be validated. -** -** \param[in, out] TblPtr Pointer to pointer that will hold address of data upon return. *TblPtr is the address of -** the Table Data. -** \param[in] TblHandle Handle of Table whose address is needed. -** \param[in] ThisAppId AppID of application making the address request. -** -** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS -** \retval #CFE_TBL_ERR_INVALID_HANDLE \copydoc CFE_TBL_ERR_INVALID_HANDLE -** \retval #CFE_TBL_ERR_NO_ACCESS \copydoc CFE_TBL_ERR_NO_ACCESS -** \retval #CFE_TBL_ERR_UNREGISTERED \copydoc CFE_TBL_ERR_UNREGISTERED -** -*/ -int32 CFE_TBL_GetAddressInternal(void **TblPtr, CFE_TBL_Handle_t TblHandle, CFE_ES_AppId_t ThisAppId); - -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Returns any pending non-error status code for the specified table. -** -** \par Description -** Returns any pending non-error status code for the specified table. -** -** \par Assumptions, External Events, and Notes: -** Note: This function assumes the TblHandle has been validated. -** -** \param[in] TblHandle Handle of Table whose pending notifications are -** to be returned. -** -** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS -** \retval #CFE_TBL_INFO_UPDATE_PENDING \copydoc CFE_TBL_INFO_UPDATE_PENDING -** \retval #CFE_TBL_INFO_UPDATED \copydoc CFE_TBL_INFO_UPDATED -** -*/ -int32 CFE_TBL_GetNextNotification(CFE_TBL_Handle_t TblHandle); - /*---------------------------------------------------------------------------------------*/ /** ** \brief Returns the Registry Index for the specified Table Name @@ -209,34 +69,6 @@ int32 CFE_TBL_GetNextNotification(CFE_TBL_Handle_t TblHandle); */ int16 CFE_TBL_FindTableInRegistry(const char *TblName); -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Locates a free slot in the Table Registry. -** -** \par Description -** Locates a free slot in the Table Registry. -** -** \par Assumptions, External Events, and Notes: -** Note: This function assumes the registry has been locked. -** -** \retval #CFE_TBL_NOT_FOUND or Index into Table Registry of unused entry -*/ -int16 CFE_TBL_FindFreeRegistryEntry(void); - -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Locates a free Access Descriptor in the Table Handles Array. -** -** \par Description -** Locates a free Access Descriptor in the Table Handles Array. -** -** \par Assumptions, External Events, and Notes: -** Note: This function assumes the registry has been locked. -** -** \retval #CFE_TBL_END_OF_LIST or Table Handle of unused Access Descriptor -*/ -CFE_TBL_Handle_t CFE_TBL_FindFreeHandle(void); - /*---------------------------------------------------------------------------------------*/ /** ** \brief Creates a Full Table name from application name and table name @@ -623,30 +455,6 @@ CFE_Status_t CFE_TBL_ValidateTableSize(const char *Name, size_t Size, uint16 Tbl */ CFE_Status_t CFE_TBL_ValidateTableOptions(const char *Name, uint16 TblOptionFlags); -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Checks if a table is already registered in the Table Registry -** -** \par Description -** This routine searches the Table Registry for a table with the specified name, -** owning app and size. If a match is found, the same handle is returned. If a -** match is not found, the function will locate a free slot in the table registry -** (unless it's already full). -** -** \par Assumptions, External Events, and Notes: -** None -** -** \retval #CFE_SUCCESS \copydoc CFE_SUCCESS -** \retval #CFE_TBL_ERR_DUPLICATE_DIFF_SIZE \copydoc CFE_TBL_ERR_DUPLICATE_DIFF_SIZE -** \retval #CFE_TBL_WARN_DUPLICATE \copydoc CFE_TBL_WARN_DUPLICATE -** \retval #CFE_TBL_ERR_DUPLICATE_NOT_OWNED \copydoc CFE_TBL_ERR_DUPLICATE_NOT_OWNED -** \retval #CFE_TBL_ERR_REGISTRY_FULL \copydoc CFE_TBL_ERR_REGISTRY_FULL -** -*/ -CFE_Status_t CFE_TBL_CheckForDuplicateRegistration(int16 *RegIndxPtr, const char *TblName, - CFE_TBL_RegistryRec_t *RegRecPtr, CFE_ES_AppId_t ThisAppId, - size_t Size, CFE_TBL_Handle_t *TblHandlePtr); - /*---------------------------------------------------------------------------------------*/ /** ** \brief Allocates memory for the table buffer @@ -695,20 +503,6 @@ void CFE_TBL_InitTableRegistryEntry(CFE_TBL_RegistryRec_t *RegRecPtr, size_t Siz CFE_TBL_CallbackFuncPtr_t TblValidationFuncPtr, const char *TblName, uint16 TblOptionFlags); -/*---------------------------------------------------------------------------------------*/ -/** -** \brief Initializes a Table Access Descriptor -** -** \par Description -** Initializes a Table Access Descriptor for a table that is being registered -** -** \par Assumptions, External Events, and Notes: -** None -** -*/ -void CFE_TBL_InitTableAccessDescriptor(CFE_TBL_Handle_t *TblHandlePtr, CFE_ES_AppId_t ThisAppId, - CFE_TBL_RegistryRec_t *RegRecPtr, int16 RegIndx); - /*---------------------------------------------------------------------------------------*/ /** ** \brief Restore the contents of a table from the Critical Data Store (if it exists) diff --git a/modules/tbl/fsw/src/cfe_tbl_module_all.h b/modules/tbl/fsw/src/cfe_tbl_module_all.h index a9f87a659..ca60f2f8d 100644 --- a/modules/tbl/fsw/src/cfe_tbl_module_all.h +++ b/modules/tbl/fsw/src/cfe_tbl_module_all.h @@ -45,6 +45,8 @@ #include "cfe_tbl_task.h" #include "cfe_tbl_task_cmds.h" #include "cfe_tbl_dispatch.h" +#include "cfe_tbl_resource.h" +#include "cfe_tbl_transaction.h" /* * Additionally TBL needs to use special/extra CDS APIs that are not in the normal API diff --git a/modules/tbl/fsw/src/cfe_tbl_resource.c b/modules/tbl/fsw/src/cfe_tbl_resource.c new file mode 100644 index 000000000..d2428a1b8 --- /dev/null +++ b/modules/tbl/fsw/src/cfe_tbl_resource.c @@ -0,0 +1,115 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/* +** File: +** cfe_tbl_resource.c +** +** Purpose: +** Function definitions related to CFE resource management +** +** References: +** Flight Software Branch C Coding Standard Version 1.0a +** cFE Flight Software Application Developers Guide +*/ + +/* +** Includes +*/ +#include "cfe_tbl_module_all.h" + +/*---------------------------------------------------------------- + * + * Implemented per public API + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_Handle_ToIndex(CFE_TBL_Handle_t TblHandle, uint32 *Idx) +{ + if (TblHandle < 0 || TblHandle >= CFE_PLATFORM_TBL_MAX_NUM_HANDLES) + { + return CFE_TBL_ERR_INVALID_HANDLE; + } + + *Idx = TblHandle; + + return CFE_SUCCESS; +} +/*---------------------------------------------------------------- + * + * Implemented per public API + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_RegId_ToIndex(CFE_TBL_RegId_t RegId, uint32 *Idx) +{ + if (RegId < 0 || RegId >= CFE_PLATFORM_TBL_MAX_NUM_TABLES) + { + return CFE_TBL_ERR_INVALID_HANDLE; + } + + *Idx = RegId; + + return CFE_SUCCESS; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_TBL_RegistryRec_t *CFE_TBL_LocateRegistryRecordByID(CFE_TBL_RegId_t RegId) +{ + CFE_TBL_RegistryRec_t *RegRecPtr; + uint32 Idx; + + if (CFE_TBL_RegId_ToIndex(RegId, &Idx) == CFE_SUCCESS) + { + RegRecPtr = &CFE_TBL_Global.Registry[Idx]; + } + else + { + RegRecPtr = NULL; + } + + return RegRecPtr; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_TBL_AccessDescriptor_t *CFE_TBL_LocateAccessDescriptorByHandle(CFE_TBL_Handle_t TblHandle) +{ + CFE_TBL_AccessDescriptor_t *AccessDescPtr; + uint32 Idx; + + if (CFE_TBL_Handle_ToIndex(TblHandle, &Idx) == CFE_SUCCESS) + { + AccessDescPtr = &CFE_TBL_Global.Handles[Idx]; + } + else + { + AccessDescPtr = NULL; + } + + return AccessDescPtr; +} diff --git a/modules/tbl/fsw/src/cfe_tbl_resource.h b/modules/tbl/fsw/src/cfe_tbl_resource.h new file mode 100644 index 000000000..6cf4d0f56 --- /dev/null +++ b/modules/tbl/fsw/src/cfe_tbl_resource.h @@ -0,0 +1,516 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * @file + * + * Contains basic prototypes and definitions related to CFE TBL resource + * management and related resource IDs. + * + * A CFE TBL Resource ID is a common way to identify CFE-managed resources such + * as registry entries, buffers, state records, and other entities. + */ + +#ifndef CFE_TBL_RESOURCE_H +#define CFE_TBL_RESOURCE_H + +/* +** Include Files +*/ +#include "cfe_resourceid.h" +#include "cfe_core_resourceid_basevalues.h" +#include "cfe_tbl_task.h" + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Locate the app table entry correlating with a given app ID. + * + * This only returns a pointer to the table entry where the record + * should reside, but does _not_ actually check/validate the entry. + * + * If the passed-in ID parameter is not within the acceptable range of ID + * values for applications, such that it could never be valid under + * any circumstances, then NULL is returned. Otherwise, a pointer to the + * corresponding table entry is returned, indicating the location where + * that ID _should_ reside, if it is currently in use. + * + * @note This only returns where the ID should reside, not that it actually + * resides there. If looking up an existing ID, then caller must additionally + * confirm that the returned record is a match to the expected ID before using + * or modifying the data within the returned record pointer. + * + * The CFE_TBL_RegistryRecordIsMatch() function can be used to check/confirm + * if the returned table entry is a positive match for the given ID. + * + * @sa CFE_TBL_RegistryRecordIsMatch() + * + * @param[in] RegId the app ID to locate + * @return pointer to App Table entry for the given app ID, or NULL if out of range + */ +CFE_TBL_RegistryRec_t *CFE_TBL_LocateRegistryRecordByID(CFE_TBL_RegId_t RegId); + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Locate the task table entry correlating with a given task ID. + * + * This only returns a pointer to the table entry where the record + * should reside, but does _not_ actually check/validate the entry. + * + * If the passed-in ID parameter is not within the acceptable range of ID + * values for tasks, such that it could never be valid under + * any circumstances, then NULL is returned. Otherwise, a pointer to the + * corresponding table entry is returned, indicating the location where + * that ID _should_ reside, if it is currently in use. + * + * @note This only returns where the ID should reside, not that it actually + * resides there. If looking up an existing ID, then caller must additionally + * confirm that the returned record is a match to the expected ID before using + * or modifying the data within the returned record pointer. + * + * The CFE_TBL_AccessDescriptorIsMatch() function can be used to check/confirm + * if the returned table entry is a positive match for the given ID. + * + * @sa CFE_TBL_AccessDescriptorIsMatch() + * + * @param[in] TblHandle the task ID to locate + * @return pointer to Task Table entry for the given task ID, or NULL if out of range + */ +CFE_TBL_AccessDescriptor_t *CFE_TBL_LocateAccessDescriptorByHandle(CFE_TBL_Handle_t TblHandle); + +#ifdef jphfix +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if an app record is in use or free/empty + * + * This routine checks if the App table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] RegRecPtr pointer to app table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_TBL_RegistryRecordIsUsed(const CFE_TBL_RegistryRec_t *RegRecPtr) +{ + return RegRecPtr->Taken; +} +#endif + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if an app record is a match for the given RegId + * + * This routine confirms that the previously-located record is valid + * and matches the expected app ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * This function may be used in conjunction with CFE_TBL_LocateRegistryRecordByID() + * to confirm that the located record is a positive match to the expected ID. + * As such, the record pointer is also permitted to be NULL, to alleviate the + * need for the caller to handle this possibility explicitly. + * + * Once a record pointer has been successfully validated using this routine, + * it may be safely passed to all other internal functions. + * + * @sa CFE_TBL_LocateRegistryRecordByID + * + * @param[in] RegRecPtr pointer to app table entry, or NULL + * @param[in] RegId expected app ID + * @returns true if the entry matches the given app ID + */ +static inline bool CFE_TBL_RegistryRecordIsMatch(const CFE_TBL_RegistryRec_t *RegRecPtr, CFE_TBL_RegId_t RegId) +{ + /* + * This technically should also check CFE_RESOURCEID_TEST_DEFINED(RegRecPtr->OwnerAppId), + * but that would currently break some registration actions (which should be fixed). + */ + return (RegRecPtr != NULL); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Obtain the name associated with the Application record + * + * Returns the name field from within the Application record + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] RegRecPtr pointer to App table entry + * @returns Pointer to Application name + */ +static inline const char *CFE_TBL_RegistryRecordGetName(const CFE_TBL_RegistryRec_t *RegRecPtr) +{ + return RegRecPtr->Name; +} + +#ifdef jphfix +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the ID value from a Task table entry + * + * This routine converts the table entry back to an abstract ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] AccessDescPtr pointer to Task table entry + * @returns TblHandle of entry + */ +static inline CFE_TBL_Handle_t CFE_TBL_AccessDescriptorGetID(const CFE_TBL_AccessDescriptor_t *AccessDescPtr) +{ + return AccessDescPtr->TblHandle; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if a Task record is in use or free/empty + * + * This routine checks if the Task table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] AccessDescPtr pointer to task table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_TBL_AccessDescriptorIsUsed(const CFE_TBL_AccessDescriptor_t *AccessDescPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(AccessDescPtr->TblHandle); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Marks a Task table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given Task ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] AccessDescPtr pointer to Task table entry + * @param[in] PendingId the Task ID of this entry + */ +static inline void CFE_TBL_AccessDescriptorSetUsed(CFE_TBL_AccessDescriptor_t *AccessDescPtr, + CFE_ResourceId_t PendingId) +{ + AccessDescPtr->TblHandle = CFE_TBL_ACCESSID_C(PendingId); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Set a Task record table entry free + * + * This allows the table entry to be re-used by another Task. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] AccessDescPtr pointer to task table entry + */ +static inline void CFE_TBL_AccessDescriptorSetFree(CFE_TBL_AccessDescriptor_t *AccessDescPtr) +{ + AccessDescPtr->TblHandle = CFE_TBL_ACCESSID_UNDEFINED; +} +#endif + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if a Task record is a match for the given TblHandle + * + * This routine confirms that the previously-located record is valid + * and matches the expected Task ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * This function may be used in conjunction with CFE_TBL_LocateTaskRecordByID() + * to confirm that the located record is a positive match to the expected ID. + * As such, the record pointer is also permitted to be NULL, to alleviate the + * need for the caller to handle this possibility explicitly. + * + * Once a record pointer has been successfully validated using this routine, + * it may be safely passed to all other internal functions. + * + * @sa CFE_TBL_LocateTaskRecordByID + * + * @param[in] AccessDescPtr pointer to task table entry + * @param[in] TblHandle The expected task ID to verify + * @returns true if the entry matches the given task ID + */ +static inline bool CFE_TBL_AccessDescriptorIsMatch(const CFE_TBL_AccessDescriptor_t *AccessDescPtr, + CFE_TBL_Handle_t TblHandle) +{ + return (AccessDescPtr != NULL && AccessDescPtr->UsedFlag); +} + +#ifdef jphfix +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Obtain the name associated with the Task record + * + * Returns the name field from within the Task record + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] AccessDescPtr pointer to Task table entry + * @returns Pointer to Task name + */ +static inline const char *CFE_TBL_AccessDescriptorGetName(const CFE_TBL_AccessDescriptor_t *AccessDescPtr) +{ + return AccessDescPtr->TaskName; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if a Counter record is in use or free/empty + * + * This routine checks if the Counter table entry is in use or if it is free + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @returns true if the entry is in use/configured, or false if it is free/empty + */ +static inline bool CFE_TBL_CounterRecordIsUsed(const CFE_TBL_GenCounterRecord_t *CounterRecPtr) +{ + return CFE_RESOURCEID_TEST_DEFINED(CounterRecPtr->CounterId); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Get the ID value from a Counter table entry + * + * This routine converts the table entry back to an abstract ID. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @returns CounterID of entry + */ +static inline CFE_TBL_CounterId_t CFE_TBL_CounterRecordGetID(const CFE_TBL_GenCounterRecord_t *CounterRecPtr) +{ + return CounterRecPtr->CounterId; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Marks a Counter table entry as used (not free) + * + * This sets the internal field(s) within this entry, and marks + * it as being associated with the given Counter ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @param[in] PendingId the Counter ID of this entry + */ +static inline void CFE_TBL_CounterRecordSetUsed(CFE_TBL_GenCounterRecord_t *CounterRecPtr, CFE_ResourceId_t PendingId) +{ + CounterRecPtr->CounterId = CFE_TBL_COUNTERID_C(PendingId); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Set a Counter record table entry free (not used) + * + * This clears the internal field(s) within this entry, and allows the + * memory to be re-used in the future. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] CounterRecPtr pointer to Counter table entry + */ +static inline void CFE_TBL_CounterRecordSetFree(CFE_TBL_GenCounterRecord_t *CounterRecPtr) +{ + CounterRecPtr->CounterId = CFE_TBL_COUNTERID_UNDEFINED; +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Check if a Counter record is a match for the given CounterID + * + * This routine confirms that the previously-located record is valid + * and matches the expected Counter ID. + * + * As this dereferences fields within the record, global data must be + * locked prior to invoking this function. + * + * This function may be used in conjunction with CFE_TBL_LocateCounterRecordByID() + * to confirm that the located record is a positive match to the expected ID. + * As such, the record pointer is also permitted to be NULL, to alleviate the + * need for the caller to handle this possibility explicitly. + * + * Once a record pointer has been successfully validated using this routine, + * it may be safely passed to all other internal functions. + * + * @sa CFE_TBL_LocateCounterRecordByID + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @param[in] CounterID expected Counter ID + * @returns true if the entry matches the given Counter ID + */ +static inline bool CFE_TBL_CounterRecordIsMatch(const CFE_TBL_GenCounterRecord_t *CounterRecPtr, + CFE_TBL_CounterId_t CounterID) +{ + return (CounterRecPtr != NULL && CFE_RESOURCEID_TEST_EQUAL(CounterRecPtr->CounterId, CounterID)); +} + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Obtain the name associated with the counter record + * + * Returns the name field from within the counter record + * + * @note This internal helper function must only be used on record pointers + * that are known to refer to an actual table location (i.e. non-null). + * + * @param[in] CounterRecPtr pointer to Counter table entry + * @returns Pointer to counter name + */ +static inline const char *CFE_TBL_CounterRecordGetName(const CFE_TBL_GenCounterRecord_t *CounterRecPtr) +{ + return CounterRecPtr->CounterName; +} +#endif + +/* + * Internal functions to perform name based resource lookups + * + * These functions do not lock, they must only be used internally by ES when + * the lock is already held. + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Finds an application table record matching the given name + * + * Helper function, aids in finding an application record from a name string. + * Must be called while locked. + * + * @returns pointer to table entry matching name, or NULL if not found + */ +CFE_TBL_RegistryRec_t *CFE_TBL_LocateRegistryRecordByName(const char *Name); + +#ifdef jphfix +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Finds a library table record matching the given name + * + * Helper function, aids in finding a library record from a name string. + * Must be called while locked. + * + * @returns pointer to table entry matching name, or NULL if not found + */ +CFE_TBL_LoadBuff_t *CFE_TBL_LocateLibRecordByName(const char *Name); + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Finds a task table record matching the given name + * + * Helper function, aids in finding a task record from a name string. + * Must be called while locked. + * + * @returns pointer to table entry matching name, or NULL if not found + */ +CFE_TBL_AccessDescriptor_t *CFE_TBL_LocateTaskRecordByName(const char *Name); + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Finds a counter table record matching the given name + * + * Helper function, aids in finding a counter record from a name string. + * Must be called while locked. + * + * @returns pointer to table entry matching name, or NULL if not found + */ +CFE_TBL_GenCounterRecord_t *CFE_TBL_LocateCounterRecordByName(const char *Name); +#endif + +/* + * Availability check functions used in conjunction with CFE_ResourceId_FindNext() + */ + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Checks if Application slot is currently used + * + * Helper function, Aids in allocating a new ID by checking if + * a given ID is available. Must be called while locked. + * + * @returns false if slot is unused/available, true if used/unavailable + */ +bool CFE_TBL_CheckRegIdSlotUsed(CFE_ResourceId_t CheckId); + +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Checks if Library slot is currently used + * + * Helper function, Aids in allocating a new ID by checking if + * a given ID is available. Must be called while locked. + * + * @returns false if slot is unused/available, true if used/unavailable + */ +bool CFE_TBL_CheckSharedBufferSlotUsed(CFE_ResourceId_t CheckId); + +#ifdef jphfix +/*---------------------------------------------------------------------------------------*/ +/** + * @brief Checks if Counter slot is currently used + * + * Helper function, Aids in allocating a new ID by checking if + * a given ID is available. Must be called while locked. + * + * @returns false if slot is unused/available, true if used/unavailable + */ +bool CFE_TBL_CheckCounterIdSlotUsed(CFE_ResourceId_t CheckId); +#endif + +#endif /* CFE_TBL_RESOURCE_H */ diff --git a/modules/tbl/fsw/src/cfe_tbl_task.h b/modules/tbl/fsw/src/cfe_tbl_task.h index f3274d230..710b7efcb 100644 --- a/modules/tbl/fsw/src/cfe_tbl_task.h +++ b/modules/tbl/fsw/src/cfe_tbl_task.h @@ -141,6 +141,11 @@ typedef struct char DataSource[OS_MAX_PATH_LEN]; /**< \brief Source of data put into buffer (filename or memory address) */ } CFE_TBL_LoadBuff_t; +/** + * Reference to an entry in the Registry table + */ +typedef int16 CFE_TBL_RegId_t; + /*******************************************************************************/ /** \brief Application to Table Access Descriptor ** @@ -152,7 +157,7 @@ typedef struct typedef struct { CFE_ES_AppId_t AppId; /**< \brief Application ID to verify access */ - int16 RegIndex; /**< \brief Index into Table Registry (a.k.a. - Global Table #) */ + CFE_TBL_RegId_t RegIndex; /**< \brief Index into Table Registry (a.k.a. - Global Table #) */ CFE_TBL_Handle_t PrevLink; /**< \brief Index of previous access descriptor in linked list */ CFE_TBL_Handle_t NextLink; /**< \brief Index of next access descriptor in linked list */ bool UsedFlag; /**< \brief Indicates whether this descriptor is being used or not */ @@ -328,10 +333,10 @@ typedef struct CFE_TBL_AccessDescriptor_t Handles[CFE_PLATFORM_TBL_MAX_NUM_HANDLES]; /**< \brief Array of Access Descriptors */ CFE_TBL_RegistryRec_t Registry[CFE_PLATFORM_TBL_MAX_NUM_TABLES]; /**< \brief Array of Table Registry Records */ CFE_TBL_CritRegRec_t - CritReg[CFE_PLATFORM_TBL_MAX_CRITICAL_TABLES]; /**< \brief Array of Critical Table Registry Records */ + CritReg[CFE_PLATFORM_TBL_MAX_CRITICAL_TABLES]; /**< \brief Array of Critical Table Registry Records */ CFE_TBL_BufParams_t Buf; /**< \brief Parameters associated with Table Task's Memory Pool */ CFE_TBL_ValidationResult_t - ValidationResults[CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS]; /**< \brief Array of Table Validation Requests */ + ValidationResults[CFE_PLATFORM_TBL_MAX_NUM_VALIDATIONS]; /**< \brief Array of Table Validation Requests */ CFE_TBL_DumpControl_t DumpControlBlocks[CFE_PLATFORM_TBL_MAX_SIMULTANEOUS_LOADS]; /**< \brief Array of Dump-Only Dump Control Blocks */ diff --git a/modules/tbl/fsw/src/cfe_tbl_task_cmds.c b/modules/tbl/fsw/src/cfe_tbl_task_cmds.c index 832f05045..44b62fbda 100644 --- a/modules/tbl/fsw/src/cfe_tbl_task_cmds.c +++ b/modules/tbl/fsw/src/cfe_tbl_task_cmds.c @@ -1095,7 +1095,7 @@ bool CFE_TBL_DumpRegistryGetter(void *Meta, uint32 RecordNum, void **Buffer, siz } /* Unlock now - remainder of data gathering uses ES */ - CFE_TBL_UnlockRegistry(); + CFE_TBL_TxnUnlockRegistry(NULL); } /* diff --git a/modules/tbl/fsw/src/cfe_tbl_transaction.c b/modules/tbl/fsw/src/cfe_tbl_transaction.c new file mode 100644 index 000000000..a1fcc9d28 --- /dev/null +++ b/modules/tbl/fsw/src/cfe_tbl_transaction.c @@ -0,0 +1,647 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/* +** File: cfe_tbl_transaction.c +** +** Purpose: cFE Table Services (TBL) utility function source file +** +** Author: D. Kobe/the Hammers Company, Inc. +** +** Notes: +** +*/ + +/* +** Required header files... +*/ +#include "cfe_tbl_module_all.h" + +#include +#include + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_TBL_TxnLockRegistry(CFE_TBL_TxnState_t *Txn) +{ + CFE_TBL_LockRegistry(); + Txn->RegIsLocked = true; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_TBL_TxnUnlockRegistry(CFE_TBL_TxnState_t *Txn) +{ + CFE_TBL_UnlockRegistry(); + Txn->RegIsLocked = false; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnInit(CFE_TBL_TxnState_t *Txn, bool CheckContext) +{ + CFE_Status_t Status; + + memset(Txn, 0, sizeof(*Txn)); + + /* + * Initialize the refs to a safe value. Preferably + * these should be zero but currently they are non-zero + * and thus "memset()" above does not make them safe. + */ + Txn->Handle = CFE_TBL_BAD_TABLE_HANDLE; + Txn->RegId = CFE_TBL_NOT_FOUND; + + /* Check to make sure App ID is legit */ + if (CheckContext) + { + Status = CFE_ES_GetAppID(&Txn->AppId); + } + else + { + Status = CFE_SUCCESS; + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnStartFromName(CFE_TBL_TxnState_t *Txn, const char *TblName, uint32 AllowedContext) +{ + CFE_Status_t Status; + + Status = CFE_TBL_TxnInit(Txn, AllowedContext != CFE_TBL_TxnContext_UNDEFINED); + + if (Status == CFE_SUCCESS) + { + CFE_TBL_TxnLockRegistry(Txn); + + Status = CFE_TBL_TxnFindRegByName(Txn, TblName); + } + + if (Status != CFE_SUCCESS) + { + /* If returning with an error, should also unlock the registry */ + CFE_TBL_TxnFinish(Txn); + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnStartFromHandle(CFE_TBL_TxnState_t *Txn, CFE_TBL_Handle_t TblHandle, uint32 AllowedContext) +{ + CFE_Status_t Status; + uint32 AccessAllowed; + CFE_TBL_AccessDescriptor_t *AccessDescPtr; + CFE_TBL_RegistryRec_t * RegRecPtr; + + AccessAllowed = 0; + Status = CFE_TBL_TxnInit(Txn, AllowedContext != CFE_TBL_TxnContext_UNDEFINED); + + if (Status == CFE_SUCCESS) + { + /* + * Check if the caller is actually table services + * (this is like the "root user" - most/all actions allowed) + */ + if (CFE_RESOURCEID_TEST_EQUAL(Txn->AppId, CFE_TBL_Global.TableTaskAppId)) + { + Txn->CallContext |= CFE_TBL_TxnContext_TABLE_SERVICES; + AccessAllowed |= ~AccessAllowed; /* Set all bits */ + } + + /* Need to lock before actually looking at the descriptor */ + CFE_TBL_TxnLockRegistry(Txn); + + Txn->AccessDescPtr = CFE_TBL_LocateAccessDescriptorByHandle(TblHandle); + if (!CFE_TBL_AccessDescriptorIsMatch(Txn->AccessDescPtr, TblHandle)) + { + /* Access descriptor is not good */ + Status = CFE_TBL_ERR_INVALID_HANDLE; + } + else + { + /* Access descriptor is good - check if caller is the descriptor owner */ + AccessDescPtr = Txn->AccessDescPtr; + if (CFE_RESOURCEID_TEST_EQUAL(Txn->AppId, AccessDescPtr->AppId)) + { + /* The calling app owns this access descriptor */ + Txn->CallContext |= CFE_TBL_TxnContext_ACCESSOR_APP; + } + + /* Now check the underlying registry entry */ + Txn->RegRecPtr = CFE_TBL_LocateRegistryRecordByID(AccessDescPtr->RegIndex); + if (!CFE_TBL_RegistryRecordIsMatch(Txn->RegRecPtr, AccessDescPtr->RegIndex)) + { + /* This means the access descriptor is stale */ + Status = CFE_TBL_ERR_UNREGISTERED; + } + else + { + /* The registry record is good, check if the caller is the owner */ + RegRecPtr = Txn->RegRecPtr; + if (CFE_RESOURCEID_TEST_EQUAL(Txn->AppId, RegRecPtr->OwnerAppId)) + { + /* The calling app owns this registry entry */ + Txn->CallContext |= CFE_TBL_TxnContext_OWNER_APP; + } + } + } + + /* If the descriptors all checked out, now check the calling context is whats required */ + if (Status == CFE_SUCCESS) + { + AccessAllowed |= Txn->CallContext; + + if ((AccessAllowed & AllowedContext) != AllowedContext) + { + Status = CFE_TBL_ERR_NO_ACCESS; + Txn->PendingEventId = CFE_TBL_HANDLE_ACCESS_ERR_EID; + } + } + } + + if (Status != CFE_SUCCESS) + { + /* If returning with an error, should also unlock the registry */ + CFE_TBL_TxnFinish(Txn); + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_TBL_TxnFinish(CFE_TBL_TxnState_t *Txn) +{ + if (Txn->RegIsLocked) + { + /* If returning with an error, must also unlock the registry */ + CFE_TBL_TxnUnlockRegistry(Txn); + } +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_FindAccessDescriptorForSelf(CFE_TBL_TxnState_t *Txn) +{ + CFE_Status_t Status = CFE_TBL_ERR_UNREGISTERED; + CFE_TBL_Handle_t AccessIndex; + CFE_TBL_RegistryRec_t *RegRecPtr; + + /* Find the existing access descriptor for the table */ + RegRecPtr = CFE_TBL_TxnRegRec(Txn); + AccessIndex = RegRecPtr->HeadOfAccessList; + + while (AccessIndex != CFE_TBL_END_OF_LIST) + { + if ((CFE_TBL_Global.Handles[AccessIndex].UsedFlag == true) && + CFE_RESOURCEID_TEST_EQUAL(CFE_TBL_Global.Handles[AccessIndex].AppId, CFE_TBL_TxnAppId(Txn)) && + (CFE_TBL_Global.Handles[AccessIndex].RegIndex == CFE_TBL_TxnRegId(Txn))) + { + Txn->Handle = AccessIndex; + Txn->AccessDescPtr = &CFE_TBL_Global.Handles[AccessIndex]; + break; + } + + AccessIndex = CFE_TBL_Global.Handles[AccessIndex].NextLink; + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnGetTableStatus(CFE_TBL_TxnState_t *Txn) +{ + int32 Status; + CFE_TBL_RegistryRec_t *RegRecPtr = CFE_TBL_TxnRegRec(Txn); + + /* Perform validations prior to performing any updates */ + if (RegRecPtr->LoadPending) + { + Status = CFE_TBL_INFO_UPDATE_PENDING; + } + else if ((RegRecPtr->ValidateActiveIndex != CFE_TBL_NO_VALIDATION_PENDING) || + (RegRecPtr->ValidateInactiveIndex != CFE_TBL_NO_VALIDATION_PENDING)) + { + Status = CFE_TBL_INFO_VALIDATION_PENDING; + } + else if (RegRecPtr->DumpControlIndex != CFE_TBL_NO_DUMP_PENDING) + { + Status = CFE_TBL_INFO_DUMP_PENDING; + } + else + { + Status = CFE_SUCCESS; + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnRemoveAccessLink(CFE_TBL_TxnState_t *Txn) +{ + int32 Status = CFE_SUCCESS; + CFE_TBL_AccessDescriptor_t *AccessDescPtr = CFE_TBL_TxnAccDesc(Txn); + CFE_TBL_RegistryRec_t * RegRecPtr = CFE_TBL_TxnRegRec(Txn); + + /* + * NOTE: In all cases where this is invoked, the registry should + * already be locked under the transaction object + */ + + /* If we are removing the head of the linked list, then point */ + /* the head pointer to the link after this one */ + if (AccessDescPtr->PrevLink == CFE_TBL_END_OF_LIST) + { + RegRecPtr->HeadOfAccessList = AccessDescPtr->NextLink; + + /* Update the next link, if there is one, to be the new head of the list */ + if (AccessDescPtr->NextLink != CFE_TBL_END_OF_LIST) + { + CFE_TBL_Global.Handles[AccessDescPtr->NextLink].PrevLink = CFE_TBL_END_OF_LIST; + } + } + else /* Access Descriptor is not the head of the list */ + { + /* Set the next link on the previous link to the next link of the link being removed */ + CFE_TBL_Global.Handles[AccessDescPtr->PrevLink].NextLink = AccessDescPtr->NextLink; + + /* If this link is not the end of the list, then complete two way linkage */ + /* by setting the next link's previous link to the previous link of the link being removed */ + if (AccessDescPtr->NextLink != CFE_TBL_END_OF_LIST) + { + CFE_TBL_Global.Handles[AccessDescPtr->NextLink].PrevLink = AccessDescPtr->PrevLink; + } + } + + /* Return the Access Descriptor to the pool */ + AccessDescPtr->UsedFlag = false; + + /* If this was the last Access Descriptor for this table, we can free the memory buffers as well */ + if (RegRecPtr->HeadOfAccessList == CFE_TBL_END_OF_LIST) + { + /* Only free memory that we have allocated. If the image is User Defined, then don't bother */ + if (RegRecPtr->UserDefAddr == false) + { + /* Free memory allocated to buffers */ + Status = CFE_ES_PutPoolBuf(CFE_TBL_Global.Buf.PoolHdl, RegRecPtr->Buffers[0].BufferPtr); + RegRecPtr->Buffers[0].BufferPtr = NULL; + + if (Status < 0) + { + CFE_ES_WriteToSysLog("%s: PutPoolBuf[0] Fail Stat=0x%08X, Hndl=0x%08lX, Buf=0x%08lX\n", __func__, + (unsigned int)Status, CFE_RESOURCEID_TO_ULONG(CFE_TBL_Global.Buf.PoolHdl), + (unsigned long)RegRecPtr->Buffers[0].BufferPtr); + } + + /* If a double buffered table, then free the second buffer as well */ + if (RegRecPtr->DoubleBuffered) + { + Status = CFE_ES_PutPoolBuf(CFE_TBL_Global.Buf.PoolHdl, RegRecPtr->Buffers[1].BufferPtr); + RegRecPtr->Buffers[1].BufferPtr = NULL; + + if (Status < 0) + { + CFE_ES_WriteToSysLog("%s: PutPoolBuf[1] Fail Stat=0x%08X, Hndl=0x%08lX, Buf=0x%08lX\n", __func__, + (unsigned int)Status, CFE_RESOURCEID_TO_ULONG(CFE_TBL_Global.Buf.PoolHdl), + (unsigned long)RegRecPtr->Buffers[1].BufferPtr); + } + } + else + { + /* If a shared buffer has been allocated to the table, then release it as well */ + if (RegRecPtr->LoadInProgress != CFE_TBL_NO_LOAD_IN_PROGRESS) + { + /* Free the working buffer */ + CFE_TBL_Global.LoadBuffs[RegRecPtr->LoadInProgress].Taken = false; + RegRecPtr->LoadInProgress = CFE_TBL_NO_LOAD_IN_PROGRESS; + } + } + } + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnGetTableAddress(CFE_TBL_TxnState_t *Txn, void **TblPtr) +{ + int32 Status; + CFE_TBL_AccessDescriptor_t *AccessDescPtr = CFE_TBL_TxnAccDesc(Txn); + CFE_TBL_RegistryRec_t * RegRecPtr = CFE_TBL_TxnRegRec(Txn); + CFE_ES_AppId_t ThisAppId; + CFE_TBL_Handle_t TblHandle; + + /* If table is unowned, then owner must have unregistered it when we weren't looking */ + if (CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, CFE_TBL_NOT_OWNED)) + { + Status = CFE_TBL_ERR_UNREGISTERED; + + ThisAppId = CFE_TBL_TxnAppId(Txn); + TblHandle = CFE_TBL_TxnHandle(Txn); + + CFE_ES_WriteToSysLog("%s: App(%lu) attempt to access unowned Tbl Handle=%d\n", __func__, + CFE_RESOURCEID_TO_ULONG(ThisAppId), (int)TblHandle); + } + else /* Table Registry Entry is valid */ + { + /* Lock the table and return the current pointer */ + AccessDescPtr->LockFlag = true; + + /* Save the buffer we are using in the access descriptor */ + /* This is used to ensure that if the buffer becomes inactive while */ + /* we are using it, no one will modify it until we are done */ + AccessDescPtr->BufferIndex = RegRecPtr->ActiveBufferIndex; + + *TblPtr = RegRecPtr->Buffers[AccessDescPtr->BufferIndex].BufferPtr; + + /* Return any pending warning or info status indicators */ + Status = CFE_TBL_TxnGetNextNotification(Txn); + + /* Clear Table Updated Notify Bit so that caller only gets it once */ + AccessDescPtr->Updated = false; + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnGetNextNotification(CFE_TBL_TxnState_t *Txn) +{ + CFE_Status_t Status = CFE_SUCCESS; + CFE_TBL_AccessDescriptor_t *AccessDescPtr = CFE_TBL_TxnAccDesc(Txn); + CFE_TBL_RegistryRec_t * RegRecPtr = CFE_TBL_TxnRegRec(Txn); + + if (!RegRecPtr->TableLoadedOnce) + { + /* If the table has never been loaded, return an error code for the address */ + Status = CFE_TBL_ERR_NEVER_LOADED; + } + else if (AccessDescPtr->Updated) + { + /* If the table has been updated recently, return the update status */ + Status = CFE_TBL_INFO_UPDATED; + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnFindRegByName(CFE_TBL_TxnState_t *Txn, const char *TblName) +{ + CFE_Status_t Status = CFE_TBL_ERR_INVALID_NAME; + int16 i = 0; + + while (i < CFE_PLATFORM_TBL_MAX_NUM_TABLES) + { + /* Check to see if the record is currently being used */ + if (!CFE_RESOURCEID_TEST_EQUAL(CFE_TBL_Global.Registry[i].OwnerAppId, CFE_TBL_NOT_OWNED)) + { + /* Perform a case sensitive name comparison */ + if (strcmp(TblName, CFE_TBL_Global.Registry[i].Name) == 0) + { + /* If the names match, then return the index */ + Txn->RegId = i; + Txn->RegRecPtr = &CFE_TBL_Global.Registry[i]; + + Status = CFE_SUCCESS; + break; + } + } + + /* Point to next record in the Table Registry */ + i++; + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnAllocateRegistryEntry(CFE_TBL_TxnState_t *Txn) +{ + CFE_Status_t Status = CFE_TBL_ERR_REGISTRY_FULL; + int16 i = 0; + + while (i < CFE_PLATFORM_TBL_MAX_NUM_TABLES) + { + /* A Table Registry is only "Free" when there isn't an owner AND */ + /* all other applications are not sharing or locking the table */ + if (CFE_RESOURCEID_TEST_EQUAL(CFE_TBL_Global.Registry[i].OwnerAppId, CFE_TBL_NOT_OWNED) && + (CFE_TBL_Global.Registry[i].HeadOfAccessList == CFE_TBL_END_OF_LIST)) + { + Txn->RegId = i; + Txn->RegRecPtr = &CFE_TBL_Global.Registry[i]; + + Status = CFE_SUCCESS; + break; + } + + i++; + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnAllocateHandle(CFE_TBL_TxnState_t *Txn) +{ + CFE_Status_t Status = CFE_TBL_ERR_HANDLES_FULL; + int16 i = 0; + + while (i < CFE_PLATFORM_TBL_MAX_NUM_HANDLES) + { + if (CFE_TBL_Global.Handles[i].UsedFlag == false) + { + Txn->Handle = i; + Txn->AccessDescPtr = &CFE_TBL_Global.Handles[i]; + + Status = CFE_SUCCESS; + break; + } + i++; + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +CFE_Status_t CFE_TBL_TxnCheckDuplicateRegistration(CFE_TBL_TxnState_t *Txn, const char *TblName, size_t Size) +{ + CFE_Status_t Status = CFE_SUCCESS; + CFE_TBL_RegistryRec_t *RegRecPtr; + CFE_ES_AppId_t ThisAppId; + + /* Check for duplicate table name */ + Status = CFE_TBL_TxnFindRegByName(Txn, TblName); + + /* Check to see if table is already in the registry */ + if (Status == CFE_SUCCESS) + { + /* Get pointer to Registry Record Entry to speed up processing */ + RegRecPtr = CFE_TBL_TxnRegRec(Txn); + ThisAppId = CFE_TBL_TxnAppId(Txn); + + /* If this app previously owned the table, then allow them to re-register */ + if (CFE_RESOURCEID_TEST_EQUAL(RegRecPtr->OwnerAppId, ThisAppId)) + { + /* If the new table is the same size as the old, then no need to reallocate memory */ + if (Size != RegRecPtr->Size) + { + /* If the new size is different, the old table must be deleted but this */ + /* function can't do that because it is probably shared and is probably */ + /* still being accessed. Someone else will need to clean up this mess. */ + Status = CFE_TBL_ERR_DUPLICATE_DIFF_SIZE; + + CFE_ES_WriteToSysLog("%s: Attempt to register existing table ('%s') with different size(%d!=%d)\n", + __func__, TblName, (int)Size, (int)RegRecPtr->Size); + } + else + { + /* + * The intent is to fill in the correct handle, but interestingly this + * does not detect/propagate the error if it fails + */ + CFE_TBL_FindAccessDescriptorForSelf(Txn); + + /* Warn calling application that this is a duplicate registration */ + Status = CFE_TBL_WARN_DUPLICATE; + } + } + else /* Duplicate named table owned by another Application */ + { + Status = CFE_TBL_ERR_DUPLICATE_NOT_OWNED; + + CFE_ES_WriteToSysLog("%s: App(%lu) Registering Duplicate Table '%s' owned by App(%lu)\n", __func__, + CFE_RESOURCEID_TO_ULONG(ThisAppId), TblName, + CFE_RESOURCEID_TO_ULONG(RegRecPtr->OwnerAppId)); + } + } + else /* Table not already in registry */ + { + /* Locate empty slot in table registry */ + Status = CFE_TBL_TxnAllocateRegistryEntry(Txn); + } + + return Status; +} + +/*---------------------------------------------------------------- + * + * Application-scope internal function + * See description in header file for argument/return detail + * + *-----------------------------------------------------------------*/ +void CFE_TBL_TxnConnectAccessDescriptor(CFE_TBL_TxnState_t *Txn) +{ + /* Initialize the Table Access Descriptor */ + CFE_TBL_AccessDescriptor_t *AccessDescPtr = CFE_TBL_TxnAccDesc(Txn); + CFE_TBL_RegistryRec_t * RegRecPtr = CFE_TBL_TxnRegRec(Txn); + + AccessDescPtr->AppId = CFE_TBL_TxnAppId(Txn); + AccessDescPtr->LockFlag = false; + AccessDescPtr->Updated = false; + + if ((RegRecPtr->DumpOnly) && (!RegRecPtr->UserDefAddr)) + { + /* Dump Only Tables are assumed to be loaded at all times unless the address is specified */ + /* by the application. In that case, it isn't loaded until the address is specified */ + RegRecPtr->TableLoadedOnce = true; + } + + AccessDescPtr->RegIndex = CFE_TBL_TxnRegId(Txn); + + AccessDescPtr->PrevLink = CFE_TBL_END_OF_LIST; /* We are the head of the list */ + AccessDescPtr->NextLink = CFE_TBL_END_OF_LIST; /* We are the end of the list */ + + AccessDescPtr->UsedFlag = true; + + /* Make sure the Table Registry entry points to First Access Descriptor */ + RegRecPtr->HeadOfAccessList = CFE_TBL_TxnHandle(Txn); +} diff --git a/modules/tbl/fsw/src/cfe_tbl_transaction.h b/modules/tbl/fsw/src/cfe_tbl_transaction.h new file mode 100644 index 000000000..822a5cb2d --- /dev/null +++ b/modules/tbl/fsw/src/cfe_tbl_transaction.h @@ -0,0 +1,518 @@ +/************************************************************************ + * NASA Docket No. GSC-18,719-1, and identified as “core Flight System: Bootes” + * + * Copyright (c) 2020 United States Government as represented by the + * Administrator of the National Aeronautics and Space Administration. + * All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ************************************************************************/ + +/** + * @file + * + * Purpose: cFE Table Services (TBL) utility function interface file + * + * Author: D. Kobe/the Hammers Company, Inc. + * + * Notes: + * + */ + +#ifndef CFE_TBL_TRANSACTION_H +#define CFE_TBL_TRANSACTION_H + +/* + * Required header files... +*/ +#include "cfe_es_api_typedefs.h" +#include "cfe_tbl_api_typedefs.h" +#include "cfe_platform_cfg.h" +#include "cfe_tbl_resource.h" +#include "cfe_tbl_eventids.h" + +/********************* Type Definitions ***************************/ + +/** + * Bit mask values for context of a table services transaction + * + * An "Accessor" of a table has a handle to it, and a corresponding Access Descriptor. + * An "Owner" of a table is the one that registered it. + * + * Note it is possible for multiple bits to be set. In particular, the original + * registrant of a table is generally both the owner and an accessor of the table. + */ +typedef enum CFE_TBL_TxnContext +{ + CFE_TBL_TxnContext_UNDEFINED = 0, + + /**< The caller is table services itself (administrative action) */ + CFE_TBL_TxnContext_TABLE_SERVICES = 0x1, + + /** The caller is the app that originally registered the table (owner) */ + CFE_TBL_TxnContext_OWNER_APP = 0x2, + + /** The caller is app that has an accessor */ + CFE_TBL_TxnContext_ACCESSOR_APP = 0x4, + + /** The caller is an app that is not associated with the table */ + CFE_TBL_TxnContext_OTHER_APP = 0x8, + + /** All context allowed (convenience value) */ + CFE_TBL_TxnContext_ALL = 0x0F + +} CFE_TBL_TxnContext_Enum_t; + +/** + * The table transaction object + * + * This tracks all the relevant information from the current API request, + * including the caller context (appID), the table handle/access descriptor + * and registry entry being acted upon, whether the registry is locked, etc. + * + * All public APIs should use fields within this object rather than + * managing these data items individually on the stack. + * + * The object can be extended as necessary. Ideally, it should track everything + * that is in the process of being changed, such that changes can be reliably + * and consistently un-done if a later step in the process fails. The goal + * should always be to either make a complete transaction, or leave the global + * state as it was at the start of the transaction (never something "half-done"). + * + * Importantly, the transaction object serves as a local snapshot of the relevant + * values from the registry, so that if they need to be refereced outside of a locked + * context (e.g. for event or syslog reporting) the copies in this object can still + * be used after the transaction completes. + */ +typedef struct CFE_TBL_TxnState +{ + CFE_ES_AppId_t AppId; + CFE_TBL_Handle_t Handle; + CFE_TBL_RegId_t RegId; + + uint32 CallContext; + uint16 PendingEventId; + bool RegIsLocked; + + CFE_TBL_AccessDescriptor_t *AccessDescPtr; + CFE_TBL_RegistryRec_t * RegRecPtr; + +} CFE_TBL_TxnState_t; + +/***************************** Simple Accessors **********************************/ + +/* + * These functions retrieve specific items from the transaction object. The intent + * is to (eventually) replace individual local stack variables with these accessors. + */ + +/** + * Gets the table handle + */ +static inline CFE_TBL_Handle_t CFE_TBL_TxnHandle(const CFE_TBL_TxnState_t *Txn) +{ + return Txn->Handle; +} + +/** + * Gets the access descriptor object + */ +static inline CFE_TBL_AccessDescriptor_t *CFE_TBL_TxnAccDesc(const CFE_TBL_TxnState_t *Txn) +{ + return Txn->AccessDescPtr; +} + +/** + * Gets the registry entry ID + */ +static inline CFE_TBL_RegId_t CFE_TBL_TxnRegId(const CFE_TBL_TxnState_t *Txn) +{ + return Txn->RegId; +} + +/** + * Gets the registry record object + */ +static inline CFE_TBL_RegistryRec_t *CFE_TBL_TxnRegRec(const CFE_TBL_TxnState_t *Txn) +{ + return Txn->RegRecPtr; +} + +/** + * Gets the calling context AppID + * + * Otherwise known as "ThisAppId" in many existing functions + */ +static inline CFE_ES_AppId_t CFE_TBL_TxnAppId(const CFE_TBL_TxnState_t *Txn) +{ + return Txn->AppId; +} + +/***************************** Function Prototypes **********************************/ + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Locks access to the Table Registry + * + * \par Description + * Locks the Table Registry to prevent multiple tasks/threads + * from modifying it at once. + * + * \par Assumptions, External Events, and Notes: + * None + * + */ +void CFE_TBL_TxnLockRegistry(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Unlocks access to the Table Registry + * + * \par Description + * Unlocks Table Registry to allow other tasks/threads to + * modify the Table Registry contents. + * + * \par Assumptions, External Events, and Notes: + * None + * + */ +void CFE_TBL_TxnUnlockRegistry(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set a transaction to a safe initial state + * + * \par Description + * Clears the transaction object and sets everything to initial state + * + * After this call, all internal object pointers will be NULL and all handles/IDs will + * be set the the respective INVALID value. + * + * The "CheckContext" parameter indicates whether the calling context (AppID) + * is relevant for the current operation. If set true, then this will invoke + * CFE_ES_GetAppID() to determine the calling context, which can be queried + * using CFE_TBL_TxnAppId(). If set to false, this call into ES will be skipped. + * This is intended as an optimization as to save the time cost of the call into + * ES to determine the app ID (which is small but not zero). + * + * \par Assumptions, External Events, and Notes: + * This call does _NOT_ lock the registry. When starting a transaction using this + * method, the caller must manage the registry lock via CFE_TBL_TxnLockRegistry() and + * CFE_TBL_TxnUnlockRegistry(). + * + * \param[inout] Txn The transaction object to operate on + * \param[in] CheckContext Whether to determine the AppID of the caller (calling context) + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnInit(CFE_TBL_TxnState_t *Txn, bool CheckContext); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set a transaction to operate on an existing table handle + * + * \par Description + * Clears the transaction object and sets everything to operate on an existing + * table handle. + * + * After this call successfully completes, all internal object pointers will be + * pointing at the relevant global table entries and the registry will be left + * in a __LOCKED__ state. As this operates on an existing table handle, both + * the access descriptor and registry record must map to valid entries. + * + * __IMPORTANT__: If this call returns successfully, it MUST be followed by a call + * to CFE_TBL_TxnFinish() to unlock the registry and clean anything else up. + * + * The "AllowedContext" parameter should be passed as a bitmask of values defined + * in CFE_TBL_TxnContext_Enum_t, indicating which callers are allowable. This is + * intended to facilitate access controls (i.e. ensuring that the caller matches + * the access descriptor, typically). + * + * \par Assumptions, External Events, and Notes: + * Upon successful return, the table registry will be left in a LOCKED state. This + * call must be followed by a call to CFE_TBL_TxnFinish() to release that lock. + * + * If this call returns with an error, the registry will be left in an UNLOCKED state + * and no changes will be made (the caller should report the error and return). + * + * \param[inout] Txn The transaction object to operate on + * \param[in] TblHandle The table handle to operate on + * \param[in] AllowedContext The allowed calling context(s) + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnStartFromHandle(CFE_TBL_TxnState_t *Txn, CFE_TBL_Handle_t TblHandle, uint32 AllowedContext); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Set a transaction to operate on an existing table name + * + * \par Description + * Clears the transaction object and sets everything to operate on an existing + * table which is located by name. + * + * After this call successfully completes, all internal object pointers will be + * pointing at the relevant global table entries and the registry will be left + * in a __LOCKED__ state. However, as this operates on a table name (not a handle) + * only the registry record will point to a valid registry entry. The access + * descriptor will be left unset (NULL), as there isn't one in this context. + * + * __IMPORTANT__: If this call returns successfully, it MUST be followed by a call + * to CFE_TBL_TxnFinish() to unlock the registry and clean anything else up. + * + * The "AllowedContext" parameter should be passed as a bitmask of values defined + * in CFE_TBL_TxnContext_Enum_t, indicating which callers are allowable. This is + * intended to facilitate access controls (i.e. ensuring that the caller matches + * the access descriptor, typically). + * + * \par Assumptions, External Events, and Notes: + * Upon successful return, the table registry will be left in a LOCKED state. This + * call must be followed by a call to CFE_TBL_TxnFinish() to release that lock. + * + * If this call returns with an error, the registry will be left in an UNLOCKED state + * and no changes will be made (the caller should report the error and return). + * + * \param[inout] Txn The transaction object to operate on + * \param[in] TblHandle The table handle to operate on + * \param[in] AllowedContext The allowed calling context(s) + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnStartFromName(CFE_TBL_TxnState_t *Txn, const char *TblName, uint32 AllowedContext); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Completes the referenced transaction + * + * \par Description + * This function releases any resource(s) that were held as part of the transaction + * and performs any related post-transaction cleanup, if needed. + * + * __IMPORTANT__: This function MUST be invoked after any successful call to a + * Transaction Start routine (CFE_TBL_TxnStartFromName(), CFE_TBL_TxnStartFromHandle()) + * + * \par Assumptions, External Events, and Notes: + * Identifiers within the transaction object will remain valid, but pointers to table + * and dedscriptor records should NOT be used after finishing a transaction. + * + * \param[inout] Txn The transaction object to operate on + */ +void CFE_TBL_TxnFinish(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Gets the table status associated with a transaction + * + * \par Description + * Returns the current status of the table being referred to in the transaction object + * + * \par Assumptions, External Events, and Notes: + * None + * + * \param[inout] Txn The transaction object to operate on + * + * \returns CFE_SUCCESS if nothing is pending, or relevent CFE status code ("info" status) + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnGetTableStatus(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Finds the access descriptor associated with the current registry entry, if any + * + * \par Description + * For a transaction object that is referring to a registry entry, this searches + * for an access descriptor that matches this combination of appid (calling context) + * and table registry entry. + * + * For example, the CFE_TBL_TxnStartFromName() function will find only a registry + * entry (as these have the name) but there could be several access descriptors + * pointing at the same table registration entry, as is the case with shared tables. + * + * \par Assumptions, External Events, and Notes: + * The registry should be locked (as part of the transaction) prior to invoking this + * + * \param[inout] Txn The transaction object to operate on + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_FindAccessDescriptorForSelf(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Removes Access Descriptor from Table's linked list of Access Descriptors + * + * \par Description + * Removes the given Access Descriptor from the Linked List + * of Access Descriptors associated with the table specified + * in the Access Descriptor itself. + * + * \par Assumptions, External Events, and Notes: + * + * \param[inout] Txn The transaction object to operate on + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnRemoveAccessLink(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Obtains the data address for the specified table + * + * \par Description + * Validates the given TblHandle, finds the location of the + * Table data and returns the address to the data to the caller. + * + * \par Assumptions, External Events, and Notes: + * -# It is possible that an Application that was sharing a table + * would discover, upon making this call, that the table has + * been unregistered by another Application. In this situation, + * this function would return #CFE_TBL_ERR_UNREGISTERED. + * + * \param[inout] Txn The transaction object to operate on + * \param[out] TblPtr Pointer to pointer that will hold address of data upon return. + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnGetTableAddress(CFE_TBL_TxnState_t *Txn, void **TblPtr); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Returns any pending non-error status code for the specified table. + * + * \par Description + * Returns any pending non-error status code for the specified table. + * + * \par Assumptions, External Events, and Notes: + * Note: This function assumes the TblHandle has been validated. + * + * \param[inout] Txn The transaction object to operate on + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnGetNextNotification(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Returns the Registry Index for the specified Table Name + * + * \par Description + * Locates given Table Name in the Table Registry and + * returns the appropriate Registry Index. + * + * \par Assumptions, External Events, and Notes: + * None + * + * \param[inout] Txn The transaction object to operate on + * \param[in] TblName - Pointer to character string containing complete + * Table Name (of the format "AppName.TblName"). + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnFindRegByName(CFE_TBL_TxnState_t *Txn, const char *TblName); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Locates a free slot in the Table Registry. + * + * \par Description + * Locates a free slot in the Table Registry. + * + * If successful, the internal pointer will be set to the newly allocated + * registry record. The accessor functions CFE_TBL_TxnRegRec() and + * CFE_TBL_TxnRegId() may be used to retrieve the pointer and handle, + * respectively. + * + * \par Assumptions, External Events, and Notes: + * Note: This function assumes the registry has been locked. + * + * \param[inout] Txn The transaction object to operate on + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnAllocateRegistryEntry(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Locates a free Access Descriptor in the Table Handles Array. + * + * \par Description + * Locates a free Access Descriptor in the Table Handles Array. + * + * If successful, the internal pointer will be set to the newly allocated + * access descriptor. The accessor functions CFE_TBL_TxnAccDesc() and + * CFE_TBL_TxnHandle() may be used to retrieve the pointer and handle, + * respectively. + * + * \par Assumptions, External Events, and Notes: + * Note: This function assumes the registry has been locked. + * No association is made between the accessor and the registry object here. The + * association is made via a separate call. This simply finds an open entry. + * + * \param[inout] Txn The transaction object to operate on + * + * \returns CFE_SUCCESS normally, or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnAllocateHandle(CFE_TBL_TxnState_t *Txn); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Checks if a table is already registered in the Table Registry + * + * \par Description + * This routine searches the Table Registry for a table with the specified name, + * owning app and size. If a match is found, the same handle is returned. If a + * match is not found, the function will locate a free slot in the table registry + * (unless it's already full). + * + * \par Assumptions, External Events, and Notes: + * A return value of CFE_SUCCESS indicates that this is NOT a duplicate registration, + * meaning it is OK to proceed with creating a new table entry. If this is a duplicate, + * this may return one of the "warning" status codes indicating the nature of duplication. + * + * \param[inout] Txn The transaction object to operate on + * \param[in] TblName The name of the table that is the pending in a registration request + * \param[in] Size The size of th table that is pending in a registration request + * + * \returns CFE_SUCCESS or relevent CFE status code + * \retval #CFE_SUCCESS \copydoc CFE_SUCCESS + */ +CFE_Status_t CFE_TBL_TxnCheckDuplicateRegistration(CFE_TBL_TxnState_t *Txn, const char *TblName, size_t Size); + +/*---------------------------------------------------------------------------------------*/ +/** + * \brief Connects a Table Access Descriptor to the current Registry record + * + * \par Description + * As part of setting up a new table, the transaction should have selected a + * (pending) access descriptor/handle and a registry record to use for the table. + * + * This call will make the necessary connections to associate the access descriptor + * with the registry record selected in the transaction object + * + * \par Assumptions, External Events, and Notes: + * None + * + * \param[inout] Txn The transaction object to operate on + */ +void CFE_TBL_TxnConnectAccessDescriptor(CFE_TBL_TxnState_t *Txn); + +#endif /* CFE_TBL_TRANSACTION_H */ diff --git a/modules/tbl/ut-coverage/tbl_UT.c b/modules/tbl/ut-coverage/tbl_UT.c index b130539d4..02f00f93f 100644 --- a/modules/tbl/ut-coverage/tbl_UT.c +++ b/modules/tbl/ut-coverage/tbl_UT.c @@ -3219,6 +3219,7 @@ void Test_CFE_TBL_TblMod(void) */ void Test_CFE_TBL_Internal(void) { + CFE_TBL_TxnState_t Txn; CFE_TBL_LoadBuff_t * WorkingBufferPtr; CFE_TBL_RegistryRec_t * RegRecPtr; CFE_TBL_AccessDescriptor_t *AccessDescPtr; @@ -3488,12 +3489,15 @@ void Test_CFE_TBL_Internal(void) CFE_UtAssert_EVENTSENT(CFE_TBL_FILE_TBL_HDR_ERR_EID); CFE_UtAssert_EVENTCOUNT(1); - /* Test CFE_TBL_RemoveAccessLink response to a failure to put back the + /* Test CFE_TBL_TxnRemoveAccessLink response to a failure to put back the * memory buffer for a double buffered table + * Note: CFE_TBL_Unregister() does not propagate this error to the caller, + * as there is no recourse and the table is still unregistered. However, it + * is invoked here for internal coverage paths. */ UT_InitData(); UT_SetDeferredRetcode(UT_KEY(CFE_ES_PutPoolBuf), 2, CFE_ES_ERR_RESOURCEID_NOT_VALID); - UtAssert_INT32_EQ(CFE_TBL_RemoveAccessLink(App1TblHandle2), CFE_ES_ERR_RESOURCEID_NOT_VALID); + UtAssert_INT32_EQ(CFE_TBL_Unregister(App1TblHandle2), CFE_SUCCESS); CFE_UtAssert_EVENTCOUNT(0); /* EarlyInit - Table Registry Mutex Create Failure */ @@ -3794,21 +3798,29 @@ void Test_CFE_TBL_Internal(void) CFE_UtAssert_SUCCESS(CFE_TBL_EarlyInit()); CFE_UtAssert_EVENTCOUNT(0); - /* Test CFE_TBL_CheckAccessRights response when the application ID matches - * the table task application ID - */ + /* Test starting a transaction where the handle is OK but the underlying registry record is invalid */ UT_InitData(); - CFE_TBL_Global.TableTaskAppId = UT_TBL_APPID_1; - CFE_UtAssert_SUCCESS(CFE_TBL_CheckAccessRights(App2TblHandle1, UT_TBL_APPID_1)); - CFE_UtAssert_EVENTCOUNT(0); + memset(&Txn, 0, sizeof(Txn)); + CFE_TBL_Global.Handles[2].UsedFlag = true; + CFE_TBL_Global.Handles[2].RegIndex = CFE_TBL_END_OF_LIST; + UtAssert_INT32_EQ(CFE_TBL_TxnStartFromHandle(&Txn, App2TblHandle1, 0), CFE_TBL_ERR_UNREGISTERED); + + UT_InitData(); + memset(&Txn, 0, sizeof(Txn)); + CFE_TBL_Global.Handles[2].UsedFlag = true; + CFE_TBL_Global.Handles[2].RegIndex = 1 + CFE_PLATFORM_TBL_MAX_NUM_TABLES; + UtAssert_INT32_EQ(CFE_TBL_TxnStartFromHandle(&Txn, App2TblHandle1, 0), CFE_TBL_ERR_UNREGISTERED); + CFE_TBL_Global.Handles[2].UsedFlag = false; - /* Test CFE_TBL_FindFreeRegistryEntry response when the registry entry is + /* Test CFE_TBL_TxnAllocateRegistryEntry response when the registry entry is * not owned but is not at the end of the list */ UT_InitData(); + memset(&Txn, 0, sizeof(Txn)); CFE_TBL_Global.Registry[0].OwnerAppId = CFE_TBL_NOT_OWNED; CFE_TBL_Global.Registry[0].HeadOfAccessList = CFE_TBL_END_OF_LIST + 1; - UtAssert_INT32_EQ(CFE_TBL_FindFreeRegistryEntry(), 1); + CFE_UtAssert_SUCCESS(CFE_TBL_TxnAllocateRegistryEntry(&Txn)); + UtAssert_INT32_EQ(CFE_TBL_TxnRegId(&Txn), 1); CFE_UtAssert_EVENTCOUNT(0); /* Test CFE_TBL_LockRegistry response when an error occurs taking the mutex