static __inline__ ClRcT clAmsEntityTriggerRecoveryListAdd(ClAmsEntityTriggerT *pEntityTrigger)
{
    clOsalMutexLock(&gClAmsEntityTriggerRecoveryCtrl.list.mutex);
    clListAddTail(&pEntityTrigger->list, 
                  &gClAmsEntityTriggerRecoveryCtrl.list.list);
    ++gClAmsEntityTriggerRecoveryCtrl.list.numElements;
    clOsalCondSignal(&gClAmsEntityTriggerRecoveryCtrl.list.cond);
    clOsalMutexUnlock(&gClAmsEntityTriggerRecoveryCtrl.list.mutex);
    return CL_OK;
}
ClRcT clTransportNotifyRegister(ClTransportNotifyCallbackT callback, ClPtrT arg)
{
    ClTransportNotifyRegistrantT *registrant = NULL;
    if(!callback) return CL_ERR_INVALID_PARAMETER;
    registrant = clHeapCalloc(1, sizeof(*registrant));
    CL_ASSERT(registrant != NULL);
    registrant->callback = callback;
    registrant->arg = arg;
    clListAddTail(&registrant->list, &gClXportNotifyRegistrants);
    return CL_OK;
}
static void clAmsEntityTriggerListAdd(ClAmsEntityTriggerT *pEntityTrigger)
{
    ClUint32T hashKey = 0;
    ClRcT rc = clCksm32bitCompute((ClUint8T*)pEntityTrigger->entity.name.value,
                                  pEntityTrigger->entity.name.length,
                                  &hashKey);
    CL_ASSERT(rc == CL_OK);
    hashKey &= CL_AMS_ENTITY_THRESHOLD_BUCKET_MASK;

    hashAdd(gClAmsEntityTriggerList.hashTable, hashKey, &pEntityTrigger->hash);
    clListAddTail(&pEntityTrigger->list, &gClAmsEntityTriggerList.list.list);
    ++gClAmsEntityTriggerList.list.numElements;
    
}
/*
 * The recovery thread for entity threshold.
 */
static ClPtrT clAmsEntityTriggerRecoveryThread(ClPtrT pArg)
{
#define MAX_RECOVERY_MASK (0x3)

    static ClInt32T numRecovery = 0;
    ClTimerTimeOutT timeout = { 0,  0 };
    ClTimerTimeOutT delay =   { 0,  500 };

    static ClAmsListHeadT recoveryList = { 0, CL_LIST_HEAD_INITIALIZER(recoveryList.list) };
    clOsalMutexLock(&gClAmsEntityTriggerRecoveryCtrl.list.mutex);
    while(gClAmsEntityTriggerRecoveryCtrl.running == CL_TRUE)
    {
        ClListHeadT *pNext = NULL;

        while(!gClAmsEntityTriggerRecoveryCtrl.list.numElements)
        {
            numRecovery = 0;
            clOsalCondWait(&gClAmsEntityTriggerRecoveryCtrl.list.cond,
                           &gClAmsEntityTriggerRecoveryCtrl.list.mutex,
                           timeout);
            if(gClAmsEntityTriggerRecoveryCtrl.running == CL_FALSE)
                goto out_drain;
        }
        /*
         * We are here when the recovery list isn't empty.
         * We shove the existing recovery batch into the temp list 
         * and reset the main list to release the lock and go lockless.
         */
        recoveryList.numElements = gClAmsEntityTriggerRecoveryCtrl.list.numElements;
        gClAmsEntityTriggerRecoveryCtrl.list.numElements = 0;
        pNext = gClAmsEntityTriggerRecoveryCtrl.list.list.pNext;
        clListDelInit(&gClAmsEntityTriggerRecoveryCtrl.list.list);
        clListAddTail(pNext, &recoveryList.list);
        clOsalMutexUnlock(&gClAmsEntityTriggerRecoveryCtrl.list.mutex);
        
        while(gClAmsEntityTriggerRecoveryCtrl.running == CL_TRUE
              &&
              recoveryList.numElements > 0
              &&
              !CL_LIST_HEAD_EMPTY(&recoveryList.list))
        {
            ClListHeadT *pFirst = recoveryList.list.pNext;
            ClAmsEntityTriggerT *pEntityTrigger = CL_LIST_ENTRY(pFirst, ClAmsEntityTriggerT, list);
            
            /*
             * Take a break incase you have done some recoveries
             */
            if(numRecovery && !(numRecovery & MAX_RECOVERY_MASK))
            {
                clOsalTaskDelay(delay);
            }
            ++numRecovery;
            numRecovery &= 0xffff;
            /*
             * Unlink the entry from the recovery list.
             */
            clListDel(pFirst);
            clAmsEntityTriggerRecovery(pEntityTrigger);
            clHeapFree(pEntityTrigger);
            --recoveryList.numElements;
        }

        clOsalMutexLock(&gClAmsEntityTriggerRecoveryCtrl.list.mutex);

        if(gClAmsEntityTriggerRecoveryCtrl.running == CL_FALSE)
        {
            goto out_drain;
        }

    }

    /*
     * Drain the entries holding the lock.
     */
    out_drain:
    clAmsListHeadDelete(&recoveryList);
    clAmsListHeadDelete(&gClAmsEntityTriggerRecoveryCtrl.list);
    clOsalMutexUnlock(&gClAmsEntityTriggerRecoveryCtrl.list.mutex);
    
    return NULL;
}