Home

Resume

Blog

Teikitu


/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
/*  »Project«   Teikitu Gaming System (TgS) (∂)
    »File«      TgS Common - Util MP - Reader Writer Lock.inl
    »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".                                                   */
/* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= */
#if !defined(TGS_COMMON_UTIL_UTM_RW_INL)
#define TGS_COMMON_UTIL_UTM_RW_INL
#pragma once


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

/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */
/*  Internal Functions                                                                                                                                                    */
/* -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. */

TgINLINE TgVOID                             tgCM_UTM_RW_INT_Init( PCU_STg2_UTM_INT_RW );
TgINLINE TgVOID                             tgCM_UTM_RW_INT_Free( PCU_STg2_UTM_INT_RW );
TgEXTN TgVOID                               tgCM_UTM_RW_INT_Update( PCU_STg2_UTM_INT_RW );




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

/* ---- Internal Functions ------------------------------------------------------------------------------------------------------------------------------------------------ */

/* ---- tgCM_UTM_RW_INT_Init -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_RW_INT_Init( PCU_STg2_UTM_INT_RW psMP_RW )
{
    memset( psMP_RW, 0, sizeof( STg2_UTM_INT_RW ) );
}


/* ---- tgCM_UTM_RW_INT_Free -------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_RW_INT_Free( PCU_STg2_UTM_INT_RW psMP_RW )
{
#if TgCOMPILE_ASSERT
    TgSINT32                            iIndex;

    TgERROR(0 == psMP_RW->m_nuiThread);
    for (iIndex = 0; iIndex < KTgMP_MAX_READ_WRITER_LOCK; ++iIndex)
    {
        TgERROR(0 == psMP_RW->m_psExecute[iIndex]);
    }
/*# TgCOMPILE_ASSERT */
#endif
}


/* ---- FIFO Atomic Reader Writer Lock ------------------------------------------------------------------------------------------------------------------------------------ */

/* ---- tgCM_UTM_AM_RW_Init --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_AM_RW_Init( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    psCM_UTM_SN_RW->m_iData = 0;
    return (KTgS_OK);
}


/* ---- tgCM_UTM_AM_RW_Free --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_AM_RW_Free( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    TgERROR(psCM_UTM_SN_RW->m.iRead == psCM_UTM_SN_RW->m.iRequest);
    TgERROR(psCM_UTM_SN_RW->m.iRead == psCM_UTM_SN_RW->m.iWrite);
}


/* ---- tgCM_UTM_AM_RW_Enter_Read_Wait_Yield ---------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_AM_RW_Enter_Read_Wait_Yield( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    C_TgUINT32                          iData = (TgUINT32)tgAM32_XADD( &psCM_UTM_SN_RW->m_iData, 1 << 16 );
    C_TgUINT08                          iRequest = (iData >> 16) & 0xFF;

    while (iRequest != psCM_UTM_SN_RW->m.iRead)
    {
        tgTR_Yield();
    };

    psCM_UTM_SN_RW->m.iRead = (TgUINT08)((psCM_UTM_SN_RW->m.iRead + 1) & 0xFF);

    tgAM_WRITE_FENCE();
}


/* ---- tgCM_UTM_AM_RW_Enter_Read_Wait_Spin ----------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_AM_RW_Enter_Read_Wait_Spin( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    C_TgUINT32                          iData = (TgUINT32)tgAM32_XADD( &psCM_UTM_SN_RW->m_iData, 1 << 16 );
    C_TgUINT08                          iRequest = (iData >> 16) & 0xFF;

    while (iRequest != psCM_UTM_SN_RW->m.iRead)
    {
        tgTR_Pause();
    };

    psCM_UTM_SN_RW->m.iRead = (TgUINT08)((psCM_UTM_SN_RW->m.iRead + 1) & 0xFF);

    tgAM_WRITE_FENCE();
}


/* ---- tgCM_UTM_AM_RW_Enter_Read --------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_AM_RW_Enter_Read( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    C_TgUINT32                          uiOrig_Data = (TgUINT32)tgAM32_READ( &psCM_UTM_SN_RW->m_iData );
    C_TgUINT08                          uiRequest = (uiOrig_Data >> 16) & 0xFF;
    C_TgUINT32                          uiCmp_Data = (uiOrig_Data & 0xFF000000) | (TgUINT32)(uiRequest << 16) | (TgUINT32)(uiRequest << 8) | (uiOrig_Data & 0xFF);
    C_TgUINT32                          uiNew_Data = (uiOrig_Data & 0xFF000000) | (TgUINT32)((uiRequest + 1) << 16) | (TgUINT32)(uiRequest << 8) | (uiOrig_Data & 0xFF);

    if (uiCmp_Data != (TgUINT32)tgAM32_XCMP( &psCM_UTM_SN_RW->m_iData, (TgSINT32)uiNew_Data, (TgSINT32)uiCmp_Data ))
    {
        return (KTgE_FAIL);
    };

    psCM_UTM_SN_RW->m.iRead = (TgUINT08)((psCM_UTM_SN_RW->m.iRead + 1) & 0xFF);

    tgAM_WRITE_FENCE();

    return (KTgS_OK);
}


/* ---- tgCM_UTM_AM_RW_Exit_Read ---------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_AM_RW_Exit_Read( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    TgUINT32                            iOrig_Data, iNew_Data;

    tgAM_READ_FENCE();

    do
    {
        iOrig_Data = (TgUINT32)tgAM32_READ( &psCM_UTM_SN_RW->m_iData );
        iNew_Data = (iOrig_Data & 0xFFFFFF00) | (((iOrig_Data & 0xFF) + 1) & 0xFF);
    }
    while (iOrig_Data != (TgUINT32)tgAM32_XCMP( &psCM_UTM_SN_RW->m_iData, (TgSINT32)iNew_Data, (TgSINT32)iOrig_Data ));
}


/* ---- tgCM_UTM_AM_RW_Enter_Write_Wait_Yield --------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_AM_RW_Enter_Write_Wait_Yield( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    C_TgUINT32                          iData = (TgUINT32)tgAM32_XADD( &psCM_UTM_SN_RW->m_iData, 1 << 16 );
    C_TgUINT08                          iRequest = (iData >> 16) & 0xFF;

    while (iRequest != psCM_UTM_SN_RW->m.iWrite)
    {
        tgTR_Yield();
    };

    tgAM_READ_FENCE();
}


/* ---- tgCM_UTM_AM_RW_Enter_Write_Wait_Spin ---------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_AM_RW_Enter_Write_Wait_Spin( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    C_TgUINT32                          iData = (TgUINT32)tgAM32_XADD( &psCM_UTM_SN_RW->m_iData, 1 << 16 );
    C_TgUINT08                          iRequest = (iData >> 16) & 0xFF;

    while (iRequest != psCM_UTM_SN_RW->m.iWrite)
    {
        tgTR_Pause();
    };

    tgAM_READ_FENCE();
}


/* ---- tgCM_UTM_AM_RW_Enter_Write -------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_AM_RW_Enter_Write( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    C_TgUINT32                          iOrig_Data = (TgUINT32)tgAM32_READ( &psCM_UTM_SN_RW->m_iData );
    C_TgUINT08                          iRequest = (iOrig_Data >> 16) & 0xFF;
    C_TgUINT32                          iCmp_Data = (iOrig_Data & 0xFF000000) | (TgUINT32)(iRequest << 16) | (TgUINT32)(iRequest << 8) | iRequest;
    C_TgUINT32                          iNew_Data = (iOrig_Data & 0xFF000000) | (TgUINT32)((iRequest + 1) << 16) | (TgUINT32)(iRequest << 8) | iRequest;

    if (iCmp_Data != (TgUINT32)tgAM32_XCMP( &psCM_UTM_SN_RW->m_iData, (TgSINT32)iNew_Data, (TgSINT32)iCmp_Data ))
    {
        return (KTgE_FAIL);
    };

    tgAM_READ_FENCE();

    return (KTgS_OK);
}


/* ---- tgCM_UTM_AM_RW_Exit_Write --------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_AM_RW_Exit_Write( PCU_STg2_UTM_AM_RW psCM_UTM_SN_RW )
{
    C_TgUINT16                          iOrig_Data = psCM_UTM_SN_RW->u.iWrite_Read;
    C_TgUINT16                          iNew_Write = (TgUINT16)(((iOrig_Data & 0xFF) + 1) & 0xFF);
    C_TgUINT16                          iNew_Read = (TgUINT16)(((iOrig_Data & 0xFF00) + 0x100) & 0xFF00);
    C_TgUINT16                          iNew_Write_Read = (TgUINT16)(iNew_Write | iNew_Read);

    tgAM_WRITE_FENCE();

    psCM_UTM_SN_RW->u.iWrite_Read = iNew_Write_Read;
}


/* ---- FIFO Spin Lock Reader Writer Lock --------------------------------------------------------------------------------------------------------------------------------- */

#if TgCOMPILE_THREAD

/* ---- tgCM_UTM_SN_RW_Init --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_SN_RW_Init( PCU_STg2_UTM_SN_RW psMP_RW )
{
    TgVERIFY( KTgS_OK == tgCM_UTM_SN_Init( &psMP_RW->m_sLock.m_sLock ) );
    tgCM_UTM_RW_INT_Init( &psMP_RW->m_sData );
    return (KTgS_OK);
}


/* ---- tgCM_UTM_SN_RW_Free --------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Free( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_RW_INT_Free( &psMP_RW->m_sData );
    tgCM_UTM_SN_Free( &psMP_RW->m_sLock.m_sLock );
}


/* ---- tgCM_UTM_SN_RW_Enter_Read_Yield_Block --------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Enter_Read_Yield_Block( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Yield( &psMP_RW->m_sLock.m_sLock );

    if (ETgUTM_RW_STATE__Writing != psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Reading;
        ++psMP_RW->m_sData.m_nuiReader;
        tgAM_WRITE_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
    }
    else
    {
        STg1_MP_SM                          sExecute;
        TgUINT32                            iIndex;

        TgERROR(psMP_RW->m_sData.m_nuiThread < KTgMP_MAX_READ_WRITER_LOCK);

        tgCM_MP_SM_Init( &sExecute, 1 );

        iIndex = (psMP_RW->m_sData.m_uiIndex + psMP_RW->m_sData.m_nuiThread) % KTgMP_MAX_READ_WRITER_LOCK;
        psMP_RW->m_sData.m_atiThread[iIndex].m_iKI = tgTR_Query_Id().m_iKI;
        psMP_RW->m_sData.m_abReader[iIndex] = TgTRUE;
        psMP_RW->m_sData.m_psExecute[iIndex] = &sExecute;
        ++psMP_RW->m_sData.m_nuiThread;

        tgAM_WRITE_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );

        tgCM_MP_SM_Wait_Block( &sExecute );
        tgCM_MP_SM_Free( &sExecute );
    };
}


/* ---- tgCM_UTM_SN_RW_Enter_Read_Spin_Block ---------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Enter_Read_Spin_Block( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Spin( &psMP_RW->m_sLock.m_sLock );

    if (ETgUTM_RW_STATE__Writing != psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Reading;
        ++psMP_RW->m_sData.m_nuiReader;
        tgAM_WRITE_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
    }
    else
    {
        STg1_MP_SM                          sExecute;
        TgUINT32                            iIndex;

        TgERROR(psMP_RW->m_sData.m_nuiThread < KTgMP_MAX_READ_WRITER_LOCK);

        tgCM_MP_SM_Init( &sExecute, 1 );

        iIndex = (psMP_RW->m_sData.m_uiIndex + psMP_RW->m_sData.m_nuiThread) % KTgMP_MAX_READ_WRITER_LOCK;
        psMP_RW->m_sData.m_atiThread[iIndex].m_iKI = tgTR_Query_Id().m_iKI;
        psMP_RW->m_sData.m_abReader[iIndex] = TgTRUE;
        psMP_RW->m_sData.m_psExecute[iIndex] = &sExecute;
        ++psMP_RW->m_sData.m_nuiThread;

        tgAM_WRITE_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );

        tgCM_MP_SM_Wait_Block( &sExecute );
        tgCM_MP_SM_Free( &sExecute );
    };
}


/* ---- tgCM_UTM_SN_RW_Enter_Read_Yield --------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_SN_RW_Enter_Read_Yield( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Yield( &psMP_RW->m_sLock.m_sLock );

    if (ETgUTM_RW_STATE__Writing != psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Reading;
        ++psMP_RW->m_sData.m_nuiReader;
        tgAM_WRITE_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
        return (KTgS_OK);
    }
    else
    {
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
        return (KTgE_FAIL);
    };
}


/* ---- tgCM_UTM_SN_RW_Enter_Read_Spin ---------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_SN_RW_Enter_Read_Spin( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Spin( &psMP_RW->m_sLock.m_sLock );

    if (ETgUTM_RW_STATE__Writing != psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Reading;
        ++psMP_RW->m_sData.m_nuiReader;
        tgAM_WRITE_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
        return (KTgS_OK);
    }
    else
    {
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
        return (KTgE_FAIL);
    };
}


/* ---- tgCM_UTM_SN_RW_Exit_Read_Yield ---------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Exit_Read_Yield( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Yield( &psMP_RW->m_sLock.m_sLock );
    TgERROR(psMP_RW->m_sData.m_enState == ETgUTM_RW_STATE__Reading);
    TgERROR(psMP_RW->m_sData.m_nuiReader > 0);

    --psMP_RW->m_sData.m_nuiReader;

    if (0 == psMP_RW->m_sData.m_nuiReader)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Waiting;
    };

    if (psMP_RW->m_sData.m_nuiThread > 0)
    {
        tgCM_UTM_RW_INT_Update( &psMP_RW->m_sData );
    };

    tgAM_READ_FENCE();
    tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
}


/* ---- tgCM_UTM_SN_RW_Exit_Read_Spin ----------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Exit_Read_Spin( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Spin( &psMP_RW->m_sLock.m_sLock );
    TgERROR(psMP_RW->m_sData.m_enState == ETgUTM_RW_STATE__Reading);
    TgERROR(psMP_RW->m_sData.m_nuiReader > 0);

    --psMP_RW->m_sData.m_nuiReader;

    if (0 == psMP_RW->m_sData.m_nuiReader)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Waiting;
    };

    if (psMP_RW->m_sData.m_nuiThread > 0)
    {
        tgCM_UTM_RW_INT_Update( &psMP_RW->m_sData );
    };

    tgAM_READ_FENCE();
    tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
}


/* ---- tgCM_UTM_SN_RW_Enter_Write_Yield_Block -------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Enter_Write_Yield_Block( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Yield( &psMP_RW->m_sLock.m_sLock );

    if (ETgUTM_RW_STATE__Waiting == psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Writing;
        TgERROR(psMP_RW->m_sData.m_nuiReader == 0);
        tgAM_READ_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
    }
    else
    {
        STg1_MP_SM                          sExecute;
        TgUINT32                            iIndex;

        TgERROR(psMP_RW->m_sData.m_nuiThread < KTgMP_MAX_READ_WRITER_LOCK);

        tgCM_MP_SM_Init( &sExecute, 1 );

        iIndex = (psMP_RW->m_sData.m_uiIndex + psMP_RW->m_sData.m_nuiThread) % KTgMP_MAX_READ_WRITER_LOCK;
        psMP_RW->m_sData.m_atiThread[iIndex].m_iKI = tgTR_Query_Id().m_iKI;
        psMP_RW->m_sData.m_abReader[iIndex] = TgFALSE;
        psMP_RW->m_sData.m_psExecute[iIndex] = &sExecute;
        ++psMP_RW->m_sData.m_nuiThread;

        tgAM_WRITE_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );

        tgCM_MP_SM_Wait_Block( &sExecute );
        tgCM_MP_SM_Free( &sExecute );
    };
}


/* ---- tgCM_UTM_SN_RW_Enter_Write_Spin_Block --------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Enter_Write_Spin_Block( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Spin( &psMP_RW->m_sLock.m_sLock );

    if (ETgUTM_RW_STATE__Waiting == psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Writing;
        TgERROR(psMP_RW->m_sData.m_nuiReader == 0);
        tgAM_READ_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
    }
    else
    {
        STg1_MP_SM                          sExecute;
        TgUINT32                            iIndex;

        TgERROR(psMP_RW->m_sData.m_nuiThread < KTgMP_MAX_READ_WRITER_LOCK);

        tgCM_MP_SM_Init( &sExecute, 1 );

        iIndex = (psMP_RW->m_sData.m_uiIndex + psMP_RW->m_sData.m_nuiThread) % KTgMP_MAX_READ_WRITER_LOCK;
        psMP_RW->m_sData.m_atiThread[iIndex].m_iKI = tgTR_Query_Id().m_iKI;
        psMP_RW->m_sData.m_abReader[iIndex] = TgFALSE;
        psMP_RW->m_sData.m_psExecute[iIndex] = &sExecute;
        ++psMP_RW->m_sData.m_nuiThread;

        tgAM_WRITE_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );

        tgCM_MP_SM_Wait_Block( &sExecute );
        tgCM_MP_SM_Free( &sExecute );
    };
}


/* ---- tgCM_UTM_SN_RW_Enter_Write_Yield -------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_SN_RW_Enter_Write_Yield( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Yield( &psMP_RW->m_sLock.m_sLock );

    if (ETgUTM_RW_STATE__Waiting == psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Writing;
        TgERROR(psMP_RW->m_sData.m_nuiReader == 0);
        tgAM_READ_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
        return (KTgS_OK);
    }
    else
    {
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
        return (KTgE_FAIL);
    };
}


/* ---- tgCM_UTM_SN_RW_Enter_Write_Spin --------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_SN_RW_Enter_Write_Spin( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Spin( &psMP_RW->m_sLock.m_sLock );

    if (ETgUTM_RW_STATE__Waiting == psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Writing;
        TgERROR(psMP_RW->m_sData.m_nuiReader == 0);
        tgAM_READ_FENCE();
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
        return (KTgS_OK);
    }
    else
    {
        tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
        return (KTgE_FAIL);
    };
}


/* ---- tgCM_UTM_SN_RW_Exit_Write_Yield --------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Exit_Write_Yield( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Yield( &psMP_RW->m_sLock.m_sLock );
    TgERROR(psMP_RW->m_sData.m_enState == ETgUTM_RW_STATE__Writing);

    psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Waiting;
    if (psMP_RW->m_sData.m_nuiThread > 0)
    {
        tgCM_UTM_RW_INT_Update( &psMP_RW->m_sData );
    };

    tgAM_WRITE_FENCE();
    tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
}


/* ---- tgCM_UTM_SN_RW_Exit_Write_Spin ---------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_SN_RW_Exit_Write_Spin( PCU_STg2_UTM_SN_RW psMP_RW )
{
    tgCM_UTM_SN_Lock_Spin( &psMP_RW->m_sLock.m_sLock );
    TgERROR(psMP_RW->m_sData.m_enState == ETgUTM_RW_STATE__Writing);

    psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Waiting;
    if (psMP_RW->m_sData.m_nuiThread > 0)
    {
        tgCM_UTM_RW_INT_Update( &psMP_RW->m_sData );
    };

    tgAM_WRITE_FENCE();
    tgCM_UTM_SN_Signal( &psMP_RW->m_sLock.m_sLock );
}

/*# TgCOMPILE_THREAD */
#endif


/* ---- FIFO Queuing Reader Writer Lock ----------------------------------------------------------------------------------------------------------------------------------- */

#if TgCOMPILE_THREAD

/* ---- tgCM_UTM_RW_Init ------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_RW_Init( PCU_STg2_UTM_RW psMP_RW )
{
    TgVERIFY( KTgS_OK == tgCM_MP_CS_Init( &psMP_RW->m_sLock ) );
    tgCM_UTM_RW_INT_Init( &psMP_RW->m_sData );
    return (KTgS_OK);
}


/* ---- tgCM_UTM_RW_Free ------------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_RW_Free( PCU_STg2_UTM_RW psMP_RW )
{
    tgCM_UTM_RW_INT_Free( &psMP_RW->m_sData );
    tgCM_MP_CS_Free( &psMP_RW->m_sLock );
}


/* ---- tgCM_UTM_RW_Enter_Read_Block ------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_RW_Enter_Read_Block( PCU_STg2_UTM_RW psMP_RW )
{
    tgCM_MP_CS_Enter_Block( &psMP_RW->m_sLock );

    if (ETgUTM_RW_STATE__Writing != psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Reading;
        ++psMP_RW->m_sData.m_nuiReader;
        tgCM_MP_CS_Exit( &psMP_RW->m_sLock );
    }
    else
    {
        STg1_MP_SM                          sExecute;
        TgUINT32                            iIndex;

        TgERROR(psMP_RW->m_sData.m_nuiThread < KTgMP_MAX_READ_WRITER_LOCK);

        tgCM_MP_SM_Init( &sExecute, 1 );

        iIndex = (psMP_RW->m_sData.m_uiIndex + psMP_RW->m_sData.m_nuiThread) % KTgMP_MAX_READ_WRITER_LOCK;
        psMP_RW->m_sData.m_atiThread[iIndex].m_iKI = tgTR_Query_Id().m_iKI;
        psMP_RW->m_sData.m_abReader[iIndex] = TgTRUE;
        psMP_RW->m_sData.m_psExecute[iIndex] = &sExecute;
        ++psMP_RW->m_sData.m_nuiThread;

        tgCM_MP_CS_Exit( &psMP_RW->m_sLock );

        tgCM_MP_SM_Wait_Block( &sExecute );
        tgCM_MP_SM_Free( &sExecute );
    };
}


/* ---- tgCM_UTM_RW_Enter_Read ------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_RW_Enter_Read( PCU_STg2_UTM_RW psMP_RW )
{
    tgCM_MP_CS_Enter_Block( &psMP_RW->m_sLock );

    if (ETgUTM_RW_STATE__Writing != psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Reading;
        ++psMP_RW->m_sData.m_nuiReader;
        tgCM_MP_CS_Exit( &psMP_RW->m_sLock );
        return (KTgS_OK);
    }
    else
    {
        tgCM_MP_CS_Exit( &psMP_RW->m_sLock );
        return (KTgE_FAIL);
    };
}


/* ---- tgCM_UTM_RW_Exit_Read ------------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_RW_Exit_Read( PCU_STg2_UTM_RW psMP_RW )
{
    tgCM_MP_CS_Enter_Block( &psMP_RW->m_sLock );
    TgERROR(psMP_RW->m_sData.m_enState == ETgUTM_RW_STATE__Reading);
    TgERROR(psMP_RW->m_sData.m_nuiReader > 0);

    --psMP_RW->m_sData.m_nuiReader;

    if (0 == psMP_RW->m_sData.m_nuiReader)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Waiting;
    };

    if (psMP_RW->m_sData.m_nuiThread > 0)
    {
        tgCM_UTM_RW_INT_Update( &psMP_RW->m_sData );
    };

    tgCM_MP_CS_Exit( &psMP_RW->m_sLock );
}


/* ---- tgCM_UTM_RW_Enter_Write_Block ----------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_RW_Enter_Write_Block( PCU_STg2_UTM_RW psMP_RW )
{
    tgCM_MP_CS_Enter_Block( &psMP_RW->m_sLock );

    if (ETgUTM_RW_STATE__Waiting == psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Writing;
        TgERROR(psMP_RW->m_sData.m_nuiReader == 0);
        tgCM_MP_CS_Exit( &psMP_RW->m_sLock );
    }
    else
    {
        STg1_MP_SM                          sExecute;
        TgUINT32                            iIndex;

        TgERROR(psMP_RW->m_sData.m_nuiThread < KTgMP_MAX_READ_WRITER_LOCK);

        tgCM_MP_SM_Init( &sExecute, 1 );

        iIndex = (psMP_RW->m_sData.m_uiIndex + psMP_RW->m_sData.m_nuiThread) % KTgMP_MAX_READ_WRITER_LOCK;
        psMP_RW->m_sData.m_atiThread[iIndex].m_iKI = tgTR_Query_Id().m_iKI;
        psMP_RW->m_sData.m_abReader[iIndex] = TgFALSE;
        psMP_RW->m_sData.m_psExecute[iIndex] = &sExecute;
        ++psMP_RW->m_sData.m_nuiThread;

        tgCM_MP_CS_Exit( &psMP_RW->m_sLock );

        tgCM_MP_SM_Wait_Block( &sExecute );
        tgCM_MP_SM_Free( &sExecute );
    };
}


/* ---- tgCM_UTM_RW_Enter_Write ----------------------------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgRESULT tgCM_UTM_RW_Enter_Write( PCU_STg2_UTM_RW psMP_RW )
{
    tgCM_MP_CS_Enter_Block( &psMP_RW->m_sLock );

    if (ETgUTM_RW_STATE__Waiting == psMP_RW->m_sData.m_enState)
    {
        psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Writing;
        TgERROR(psMP_RW->m_sData.m_nuiReader == 0);
        tgCM_MP_CS_Exit( &psMP_RW->m_sLock );
        return (KTgS_OK);
    }
    else
    {
        tgCM_MP_CS_Exit( &psMP_RW->m_sLock );
        return (KTgE_FAIL);
    };
}


/* ---- tgCM_UTM_RW_Exit_Write ------------------------------------------------------------------------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
TgINLINE TgVOID tgCM_UTM_RW_Exit_Write( PCU_STg2_UTM_RW psMP_RW )
{
    tgCM_MP_CS_Enter_Block( &psMP_RW->m_sLock );
    TgERROR(psMP_RW->m_sData.m_enState == ETgUTM_RW_STATE__Writing);

    psMP_RW->m_sData.m_enState = ETgUTM_RW_STATE__Waiting;
    if (psMP_RW->m_sData.m_nuiThread > 0)
    {
        tgCM_UTM_RW_INT_Update( &psMP_RW->m_sData );
    };

    tgCM_MP_CS_Exit( &psMP_RW->m_sLock );
}

/*# TgCOMPILE_THREAD */
#endif

/* ====================================================================================================================================================================== */
#endif