From 0604676c20e3be94e1d494d80afe7420e4c7c151 Mon Sep 17 00:00:00 2001 From: Joseph Hickey Date: Thu, 28 Mar 2024 13:17:46 -0400 Subject: [PATCH] Fix #2538, table transaction initial implementation Initial implementation of transaction concept for table API calls. The basic transaction object wraps all of the relevant pointers and state information for the request being processed. --- modules/tbl/CMakeLists.txt | 2 + modules/tbl/fsw/src/cfe_tbl_api.c | 382 ++++++------- modules/tbl/fsw/src/cfe_tbl_internal.c | 492 +--------------- modules/tbl/fsw/src/cfe_tbl_internal.h | 206 ------- modules/tbl/fsw/src/cfe_tbl_module_all.h | 2 + modules/tbl/fsw/src/cfe_tbl_resource.c | 115 ++++ modules/tbl/fsw/src/cfe_tbl_resource.h | 516 +++++++++++++++++ modules/tbl/fsw/src/cfe_tbl_task.h | 11 +- modules/tbl/fsw/src/cfe_tbl_transaction.c | 647 ++++++++++++++++++++++ modules/tbl/fsw/src/cfe_tbl_transaction.h | 520 +++++++++++++++++ modules/tbl/ut-coverage/tbl_UT.c | 32 +- 11 files changed, 2046 insertions(+), 879 deletions(-) create mode 100644 modules/tbl/fsw/src/cfe_tbl_resource.c create mode 100644 modules/tbl/fsw/src/cfe_tbl_resource.h create mode 100644 modules/tbl/fsw/src/cfe_tbl_transaction.c create mode 100644 modules/tbl/fsw/src/cfe_tbl_transaction.h 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..4e9ec4406 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,20 @@ 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); + + ThisAppId = CFE_TBL_TxnAppId(&Txn); 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 +675,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 +687,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 +717,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 +730,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 +757,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 +774,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 +843,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 +1048,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 +1080,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 +1091,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 +1125,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 +1140,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 +1198,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 +1270,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_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..da61d1fe4 --- /dev/null +++ b/modules/tbl/fsw/src/cfe_tbl_transaction.h @@ -0,0 +1,520 @@ +/************************************************************************ + * 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 + * + * \param[inout] Txn The transaction object to operate on + */ +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 + * + * \param[inout] Txn The transaction object to operate on + */ +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] TblName The table name to find in the registry + * \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