Home

Resume

Blog

Teikitu


/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/*  »Project«   Teikitu Gaming System (TgS) (∂)
    »File«      TgS Common - Event MGR.c
    »Author«    Andrew Aye (EMail: mailto:andrew.aye@gmail.com, Web: http://www.andrewaye.com)
    »Version«   4.51 / »GUID« A9981407-3EC9-42AF-8B6F-8BE6DD919615                                                                                                        */
/*   -------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
/*  Copyright: © 2002-2017, Andrew Aye.  All Rights Reserved.
    This software is free for non-commercial use.  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
      following conditions are met:
        Redistribution of source code must retain this copyright notice, this list of conditions and the following disclaimers.
        Redistribution in binary form must reproduce this copyright notice, this list of conditions and the following disclaimers in the documentation and other materials
          provided with the distribution.
    The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission.
    The intellectual property rights of the algorithms used reside with Andrew Aye.
    You may not use this software, in whole or in part, in support of any commercial product without the express written consent of the author.
    There is no warranty or other guarantee of fitness of this software for any purpose. It is provided solely "as is".                                                   */
/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */

#include "TgS Common - Event MGR - Internal.h"
#include "TgS Common - Event MGR.inl"


/* == Common ============================================================================================================================================================ */

/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Private Data                                                                                                                                                          */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

TgCOMPILER_ASSERT( sizeof( g_apsEM_TE_FRM ) == KTgEM_MAX_TE_FRM_POOL*sizeof( P_STg2_EM_TE_Pool ), 0 );
TgCOMPILER_ASSERT( sizeof( g_apsEM_TE_SEC ) == KTgEM_MAX_TE_SEC_POOL*sizeof( P_STg2_EM_TE_Pool ), 1 );

P_STg2_EM_TE_Pool                           g_apsEM_TE_FRM[KTgEM_MAX_TE_FRM_POOL]; /*« Synchronization: g_sEM_TE_FRM_Lock */
P_STg2_EM_TE_Pool                           g_psEM_TE_FRM_Free_List;               /*« Synchronization: g_sEM_TE_FRM_Lock */
STg2_UTM_SN_ISO                             g_sEM_TE_FRM_Lock;
STg2_UTM_SN_ISO                             g_asEM_TE_FRM_Data_Lock[KTgEM_MAX_TE_FRM_POOL];
STg2_UTM_SN_ISO                             g_asEM_TE_FRM_NewDel_Lock[KTgEM_MAX_TE_FRM_POOL];

P_STg2_EM_TE_Pool                           g_apsEM_TE_SEC[KTgEM_MAX_TE_SEC_POOL]; /*« Synchronization: g_sEM_TE_SEC_Lock */
P_STg2_EM_TE_Pool                           g_psEM_TE_SEC_Free_List;               /*« Synchronization: g_sEM_TE_SEC_Lock */
STg2_UTM_SN_ISO                             g_sEM_TE_SEC_Lock;
STg2_UTM_SN_ISO                             g_asEM_TE_SEC_Data_Lock[KTgEM_MAX_TE_SEC_POOL];
STg2_UTM_SN_ISO                             g_asEM_TE_SEC_NewDel_Lock[KTgEM_MAX_TE_SEC_POOL];




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Types                                                                                                                                                      */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

enum { ETgEM_JOB_COMPLETE_COUNT = 4 };

typedef enum
{
    ETgEM_UPDATE_STAGE__UNKNOWN,
    ETgEM_UPDATE_STAGE__WAIT_FOR_EXECUTE,
    ETgEM_UPDATE_STAGE__NEW_STITCH,
    ETgEM_UPDATE_STAGE__PAUSED,
    ETgEM_UPDATE_STAGE__WAITING,
    ETgEM_UPDATE_STAGE__PROCESS,
    ETgEM_UPDATE_STAGE__DELETE,
    ETgEM_UPDATE_STAGE__DONE,
} ETgEM_UPDATE_STAGE;

TgTYPE_STRUCT(STg2_EM_TE_Job_Update_PM,)
{
    /* Pointers to the SoA data elements for the time event pools and locks */
    P_STg2_EM_TE_Pool                           m_psPool;              /*« Example: g_apsEM_TE_FRM[iPool] */
    P_STg2_UTM_SN                               m_psData_Lock;         /*« Example: g_asEM_TE_FRM_Data_Lock[iPool] */
    P_STg2_UTM_SN                               m_psNewDel_Lock;       /*« Example: g_asEM_TE_FRM_NewDel_Lock[iPool] */

    /* Used to trigger the final free pool cleanup job */
    volatile TgATOMIC_SINT32                    *m_pxiFree_Trigger;    /*« Example: s_xiJob_Trigger_TE_FRM */

    ETgEM_UPDATE_STAGE                          m_enStage;
    TgFLOAT32                                   m_fTime_Step;
    TgFLOAT32                                   m_fTotal;
    TgSINT32                                    m_iTotal;
    TgBOOL                                      m_bIsPaused;
    TgUINT32                                    m_uiPad0;
};

TgTYPE_STRUCT(STg2_EM_TE_Job_Free_Pool_PM,)
{
    PP_STg2_EM_TE_Pool                          m_ppsPool_List;        /*« Example: g_apsEM_TE_FRM */
    PP_STg2_EM_TE_Pool                          m_ppsFree_Pool_Head;   /*« Example: &(g_psEM_TE_FRM_Free_List) */
    P_STg2_UTM_SN                               m_psFree_Lock;         /*« Example: g_sEM_TE_FRM_Lock */
    TgSINT32                                    m_niMax_Pool;          /*« Example: KTgEM_MAX_TE_FRM_POOL */
    TgUINT32                                    m_uiPad0;
    P_STg2_UTM_SN_ISO                           m_psData_Lock;         /*« Example: g_asEM_TE_FRM_Data_Lock */
    P_STg2_UTM_SN_ISO                           m_psNewDel_Lock;       /*« Example: g_asEM_TE_FRM_NewDel_Lock */
};

TgTYPE_STRUCT(STg2_Event_Pool_Update,)
{
    PP_STg2_EM_TE_Pool                          m_apsPool;
    P_STg2_UTM_SN_ISO                           m_asData_Lock;
    P_STg2_UTM_SN_ISO                           m_asNewDel_Lock;
    TgSINT32                                    m_iMax;
    TgUINT32                                    m_uiPad0;
    TgFCN_JOB_CALLBACK                          m_pfnExecute;
    volatile TgATOMIC_SINT32                    *m_pxiFree_Trigger;
    CP_STg2_EM_TE_Job_Update_PM                 m_psUpdate_Data;

    P_STg2_Job                                  m_psFree_Job;
    PP_STg2_EM_TE_Pool                          m_ppsFree_Pool_Head;
    P_STg2_UTM_SN                               m_psFree_Lock;
};

TgCOMPILER_ASSERT( sizeof( STg2_EM_TE_Job_Update_PM ) <= KTgJOB_DATA_SIZE, 0 );
TgCOMPILER_ASSERT( sizeof( STg2_EM_TE_Job_Free_Pool_PM ) <= KTgJOB_DATA_SIZE, 0 );




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Functions and Data                                                                                                                                         */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

static TgBOOL                               tgEM_Job_Execute__Submit_Event_Pool_Update( PCU_STg2_Event_Pool_Update );
static TgVOID                               tgEM_Job_Execute__Submit_Free_Pool_Fixup( PCU_STg2_Event_Pool_Update );
TgFORCEINLINE TgVOID                        tgEM_List_Stitch( PP_STg2_EM_TB, P_STg2_EM_TB );
static TgVOID                               tgEM_TE_Update_Internal__Waiting( PC_STg2_EM_TE_Pool, CPCU_STg2_EM_TE_Job_Update_PM );
static TgVOID                               tgEM_TE_Update_Internal__Process( PP_STg2_EM_TB, P_TgSINT32, P_STg2_EM_TE, PC_STg2_EM_TE_Pool, CPCU_STg2_EM_TE_Job_Update_PM );
static TgVOID                               tgEM_TE_Update_Internal__Delete( PC_STg2_EM_TE_Pool, P_STg2_EM_TB, CPCU_STg2_EM_TE_Job_Update_PM );
static TgVOID                               tgEM_TE_Update_Internal__Paused( PC_STg2_EM_TE_Pool, CPCU_STg2_EM_TE_Job_Update_PM );
static TgRESULT                             tgEM_Job_Execute__Update( PC_STg2_Job );
static TgRESULT                             tgEM_Job_Execute__TE( PC_STg2_Job );
static TgRESULT                             tgEM_Job_Execute__Free_Pool( PC_STg2_Job );

#if TgS_STAT_COMMON
static TgVOID                               tgEM_Print_Pool_Stat( PCU_STg2_EM_TE_Pool, P_STg2_Output );
#endif

static ETgMODULE_STATE                      s_enEvent_MGR_State = ETgMODULE_STATE__FREED;
static TgUINT32                             s_bfCntTime;

static STg2_Job                             s_sJob_Update;
static TgATOMIC_SINT32                      s_xiJob_Count;

static STg2_Job                             s_sJob_TE_FRM__Free_Pool_Update;
static volatile TgATOMIC_SINT32             s_xiJob_Trigger_TE_FRM;
static STg2_Job                             s_sJob_TE_SEC__Free_Pool_Update;
static volatile TgATOMIC_SINT32             s_xiJob_Trigger_TE_SEC;




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Public Functions                                                                                                                                                      */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

/* ---- tgEM_Init_MGR --------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgEM_Init_MGR( TgVOID )
{
    TgSINT32                            iIndex;

    /* Verify the state of the system */
    TgERROR(ETgMODULE_STATE__FREED == s_enEvent_MGR_State);
    s_enEvent_MGR_State = ETgMODULE_STATE__INITIALIZING;

    /* Init Global Variables */
    memset( g_apsEM_TE_FRM, 0, sizeof( g_apsEM_TE_FRM ) );
    g_psEM_TE_FRM_Free_List = nullptr;
    tgCM_UTM_SN_Init( &g_sEM_TE_FRM_Lock.m_sLock );

    for (iIndex = 0; iIndex < KTgEM_MAX_TE_FRM_POOL; ++iIndex)
    {
        tgCM_UTM_SN_Init( &g_asEM_TE_FRM_Data_Lock[iIndex].m_sLock );
        tgCM_UTM_SN_Init( &g_asEM_TE_FRM_NewDel_Lock[iIndex].m_sLock );
    };

    memset( g_apsEM_TE_SEC, 0, sizeof( g_apsEM_TE_SEC ) );
    g_psEM_TE_SEC_Free_List = nullptr;
    tgCM_UTM_SN_Init( &g_sEM_TE_SEC_Lock.m_sLock );

    for (iIndex = 0; iIndex < KTgEM_MAX_TE_SEC_POOL; ++iIndex)
    {
        tgCM_UTM_SN_Init( &g_asEM_TE_SEC_Data_Lock[iIndex].m_sLock );
        tgCM_UTM_SN_Init( &g_asEM_TE_SEC_NewDel_Lock[iIndex].m_sLock );
    };

    /* Configuration Parameters */
    tgBF_Reset_U32( &s_bfCntTime );

    /* Initialize the job structures */
    memset( &s_sJob_Update, 0, sizeof( s_sJob_Update ) );
    s_xiJob_Count = 0;

    s_enEvent_MGR_State = ETgMODULE_STATE__INITIALIZED;
    return (KTgS_OK);
}


/* ---- tgEM_Boot_MGR --------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgEM_Boot_MGR( TgVOID )
{
    /* Verify the state of the system */
    TgERROR(ETgMODULE_STATE__INITIALIZED == s_enEvent_MGR_State);
    s_enEvent_MGR_State = ETgMODULE_STATE__BOOTED;
    return (KTgS_OK);
}


/* ---- tgEM_Stop_MGR --------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgEM_Stop_MGR( TgVOID )
{
    if ((ETgMODULE_STATE__FREED == s_enEvent_MGR_State) || (ETgMODULE_STATE__INITIALIZED == s_enEvent_MGR_State))
    {
        return (KTgS_OK);
    };

    /* Verify the state of the system */
    TgERROR(ETgMODULE_STATE__BOOTED == s_enEvent_MGR_State);
    s_enEvent_MGR_State = ETgMODULE_STATE__STOPPED;
    return (KTgS_OK);
}


/* ---- tgEM_Free_MGR --------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgEM_Free_MGR( TgVOID )
{
    TgSINT32                            iIndex;

    if (ETgMODULE_STATE__FREED == s_enEvent_MGR_State)
    {
        return (KTgS_OK);
    };

    /* Verify the state of the system */
    TgERROR((ETgMODULE_STATE__STOPPED == s_enEvent_MGR_State) || (ETgMODULE_STATE__INITIALIZED == s_enEvent_MGR_State));
    s_enEvent_MGR_State = ETgMODULE_STATE__FREEING;

    for (iIndex = 0; iIndex < KTgEM_MAX_TE_SEC_POOL; ++iIndex)
    {
        if (g_apsEM_TE_SEC[iIndex])
        {
            TgFREE_POOL( g_apsEM_TE_SEC[iIndex] );
            g_apsEM_TE_SEC[iIndex] = nullptr;
        };
        tgCM_UTM_SN_Free( &g_asEM_TE_SEC_Data_Lock[iIndex].m_sLock );
        tgCM_UTM_SN_Free( &g_asEM_TE_SEC_NewDel_Lock[iIndex].m_sLock );
    };
    tgCM_UTM_SN_Free( &g_sEM_TE_SEC_Lock.m_sLock );

    for (iIndex = 0; iIndex < KTgEM_MAX_TE_FRM_POOL; ++iIndex)
    {
        if (g_apsEM_TE_FRM[iIndex])
        {
            TgFREE_POOL( g_apsEM_TE_FRM[iIndex] );
            g_apsEM_TE_FRM[iIndex] = nullptr;
        };
        tgCM_UTM_SN_Free( &g_asEM_TE_FRM_Data_Lock[iIndex].m_sLock );
        tgCM_UTM_SN_Free( &g_asEM_TE_FRM_NewDel_Lock[iIndex].m_sLock );
    };
    tgCM_UTM_SN_Free( &g_sEM_TE_FRM_Lock.m_sLock );

    s_enEvent_MGR_State = ETgMODULE_STATE__FREED;
    return (KTgS_OK);
}


/* ---- tgEM_Update_MGR ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgRESULT tgEM_Update_MGR( C_TgFLOAT32 fDT )
{
    union
    {
        P_TgUINT08                          m_psUntyped;
        P_STg2_EM_TE_Job_Update_PM          m_psJob_Data;
    }                                   tgUpdate_Param;

    /* Yield until the previous update has completed */
    while (TgTRUE != tgEM_Update_MGR_Is_Complete())
    {
        tgTR_Yield();
    };

    /* Submit the first stage of the update chain to the job system */
    tgUpdate_Param.m_psUntyped = s_sJob_Update.m_auiData;
    memset( &s_sJob_Update, 0, sizeof( s_sJob_Update ) );

    s_sJob_Update.m_pfnExecute = tgEM_Job_Execute__Update;
    s_sJob_Update.m_pxiFinish = &s_xiJob_Count;

    tgUpdate_Param.m_psJob_Data->m_enStage = ETgEM_UPDATE_STAGE__WAIT_FOR_EXECUTE;
    tgUpdate_Param.m_psJob_Data->m_fTime_Step = fDT;
    tgUpdate_Param.m_psJob_Data->m_fTotal = tgGB_Query_Total_Time();
    tgUpdate_Param.m_psJob_Data->m_iTotal = tgGB_Query_Total_Frame();
    tgUpdate_Param.m_psJob_Data->m_bIsPaused = tgEM_Query_Pause();

    tgAM32_INC( &s_xiJob_Count );
    if (TgFAILED(tgJM_Queue_Job( g_tiJob_Queue__OS, &s_sJob_Update )))
    {
        tgAM32_DEC( &s_xiJob_Count );
    };

    return (KTgS_OK);
}


/* ---- tgEM_Update_MGR_Is_Complete ------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgEM_Update_MGR_Is_Complete( TgVOID )
{
    return (s_xiJob_Count > 0 ? TgFALSE : TgTRUE);
}

/* ---- tgEM_Query_Init ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgEM_Query_Init( TgVOID )
{
    return (ETgMODULE_STATE__INITIALIZED <= s_enEvent_MGR_State && s_enEvent_MGR_State <= ETgMODULE_STATE__STOPPED ? TgTRUE : TgFALSE);
}


/* ---- tgEM_Query_Boot ------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgEM_Query_Boot( TgVOID )
{
    return (ETgMODULE_STATE__BOOTED == s_enEvent_MGR_State ? TgTRUE : TgFALSE);
}


/* ---- tgEM_Query_Fixed_Memory ----------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgSIZE tgEM_Query_Fixed_Memory( TgVOID )
{
    return (0
             + sizeof( g_apsEM_TE_FRM )
             + sizeof( g_psEM_TE_FRM_Free_List )
             + sizeof( g_sEM_TE_FRM_Lock )
             + sizeof( g_asEM_TE_FRM_Data_Lock )
             + sizeof( g_asEM_TE_FRM_NewDel_Lock )
             + sizeof( g_apsEM_TE_SEC )
             + sizeof( g_psEM_TE_SEC_Free_List )
             + sizeof( g_sEM_TE_SEC_Lock )
             + sizeof( g_asEM_TE_SEC_Data_Lock )
             + sizeof( g_asEM_TE_SEC_NewDel_Lock )
             + sizeof( s_enEvent_MGR_State )
             + sizeof( s_bfCntTime )
             + sizeof( s_sJob_Update )
             + sizeof( s_xiJob_Count )
             + sizeof( s_sJob_TE_FRM__Free_Pool_Update )
             + sizeof( s_xiJob_Trigger_TE_FRM )
             + sizeof( s_sJob_TE_SEC__Free_Pool_Update )
             + sizeof( s_xiJob_Trigger_TE_SEC )
    );
}


/* ---- tgEM_Set_Pause -------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgEM_Set_Pause( C_TgBOOL bFlag )
{
    tgBF_Set_Flag_U32( &s_bfCntTime, 0, bFlag );
}


/* ---- tgEM_Query_Pause ------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgBOOL tgEM_Query_Pause( TgVOID )
{
    return (tgBF_Query_Flag_U32( &s_bfCntTime, 0 ));
}


/* ---- tgEM_Stats ------------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
#if TgS_STAT_COMMON
TgVOID tgEM_Stats( P_STg2_Output psOutput )
{
    TgSINT32                            iIndex;

    if (nullptr == psOutput)
    {
        return;
    };

    /* Yield until the previous update has completed */
    while (0 != tgAM32_XCMP( &s_xiJob_Count, 1, 0 ))
    {
        tgTR_Yield();
    };

    /* Collect all of the pools that have free lists */
    tgCM_UTM_SN_Lock_Spin( &g_sEM_TE_FRM_Lock.m_sLock );
    for (iIndex = 0; iIndex < KTgEM_MAX_TE_FRM_POOL; ++iIndex)
    {
        tgCM_UTM_SN_Lock_Spin( &g_asEM_TE_FRM_NewDel_Lock[iIndex].m_sLock );

        if (nullptr != g_apsEM_TE_FRM[iIndex])
        {
            tgCM_UTM_SN_Lock_Spin( &g_asEM_TE_FRM_Data_Lock[iIndex].m_sLock );
            tgEM_Print_Pool_Stat( g_apsEM_TE_FRM[iIndex], psOutput );
            tgCM_UTM_SN_Signal( &g_asEM_TE_FRM_Data_Lock[iIndex].m_sLock );
        };

        tgCM_UTM_SN_Signal( &g_asEM_TE_FRM_NewDel_Lock[iIndex].m_sLock );
    };
    tgCM_UTM_SN_Signal( &g_sEM_TE_FRM_Lock.m_sLock );

    tgCM_UTM_SN_Lock_Spin( &g_sEM_TE_SEC_Lock.m_sLock );
    for (iIndex = 0; iIndex < KTgEM_MAX_TE_SEC_POOL; ++iIndex)
    {
        tgCM_UTM_SN_Lock_Spin( &g_asEM_TE_SEC_NewDel_Lock[iIndex].m_sLock );

        if (nullptr != g_apsEM_TE_SEC[iIndex])
        {
            tgCM_UTM_SN_Lock_Spin( &g_asEM_TE_SEC_Data_Lock[iIndex].m_sLock );
            tgEM_Print_Pool_Stat( g_apsEM_TE_SEC[iIndex], psOutput );
            tgCM_UTM_SN_Signal( &g_asEM_TE_SEC_Data_Lock[iIndex].m_sLock );
        };

        tgCM_UTM_SN_Signal( &g_asEM_TE_SEC_NewDel_Lock[iIndex].m_sLock );
    };
    tgCM_UTM_SN_Signal( &g_sEM_TE_SEC_Lock.m_sLock );

    /* Free the system for updating */
    tgAM32_DEC( &s_xiJob_Count );
}
/*# TgS_STAT_COMMON */
#endif




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Private Functions                                                                                                                                                     */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

/* ---- tgEM_TP_Init ---------------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgVOID tgEM_TP_Init( PC_STg2_EM_TE_Pool psPool )
{
    TgSINT32                            iIndex;

    /* Validate the parameters */
    TgPARAM_CHECK(nullptr != psPool);

    /* Initialize the data structure */
    memset( &psPool->m_sJob, 0, sizeof( psPool->m_sJob ) );
    psPool->m_psFree_Next = 0;
    psPool->m_psFree = psPool->m_asTB;
    psPool->m_niUsed = 0;
    tgBF_Reset_U32( &psPool->m_bfFlags );
    psPool->m_iPool = KTgMAX_S32;
    psPool->m_psWaiting = 0;
    psPool->m_psActive = 0;
    tgCM_UTM_AM_ST_Init( &psPool->m_sNew );

    /* Invalidate all of the entity ids */
    memset( psPool->m_aiKI, 0xFF, KTgEM_NUM_TE_IN_POOL*sizeof( TgSINT64 ) );

    /* Stitch the free list */
    for (iIndex = 0; iIndex < KTgEM_NUM_TE_IN_POOL - 1; ++iIndex)
    {
        psPool->m_asTB[iIndex].m_sHead.psNext = psPool->m_asTB + iIndex + 1;
    };

    psPool->m_asTB[KTgEM_NUM_TE_IN_POOL - 1].m_sHead.psNext = nullptr;
}




/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  File Local Functions                                                                                                                                                  */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

/* ---- tgEM_Job_Execute__Submit_Pool_Update ---------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgBOOL tgEM_Job_Execute__Submit_Event_Pool_Update( PCU_STg2_Event_Pool_Update psInit )
{
    union
    {
        P_TgUINT08                              m_psUntyped;
        P_STg2_EM_TE_Job_Update_PM              m_psJob_Param;
    }                                   tgUpdate_Cast;

    P_STg2_EM_TE_Job_Update_PM          psJob_Data;
    TgSINT32                            iIndex;
    TgSINT32                            niUpdate;

    niUpdate = 0;
    for (iIndex = 0; iIndex < psInit->m_iMax; ++iIndex)
    {
        if (nullptr == psInit->m_apsPool[iIndex])
        {
            continue;
        };

        tgUpdate_Cast.m_psUntyped = psInit->m_apsPool[iIndex]->m_sJob.m_auiData;
        psJob_Data = tgUpdate_Cast.m_psJob_Param;

        memset( &psInit->m_apsPool[iIndex]->m_sJob, 0, sizeof( STg2_Job ) );
        psInit->m_apsPool[iIndex]->m_sJob.m_pfnExecute = psInit->m_pfnExecute;
        psInit->m_apsPool[iIndex]->m_sJob.m_pxiFinish = &s_xiJob_Count;

        psJob_Data->m_psPool = psInit->m_apsPool[iIndex];
        psJob_Data->m_psData_Lock = &(psInit->m_asData_Lock[iIndex].m_sLock);
        psJob_Data->m_psNewDel_Lock = &(psInit->m_asNewDel_Lock[iIndex].m_sLock);
        psJob_Data->m_pxiFree_Trigger = psInit->m_pxiFree_Trigger;
        psJob_Data->m_enStage = ETgEM_UPDATE_STAGE__WAIT_FOR_EXECUTE;
        psJob_Data->m_fTime_Step = psInit->m_psUpdate_Data->m_fTime_Step;
        psJob_Data->m_fTotal = psInit->m_psUpdate_Data->m_fTotal;
        psJob_Data->m_iTotal = psInit->m_psUpdate_Data->m_iTotal;
        psJob_Data->m_bIsPaused = psInit->m_psUpdate_Data->m_bIsPaused;

        tgAM32_INC( psInit->m_pxiFree_Trigger );
        tgAM32_INC( &s_xiJob_Count );
        ++niUpdate;
        if (TgFAILED(tgJM_Queue_Job( g_tiJob_Queue__OS, &psInit->m_apsPool[iIndex]->m_sJob )))
        {
            tgAM32_DEC( psInit->m_pxiFree_Trigger );
            tgAM32_DEC( &s_xiJob_Count );
        };
    };

    return (niUpdate > 0 ? TgTRUE : TgFALSE);
}


/* ---- tgEM_Job_Execute__Submit_Pool_Update ---------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgEM_Job_Execute__Submit_Free_Pool_Fixup( PCU_STg2_Event_Pool_Update psInit )
{
    union
    {
        P_TgUINT08                              m_psUntyped;
        P_STg2_EM_TE_Job_Free_Pool_PM           m_psJob_Param;
    }                                   tgJob_Cast;

    tgJob_Cast.m_psUntyped = psInit->m_psFree_Job->m_auiData;

    memset( psInit->m_psFree_Job, 0, sizeof( STg2_Job ) );
    psInit->m_psFree_Job->m_pfnExecute = tgEM_Job_Execute__Free_Pool;
    psInit->m_psFree_Job->m_pxiTrigger = psInit->m_pxiFree_Trigger;
    psInit->m_psFree_Job->m_pxiFinish = &s_xiJob_Count;

    tgJob_Cast.m_psJob_Param->m_ppsPool_List = psInit->m_apsPool;
    tgJob_Cast.m_psJob_Param->m_ppsFree_Pool_Head = psInit->m_ppsFree_Pool_Head;
    tgJob_Cast.m_psJob_Param->m_psFree_Lock = psInit->m_psFree_Lock;
    tgJob_Cast.m_psJob_Param->m_niMax_Pool = psInit->m_iMax;
    tgJob_Cast.m_psJob_Param->m_psData_Lock = psInit->m_asData_Lock;
    tgJob_Cast.m_psJob_Param->m_psNewDel_Lock = psInit->m_asNewDel_Lock;

    tgAM32_INC( &s_xiJob_Count );
    if (TgFAILED(tgJM_Queue_Job( g_tiJob_Queue__OS, psInit->m_psFree_Job )))
    {
        tgAM32_DEC( &s_xiJob_Count );
    };
}


/* ---- tgEM_List_Stitch ------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgFORCEINLINE TgVOID tgEM_List_Stitch( PP_STg2_EM_TB ppsTB_List, P_STg2_EM_TB psInsert_TB )
{
    P_STg2_EM_TB                        psTB = *ppsTB_List;
    P_STg2_EM_TB                        psPrev_TB = nullptr;

    /* Add this time line to the parameter list in an index priority */
    while (1)
    {
        if (nullptr == psTB || psTB->m_uiLocal_Index > psInsert_TB->m_uiLocal_Index)
        {
            if (nullptr == psPrev_TB)
            {
                psInsert_TB->m_sHead.psNext = *ppsTB_List;
                *ppsTB_List = psInsert_TB;
            }
            else
            {
                psInsert_TB->m_sHead.psNext = psTB;
                psPrev_TB->m_sHead.psNext = psInsert_TB;
            };

            return;
        };

        psPrev_TB = psTB;
        psTB = psTB->m_sHead.psNext;
        TgPARAM_CHECK(psTB != psPrev_TB);
    };
}


/* ---- tgEM_TE_Update_Internal__Waiting -------------------------------------------------------------------------------------------------------------------------------- */
/*  NOTE: Since pause state is polled it is possible, if the time line pause state is being toggled on a concurrent thread that we will retrieve a temporary or in-flight */
/*        value. If you need to toggle pause state this should be done within the events instead as a directed action instead of indirectly by pause state on the time line */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgEM_TE_Update_Internal__Waiting( PC_STg2_EM_TE_Pool psPool, CPCU_STg2_EM_TE_Job_Update_PM psParam )
{
    PP_STg2_EM_TB                       ppsPrev_TB;
    P_STg2_EM_TB                        psTB;

    /* Process all the waiting time lines based on frame count */
    for (ppsPrev_TB = &psPool->m_psWaiting, psTB = psPool->m_psWaiting; nullptr != psTB; )
    {
        TgERROR((TgSINT32)(psTB->m_uiLocal_Index & 0xFFFF) < KTgEM_NUM_TE_IN_POOL);

        /* Check to see if the time line is paused */
        if (tgEM_TB_Is_Paused( psTB ))
        {
            psTB->m_fStart = tgPM_FSEL_F32( psTB->m_fStart, psTB->m_fStart + psParam->m_fTime_Step, psTB->m_fStart );
            psTB->m_fEnd = tgPM_FSEL_F32( psTB->m_fEnd, psTB->m_fEnd + psParam->m_fTime_Step, psTB->m_fEnd );
            psTB->m_iStart = (TgSINT32)((TgUINT32)psTB->m_iStart & 0x80000000) | (psTB->m_iStart + 1);
            psTB->m_iEnd = (TgSINT32)((TgUINT32)psTB->m_iEnd & 0x80000000) | (psTB->m_iEnd + 1);

            continue;
        };

        /* Check to see if the entity should be started (negative values in unsigned space will always exceed signed total) */
        /* Thus, only one of these tests can ever be true (the unused value has a negative value) */
        if (((TgUINT32)psTB->m_iStart <= (TgUINT32)psParam->m_iTotal) || (tgPM_FSEL_F32( psTB->m_fStart, psTB->m_fStart, KTgMAX_F32 ) <= psParam->m_fTotal))
        {
            PC_STg2_EM_TB                       psActive_TB = psTB;

            /* Remove this entry from the waiting list */
            *ppsPrev_TB = psTB->m_sHead.psNext;
            psTB = psTB->m_sHead.psNext;

            /* Add the entity to the active list */
            tgEM_TB_Set_Active( psActive_TB, TgTRUE );
            tgEM_List_Stitch( &psPool->m_psActive, psActive_TB );
        }
        else
        {
            ppsPrev_TB = &psTB->m_sHead.psNext;
            psTB = psTB->m_sHead.psNext;
        };
    };
}


/* ---- tgEM_TE_Update_Internal__Process -------------------------------------------------------------------------------------------------------------------------------- */
/*  NOTE: Since pause state is polled it is possible, if the time line pause state is being toggled on a concurrent thread that we will retrieve a temporary or in-flight */
/*        value. If you need to toggle pause state this should be done within the events instead as a directed action instead of indirectly by pause state on the time line */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgEM_TE_Update_Internal__Process( PP_STg2_EM_TB ppsFree_TB, P_TgSINT32 pniCallback, P_STg2_EM_TE psCallback, PC_STg2_EM_TE_Pool psPool_TB,
    CPCU_STg2_EM_TE_Job_Update_PM psParam )
{
    PP_STg2_EM_TB                       ppsPrev_TB;
    P_STg2_EM_TB                        psTB;
    P_STg2_EM_TE_Pool                   psPool_TE;
    TgSINT32                            niCallback;
    P_STg2_EM_TB                        psFree_TB;

    TgPARAM_CHECK(nullptr != ppsFree_TB && nullptr != pniCallback && nullptr != psCallback && nullptr != psPool_TB && nullptr != psParam);

    psPool_TE = (P_STg2_EM_TE_Pool)psPool_TB;
    niCallback = 0;
    psFree_TB = nullptr;

    /* Process all the waiting time lines based on frame count */
    for (ppsPrev_TB = &psPool_TB->m_psActive, psTB = psPool_TB->m_psActive; nullptr != psTB; )
    {
        TgSINT32                            iLocal_Index;

        iLocal_Index = (TgSINT32)(psTB->m_uiLocal_Index & 0xFFFF);
        TgERROR(iLocal_Index < KTgEM_NUM_TE_IN_POOL);

        /* Check to see if the time line is paused */
        if (TgTRUE == tgEM_TB_Is_Paused( psTB ))
        {
            psTB->m_fStart = tgPM_FSEL_F32( psTB->m_fStart, psTB->m_fStart + psParam->m_fTime_Step, psTB->m_fStart );
            psTB->m_fEnd = tgPM_FSEL_F32( psTB->m_fEnd, psTB->m_fEnd + psParam->m_fTime_Step, psTB->m_fEnd );
            psTB->m_iStart = (TgSINT32)((TgUINT32)psTB->m_iStart & 0x80000000) | (psTB->m_iStart + 1);
            psTB->m_iEnd = (TgSINT32)((TgUINT32)psTB->m_iEnd & 0x80000000) | (psTB->m_iEnd + 1);

            continue;
        };

        /* Add this call back to the queue */
        psCallback[niCallback++] = psPool_TE->m_asTE[iLocal_Index];

        /* Check to see if the time line should be closed (negative values in unsigned space will always exceed signed total) */
        /* Thus, only one of these tests can ever be true (the unused value has a negative value) */
        if (((TgUINT32)psTB->m_iEnd <= (TgUINT32)psParam->m_iTotal) || (tgPM_FSEL_F32( psTB->m_fEnd, psTB->m_fEnd, KTgMAX_F32 ) <= psParam->m_fTotal))
        {
            P_STg2_EM_TB                        psFinish_TB;
            TgUINT32                            uiIndex;

            psFinish_TB = psTB;
            uiIndex = (TgUINT32)((psTB - psPool_TB->m_asTB) & 0xFFFF);
            TgERROR(uiIndex < KTgEM_NUM_TE_IN_POOL);
            TgPARAM_CHECK_INDEX( uiIndex, psPool_TB->m_aiKI );

            /* Mark the entry to be invalid */
            psPool_TB->m_aiKI[uiIndex] = KTgEM_TE_FRM_ID__INVALID.m_iKI;

            /* Remove this entry from the processing list */
            *ppsPrev_TB = psTB->m_sHead.psNext;
            psTB = psTB->m_sHead.psNext;

            /* Add the entity to the free list */
            psFinish_TB->m_sHead.psNext = psFree_TB;
            psFree_TB = psFinish_TB;
        }
        else
        {
            ppsPrev_TB = &psTB->m_sHead.psNext;
            psTB = psTB->m_sHead.psNext;
        };
    };

    *ppsFree_TB = psFree_TB;
    *pniCallback = niCallback;
}


/* ---- tgEM_TE_Update_Internal__Delete --------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgEM_TE_Update_Internal__Delete( PC_STg2_EM_TE_Pool psPool, P_STg2_EM_TB psFree_TB, CPCU_STg2_EM_TE_Job_Update_PM psParam )
{
    P_STg2_EM_TB                        psTB;

    TgPARAM_CHECK(nullptr != psFree_TB);

    /* Add all of the deleted entities to the pool's free list */
    while (nullptr != (psTB = psFree_TB))
    {
        psFree_TB = psFree_TB->m_sHead.psNext;

        /* We recheck after processing since it is possible that the call back functions have modified our end criteria */
        if (((TgUINT32)psTB->m_iEnd <= (TgUINT32)psParam->m_iTotal) || (tgPM_FSEL_F32( psTB->m_fEnd, psTB->m_fEnd, KTgMAX_F32 ) <= psParam->m_fTotal))
        {
            --psPool->m_niUsed;
            tgEM_List_Stitch( &psPool->m_psFree, psTB );
        }
        else
        {
            tgEM_List_Stitch( &psPool->m_psWaiting, psTB );
        };
    };
}


/* ---- tgEM_TE_Update_Internal__Paused --------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgVOID tgEM_TE_Update_Internal__Paused( PC_STg2_EM_TE_Pool psPool, CPCU_STg2_EM_TE_Job_Update_PM psParam )
{
    P_STg2_EM_TB                        psTB;

    /* PROCESS ALL WAITING TIME LINES */
    for (psTB = psPool->m_psWaiting; nullptr != psTB; psTB = psTB->m_sHead.psNext)
    {
        TgERROR((TgSINT32)(psTB->m_uiLocal_Index & 0xFFFF) < KTgEM_NUM_TE_IN_POOL);

        /* Check to see if the time line is paused */
        psTB->m_fStart = tgPM_FSEL_F32( psTB->m_fStart, psTB->m_fStart + psParam->m_fTime_Step, psTB->m_fStart );
        psTB->m_fEnd = tgPM_FSEL_F32( psTB->m_fEnd, psTB->m_fEnd + psParam->m_fTime_Step, psTB->m_fEnd );
        psTB->m_iStart = (TgSINT32)((TgUINT32)psTB->m_iStart & 0x80000000) | (psTB->m_iStart + 1);
        psTB->m_iEnd = (TgSINT32)((TgUINT32)psTB->m_iEnd & 0x80000000) | (psTB->m_iEnd + 1);
    };


    /* PROCESS ALL PROCESS TIME LINES */
    for (psTB = psPool->m_psActive; nullptr != psTB; psTB = psTB->m_sHead.psNext)
    {
        TgERROR((TgSINT32)(psTB->m_uiLocal_Index & 0xFFFF) < KTgEM_NUM_TE_IN_POOL);

        /* Check to see if the time line is paused */
        psTB->m_fStart = tgPM_FSEL_F32( psTB->m_fStart, psTB->m_fStart + psParam->m_fTime_Step, psTB->m_fStart );
        psTB->m_fEnd = tgPM_FSEL_F32( psTB->m_fEnd, psTB->m_fEnd + psParam->m_fTime_Step, psTB->m_fEnd );
        psTB->m_iStart = (TgSINT32)((TgUINT32)psTB->m_iStart & 0x80000000) | (psTB->m_iStart + 1);
        psTB->m_iEnd = (TgSINT32)((TgUINT32)psTB->m_iEnd & 0x80000000) | (psTB->m_iEnd + 1);
    };
}


/* ---- tgEM_Job_Execute__Update ---------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgRESULT tgEM_Job_Execute__Update( PC_STg2_Job psJob )
{
    union
    {
        P_TgUINT08                              m_psUntyped;
        P_STg2_EM_TE_Job_Update_PM              m_psJob_Data;
    }                                   tgJob_Cast;
    TgBOOL                              bNeedFixUp;
    STg2_Event_Pool_Update              tgUpdate_Init;

    tgJob_Cast.m_psUntyped = psJob->m_auiData;

    /* Submit the jobs to update the time event pools */
    tgUpdate_Init.m_apsPool = g_apsEM_TE_FRM;
    tgUpdate_Init.m_asData_Lock = g_asEM_TE_FRM_Data_Lock;
    tgUpdate_Init.m_asNewDel_Lock = g_asEM_TE_FRM_NewDel_Lock;
    tgUpdate_Init.m_iMax = KTgEM_MAX_TE_FRM_POOL;
    tgUpdate_Init.m_pfnExecute = tgEM_Job_Execute__TE;
    tgUpdate_Init.m_pxiFree_Trigger = &s_xiJob_Trigger_TE_FRM;
    tgUpdate_Init.m_psUpdate_Data = tgJob_Cast.m_psJob_Data;

    bNeedFixUp = tgEM_Job_Execute__Submit_Event_Pool_Update( &tgUpdate_Init );
    if (TgFALSE != bNeedFixUp)
    {
        tgUpdate_Init.m_psFree_Job = &s_sJob_TE_FRM__Free_Pool_Update;
        tgUpdate_Init.m_psFree_Lock = &g_sEM_TE_FRM_Lock.m_sLock;
        tgUpdate_Init.m_ppsFree_Pool_Head = &g_psEM_TE_FRM_Free_List;

        tgEM_Job_Execute__Submit_Free_Pool_Fixup( &tgUpdate_Init );
    };

    tgUpdate_Init.m_apsPool = g_apsEM_TE_SEC;
    tgUpdate_Init.m_asData_Lock = g_asEM_TE_SEC_Data_Lock;
    tgUpdate_Init.m_asNewDel_Lock = g_asEM_TE_SEC_NewDel_Lock;
    tgUpdate_Init.m_iMax = KTgEM_MAX_TE_SEC_POOL;
    tgUpdate_Init.m_pfnExecute = tgEM_Job_Execute__TE;
    tgUpdate_Init.m_pxiFree_Trigger = &s_xiJob_Trigger_TE_SEC;
    tgUpdate_Init.m_psUpdate_Data = tgJob_Cast.m_psJob_Data;

    bNeedFixUp = tgEM_Job_Execute__Submit_Event_Pool_Update( &tgUpdate_Init );
    if (TgFALSE != bNeedFixUp)
    {
        tgUpdate_Init.m_psFree_Job = &s_sJob_TE_SEC__Free_Pool_Update;
        tgUpdate_Init.m_psFree_Lock = &g_sEM_TE_SEC_Lock.m_sLock;
        tgUpdate_Init.m_ppsFree_Pool_Head = &g_psEM_TE_SEC_Free_List;

        tgEM_Job_Execute__Submit_Free_Pool_Fixup( &tgUpdate_Init );
    };

    return (KTgS_OK);
}


/* ---- tgEM_Job_Execute__TE -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgRESULT tgEM_Job_Execute__TE( PC_STg2_Job psJob )
{
    union
    {
        P_TgUINT08                          m_psUntyped;
        P_STg2_EM_TE_Job_Update_PM          m_psJob_Data;
    }                                   tgJob_Cast;
    union
    {
        P_STg2_UTM_Node                     m_psElement;
        P_STg2_EM_TB                        m_psJob;
    }                                   tgJob;
    P_STg2_EM_TE_Job_Update_PM          psJob_Data;
    TgSINT32                            iIndex;

    TgPARAM_CHECK(nullptr != psJob);

    tgJob_Cast.m_psUntyped = psJob->m_auiData;
    psJob_Data = tgJob_Cast.m_psJob_Data;

    TgPARAM_CHECK(nullptr != psJob_Data->m_psPool);

    /* Add new time events to the waiting or processing linked lists */
    psJob_Data->m_enStage = ETgEM_UPDATE_STAGE__NEW_STITCH;
    tgJob.m_psElement = tgCM_UTM_AM_ST_Pop( &psJob_Data->m_psPool->m_sNew );
    while (nullptr != tgJob.m_psJob)
    {
        tgEM_List_Stitch( &psJob_Data->m_psPool->m_psWaiting, tgJob.m_psJob );
        tgJob.m_psElement = tgCM_UTM_AM_ST_Pop( &psJob_Data->m_psPool->m_sNew );
    };

    /* Process all of the time events attached to the linked lists */
    /* This includes moving deleted entities to the free list */
    if (psJob_Data->m_bIsPaused)
    {
        tgCM_UTM_SN_Lock_Spin( psJob_Data->m_psData_Lock );
        psJob_Data->m_enStage = ETgEM_UPDATE_STAGE__PAUSED;
        tgEM_TE_Update_Internal__Paused( psJob_Data->m_psPool, psJob_Data );
        tgCM_UTM_SN_Signal( psJob_Data->m_psData_Lock );
    }
    else
    {
        STg2_EM_TE                          asCallback[KTgEM_NUM_TE_IN_POOL];
        TgSINT32                            niCallback;
        P_STg2_EM_TB                        psFree_TB;

        tgCM_UTM_SN_Lock_Spin( psJob_Data->m_psData_Lock );
        psJob_Data->m_enStage = ETgEM_UPDATE_STAGE__WAITING;
        tgEM_TE_Update_Internal__Waiting( psJob_Data->m_psPool, psJob_Data );
        psJob_Data->m_enStage = ETgEM_UPDATE_STAGE__PROCESS;
        tgEM_TE_Update_Internal__Process( &psFree_TB, &niCallback, asCallback, psJob_Data->m_psPool, psJob_Data );
        tgCM_UTM_SN_Signal( psJob_Data->m_psData_Lock );

        for (iIndex = 0; iIndex < niCallback; ++iIndex)
        {
            asCallback[iIndex].m_pfnCallback( asCallback[iIndex].m_uiParam );
        };

        if (nullptr != psFree_TB)
        {
            tgCM_UTM_SN_Lock_Spin( psJob_Data->m_psNewDel_Lock );
            psJob_Data->m_enStage = ETgEM_UPDATE_STAGE__DELETE;
            tgEM_TE_Update_Internal__Delete( psJob_Data->m_psPool, psFree_TB, psJob_Data );
            tgCM_UTM_SN_Signal( psJob_Data->m_psNewDel_Lock );
        }
    };

    psJob_Data->m_enStage = ETgEM_UPDATE_STAGE__DONE;
    tgAM_WRITE_FENCE();

    tgAM32_DEC( psJob_Data->m_pxiFree_Trigger );

    return (KTgS_OK);
}


/* ---- tgEM_Job_Execute__Free_Pool ------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
static TgRESULT tgEM_Job_Execute__Free_Pool( PC_STg2_Job psJob )
{
    union
    {
        P_TgUINT08                              m_psUntyped;
        P_STg2_EM_TE_Job_Free_Pool_PM           m_psJob_Data;
    }                                   tgJob_Cast;

    P_STg2_EM_TE_Job_Free_Pool_PM       psJob_Data;

    TgSINT32                            iSort0, iSort1, iIndex;
    PP_STg2_EM_TE_Pool                  ppsFP_List; /* Dynamic array holding all of the valid pools with free elements */

    TgSINT32                            niFree = 0;

    TgPARAM_CHECK(nullptr != psJob);

    tgJob_Cast.m_psUntyped = psJob->m_auiData;
    psJob_Data = tgJob_Cast.m_psJob_Data;

    /* Lock the free pools for the system. */
    TgPARAM_CHECK(nullptr != psJob_Data->m_psFree_Lock);
    tgCM_UTM_SN_Lock_Spin( psJob_Data->m_psFree_Lock );

    TgALLOCA( P_STg2_EM_TE_Pool, (TgSIZE)psJob_Data->m_niMax_Pool, ppsFP_List );
    if (!ppsFP_List)
    {
        TgFREEA( ppsFP_List );
        tgCM_UTM_SN_Signal( psJob_Data->m_psFree_Lock );
        return (KTgE_FAIL);
    }

    /* Collect all of the pools that have free lists */
    for (iIndex = 0; iIndex < psJob_Data->m_niMax_Pool; ++iIndex)
    {
        tgCM_UTM_SN_Lock_Spin( &psJob_Data->m_psNewDel_Lock[iIndex].m_sLock );

        if (nullptr != psJob_Data->m_ppsPool_List[iIndex] && nullptr != psJob_Data->m_ppsPool_List[iIndex]->m_psFree)
        {
            ppsFP_List[niFree++] = psJob_Data->m_ppsPool_List[iIndex];
        }
        else
        {
            tgCM_UTM_SN_Signal( &psJob_Data->m_psNewDel_Lock[iIndex].m_sLock );
        };
    };

    if (0 == niFree)
    {
        TgFREEA( ppsFP_List );
        tgCM_UTM_SN_Signal( psJob_Data->m_psFree_Lock );
        return (KTgS_OK);
    }

    /* Free Pool Sort - The most filled pools are the top of the free stack */
    for (iSort0 = 0; iSort0 < niFree; ++iSort0)
    {
        TgSINT32                            niUsed = ppsFP_List[iSort0]->m_niUsed;
        TgSINT32                            iSort_Swap = -1;

        for (iSort1 = iSort0 + 1; iSort1 < niFree; ++iSort1)
        {
            if (ppsFP_List[iSort1]->m_niUsed > niUsed)
            {
                niUsed = ppsFP_List[iSort1]->m_niUsed;
                iSort_Swap = iSort1;
            };
        };

        if (iSort_Swap >= 0)
        {
            P_STg2_EM_TE_Pool                   psSwap = ppsFP_List[iSort0];

            ppsFP_List[iSort0] = ppsFP_List[iSort1];
            ppsFP_List[iSort1] = psSwap;
        };
    };

    /* Look to remove empty pools */
    if (niFree >= 2)
    {
        do
        {
            C_TgSINT32                          iPool = ppsFP_List[niFree - 1]->m_iPool;
            P_STg2_EM_TE_Pool                   psDel;

            if (0 == ppsFP_List[niFree - 1]->m_niUsed && tgCM_UTM_AM_ST_Is_Empty( &ppsFP_List[niFree - 1]->m_sNew ))
            {
                break;
            };

            tgCM_UTM_SN_Lock_Spin( &psJob_Data->m_psData_Lock[iPool].m_sLock );

            psDel = psJob_Data->m_ppsPool_List[iPool];
            psJob_Data->m_ppsPool_List[iPool] = nullptr;

            tgCM_UTM_AM_ST_Free( &ppsFP_List[niFree - 1]->m_sNew );
            TgFREE_POOL( psDel );
            --niFree;

            tgCM_UTM_SN_Signal( &psJob_Data->m_psData_Lock[iPool].m_sLock );
            tgCM_UTM_SN_Signal( &psJob_Data->m_psNewDel_Lock[iPool].m_sLock );
        }
        while (0);
    };


    /* Link up the free pool list and assigned into the link list head */
    for (iIndex = 0; iIndex + 1 < niFree; ++iIndex)
    {
        ppsFP_List[iIndex]->m_psFree_Next = ppsFP_List[iIndex + 1];
        tgCM_UTM_SN_Signal( &psJob_Data->m_psNewDel_Lock[ppsFP_List[iIndex]->m_iPool].m_sLock );
    };

    ppsFP_List[niFree - 1]->m_psFree_Next = nullptr;
    tgCM_UTM_SN_Signal( &psJob_Data->m_psNewDel_Lock[ppsFP_List[niFree - 1]->m_iPool].m_sLock );

    *psJob_Data->m_ppsFree_Pool_Head = ppsFP_List[0];


    /* Free the system lock and terminate */
    TgFREEA( ppsFP_List );
    tgCM_UTM_SN_Signal( psJob_Data->m_psFree_Lock );
    return (KTgS_OK);
}


/* ---- tgEM_Print_Pool_Stat -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
#if TgS_STAT_COMMON
static TgVOID tgEM_Print_Pool_Stat( PCU_STg2_EM_TE_Pool psPool, P_STg2_Output psOutput )
{
    P_STg2_EM_TB                        psTB;

    for (psTB = psPool->m_psWaiting; psTB != nullptr; psTB = psTB->m_sHead.psNext)
    {
        tgIO_PrintF( psOutput, TgT("% 2d (% 9d) "), psPool->m_iPool, (TgUINT32)psPool->m_aiKI[psTB - psPool->m_psWaiting] );

        if (psTB->m_iStart < 0)
        {
            tgIO_PrintF( psOutput, TgT("SEC   %5.2f - %5.2f"), (TgFLOAT64)psTB->m_fStart, (TgFLOAT64)psTB->m_fEnd );
        }
        else
        {
            tgIO_PrintF( psOutput, TgT("FRM   %8d - %8d"), psTB->m_iStart, psTB->m_iEnd );
        };

        tgIO_PrintF( psOutput, TgT(" Waiting\n") );
    };

    for (psTB = psPool->m_psActive; psTB != nullptr; psTB = psTB->m_sHead.psNext)
    {
        tgIO_PrintF( psOutput, TgT("% 2d (% 9d) "), psPool->m_iPool, (TgUINT32)psPool->m_aiKI[psTB - psPool->m_psActive] );

        if (psTB->m_iStart < 0)
        {
            tgIO_PrintF( psOutput, TgT("SEC   %5.2f - %5.2f"), (TgFLOAT64)psTB->m_fStart, (TgFLOAT64)psTB->m_fEnd );
        }
        else
        {
            tgIO_PrintF( psOutput, TgT("FRM   %8d - %8d"), psTB->m_iStart, psTB->m_iEnd );
        };

        tgIO_PrintF( psOutput, TgT(" Process\n") );
    };
}
/*# TgS_STAT_COMMON */
#endif