| /************************************************************************ | 
 | * °æÈ¨ËùÓÐ (C)2008, ÉîÛÚÊÐÖÐÐËͨѶ¹É·ÝÓÐÏÞ¹«Ë¾¡£ | 
 | *  | 
 | * ÎļþÃû³Æ£º soft_timer.c | 
 | * Îļþ±êʶ£º  | 
 | * ÄÚÈÝÕªÒª£º ¶¨ÒåͨÓõÄÈí¼þ¶¨Ê±Æ÷Ï߳̿â | 
 | * ÆäËü˵Ã÷£º  | 
 | * µ±Ç°°æ±¾£º V1.0 | 
 | * ×÷    Õߣº Ö£ÓñæÃ | 
 | * Íê³ÉÈÕÆÚ£º 2010-09-02 | 
 | *  | 
 | * Ð޸ļǼ1£º | 
 | *    ÐÞ¸ÄÈÕÆÚ£º2010-09-02 | 
 | *    °æ ±¾ ºÅ£ºV1.0 | 
 | *    ÐÞ ¸Ä ÈË£ºÖ£ÓñæÃ | 
 | *    ÐÞ¸ÄÄÚÈÝ£º³õʼ°æ±¾ | 
 | ************************************************************************/ | 
 | #include  <stdio.h> | 
 | #include  <stdlib.h> | 
 | #include  <signal.h> | 
 | #include  <sys/time.h> | 
 | #include  <syslog.h> | 
 | #include <fcntl.h> | 
 | #include  <pthread.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 | #include <errno.h> | 
 | #include  "soft_timer.h" | 
 |  | 
 | /* | 
 | #define SOFT_TIMER_DEBUG 1 | 
 | */ | 
 | /******************************************** | 
 |  * Macro declaration | 
 |  *******************************************/ | 
 | #define NEW_TIMER_STEP 10 | 
 | #define MAX_TIMER_POOL_NUM  33 | 
 |  | 
 | /******************************************** | 
 |  * static variables | 
 |  *******************************************/ | 
 | static SOFT_TIMER g_timer_pool[MAX_TIMER_POOL_NUM] = {0}; | 
 | static SOFT_TIMER *g_pTimerHeader = NULL; | 
 | static int g_timer_initialized = 0; | 
 |  | 
 | pthread_mutex_t g_timer_mutex = PTHREAD_MUTEX_INITIALIZER; | 
 |  | 
 | static void* SoftTimer_Loop(void *args); | 
 |  | 
 | /******************************************** | 
 |  * SoftTimer_Init | 
 |  * initialize global variables | 
 |  *******************************************/ | 
 | static int SoftTimer_Init(void) | 
 | { | 
 |     int  result = 0; | 
 |     pthread_attr_t attribute; | 
 |     pthread_t stHandle; | 
 |  | 
 |     if(0 == g_timer_initialized) | 
 |     { | 
 |         g_timer_initialized = 1; | 
 |  | 
 |         pthread_attr_init(&attribute); | 
 |         pthread_attr_setstacksize(&attribute, 32*1024); | 
 |  | 
 | 		#ifdef SOFT_TIMER_DEBUG | 
 |         printf("Create soft timer first time, must create a thread now!\n"); | 
 | 		#endif | 
 |         result = pthread_create(&stHandle, &attribute, SoftTimer_Loop, NULL); | 
 |         if(0 != result) | 
 |         { | 
 | 		    #ifdef SOFT_TIMER_DEBUG | 
 |             printf("StartSoftTimer failed, create thread fail %d!\n", result); | 
 | 			#endif | 
 |             return ERROR; | 
 |         } | 
 |  | 
 |         g_pTimerHeader = &g_timer_pool[0]; | 
 |         g_pTimerHeader->next = g_pTimerHeader; | 
 |         g_pTimerHeader->prev = g_pTimerHeader; | 
 |         g_pTimerHeader->used = 1; | 
 |     } | 
 |  | 
 |     return OK; | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * SetBasicTimerEx | 
 |  * call [setitimer] to set basic timer | 
 |  *******************************************/ | 
 | static void SetBasicTimerEx(int value) | 
 | { | 
 |     struct itimerval stTimer = {0}; | 
 |     struct itimerval oldTimer = {0}; | 
 |  | 
 | 	#ifdef SOFT_TIMER_DEBUG | 
 |     printf("SetBasicTimerEx: value=%d\n", value); | 
 | 	#endif | 
 |  | 
 |     stTimer.it_value.tv_sec = value / 1000; | 
 |     stTimer.it_value.tv_usec = (value * 1000) % 1000000; | 
 |  | 
 |     //stTimer.it_interval.tv_sec = stTimer.it_value.tv_sec; | 
 |     //stTimer.it_interval.tv_usec = stTimer.it_value.tv_usec; | 
 |  | 
 |     setitimer(ITIMER_REAL, &stTimer, &oldTimer); | 
 |  | 
 | 	#ifdef SOFT_TIMER_DEBUG | 
 |     printf("SetBasicTimerEx: tv_sec=%d, tv_usec=%d\n", stTimer.it_value.tv_sec, stTimer.it_value.tv_usec); | 
 |     printf("SetBasicTimerEx:oldTimer tv_sec=%d, tv_usec=%d\n", oldTimer.it_value.tv_sec, oldTimer.it_value.tv_usec); | 
 | 	#endif | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * SoftTimer_Add | 
 |  * add a soft timer to double linked list | 
 |  *******************************************/ | 
 | static int SoftTimer_Add(SOFT_TIMER *pHeader, SOFT_TIMER *pItem) | 
 | { | 
 |     SOFT_TIMER *pTmp = pHeader; | 
 |  | 
 |     if(NULL == pHeader || NULL == pItem) | 
 |     { | 
 |         return -1; | 
 |     } | 
 |  | 
 |     do | 
 |     { | 
 |         if(pTmp->timerValue <= pItem->timerValue) | 
 |         { | 
 |             pTmp = pTmp->next; | 
 |         } | 
 |         else | 
 |         { | 
 |             break; | 
 |         } | 
 |     } while(pTmp != pHeader); | 
 |  | 
 |     if(pTmp != pHeader) | 
 |     { | 
 |         pItem->next = pTmp; | 
 |         pItem->prev = pTmp->prev; | 
 |         pTmp->prev->next = pItem; | 
 |         pTmp->prev = pItem; | 
 |     } | 
 |     else | 
 |     { | 
 |         pItem->next = pTmp; | 
 |         pItem->prev = pTmp->prev; | 
 |         pTmp->prev->next = pItem; | 
 |         pTmp->prev = pItem; | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * SoftTimer_Delete | 
 |  * delete a soft timer from double linked list | 
 |  *******************************************/ | 
 | static int SoftTimer_Delete(SOFT_TIMER *pHeader, SOFT_TIMER *pItem) | 
 | { | 
 |     if(NULL == pHeader || NULL == pItem) | 
 |     { | 
 |         return -1; | 
 |     } | 
 |  | 
 |     if(pItem == pHeader) | 
 |     { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     pItem->prev->next = pItem->next; | 
 |     pItem->next->prev = pItem->prev; | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * SoftTimer_GetSize | 
 |  * get total size of double linked list | 
 |  *******************************************/ | 
 | static int SoftTimer_GetSize(SOFT_TIMER *pHeader) | 
 | { | 
 |     int size = 0; | 
 |     SOFT_TIMER *pTmp = pHeader->next; | 
 |  | 
 |     while(pTmp != pHeader) | 
 |     { | 
 |         size++; | 
 |         pTmp = pTmp->next; | 
 |     } | 
 |  | 
 | 	#ifdef SOFT_TIMER_DEBUG | 
 |     printf("SoftTimer_GetSize: size=%d\n", size); | 
 | 	#endif | 
 |     return size; | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * SoftTimer_Adjust | 
 |  * adjust soft-timer's value in the double linked list | 
 |  *******************************************/ | 
 | static int SoftTimer_AdjustTimerValue(SOFT_TIMER *pHeader, int value) | 
 | { | 
 |     SOFT_TIMER *pTmp = pHeader->next; | 
 |  | 
 |     while(pTmp != pHeader) | 
 |     { | 
 |         pTmp->timerValue -= value; | 
 |         pTmp = pTmp->next; | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * SoftTimer_Handler | 
 |  * handler of soft-timer | 
 |  *******************************************/ | 
 | static void SoftTimer_Handler(void) | 
 | { | 
 |     int index; | 
 |     SOFT_TIMER *pHeader = g_pTimerHeader; | 
 |     SOFT_TIMER *pTmp = pHeader->next; | 
 |     unsigned int curTime = 0; | 
 |  | 
 |     curTime = (pTmp != pHeader?pTmp->timerValue:0); | 
 |  | 
 | 	#ifdef SOFT_TIMER_DEBUG | 
 |     printf("SoftTimer_Ops: curTime=%d\n", curTime); | 
 | 	#endif | 
 |  | 
 |     pthread_mutex_lock(&g_timer_mutex); | 
 |  | 
 |     while(pTmp != pHeader) | 
 |     { | 
 |         pTmp->timerValue -= curTime; | 
 |         if(pTmp->timerValue <= 0) | 
 |         { | 
 |             pTmp->procCallBack(pTmp->args); | 
 |  | 
 |             if(TIMER_FLAG_RESTART == pTmp->ucFlag) | 
 |             { | 
 |                 pTmp->timerValue = pTmp->ulNextInterval; | 
 |             } | 
 |             else | 
 |             { | 
 |                 pTmp->timerValue = -1; | 
 |             } | 
 |             pTmp->done = 1; | 
 |         } | 
 |         pTmp = pTmp->next; | 
 |     } | 
 |  | 
 | 	#ifdef SOFT_TIMER_DEBUG | 
 |     printf("SoftTimer_Ops:[1] %d\n", SoftTimer_GetSize(pHeader)); | 
 | 	#endif | 
 |  | 
 |     for(index = 1; index < MAX_TIMER_POOL_NUM; index++) | 
 |     { | 
 |         if(g_timer_pool[index].used && g_timer_pool[index].done) | 
 |         { | 
 |             g_timer_pool[index].done = 0; | 
 |  | 
 |             SoftTimer_Delete(pHeader, &g_timer_pool[index]); | 
 |  | 
 |             if(g_timer_pool[index].timerValue > 0) | 
 |             { | 
 |                 SoftTimer_Add(pHeader, &g_timer_pool[index]); | 
 |             } | 
 |             else | 
 |             { | 
 |                 memset(&g_timer_pool[index], 0, sizeof(SOFT_TIMER)); | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 | 	#ifdef SOFT_TIMER_DEBUG | 
 |     printf("SoftTimer_Ops:[2] %d\n", SoftTimer_GetSize(pHeader)); | 
 | 	#endif | 
 |  | 
 |     SetBasicTimerEx(pHeader->next->timerValue); | 
 |  | 
 |     pthread_mutex_unlock(&g_timer_mutex); | 
 |  | 
 | 	#ifdef SOFT_TIMER_DEBUG | 
 |     printf("SoftTimer_Ops: nextTime=%d\n", pHeader->next->timerValue); | 
 |     #endif | 
 |  | 
 |     return; | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * SoftTimer_Loop | 
 |  * A thread entry for soft-timer! | 
 |  *******************************************/ | 
 | static void* SoftTimer_Loop(void *args) | 
 | { | 
 |     struct sigaction act; | 
 |  | 
 |     act.sa_handler =(void*) SoftTimer_Handler; | 
 |     act.sa_flags = 0; | 
 |     sigemptyset(&act.sa_mask); | 
 |     sigaction(SIGALRM, &act, NULL); | 
 |  | 
 |     while(1) | 
 |     { | 
 |         pause(); | 
 |     } | 
 | } | 
 |  | 
 | static void SoftTimer_Process(int freeIndex) | 
 | { | 
 |     ULONG ulResidue = 0; | 
 |     ULONG ulQuotient = 0; | 
 |     int oldTimeValue = 0; | 
 |     int leftTimeValue = 0; | 
 |  | 
 |     struct itimerval stTimer = {0}; | 
 |  | 
 |     if(0 == SoftTimer_GetSize(g_pTimerHeader)) | 
 |     { | 
 |         SoftTimer_Add(g_pTimerHeader, &( g_timer_pool[freeIndex])); | 
 |         SetBasicTimerEx(g_pTimerHeader->next->timerValue); | 
 |     } | 
 |     else | 
 |     { | 
 |         oldTimeValue = g_pTimerHeader->next->timerValue; | 
 |         if(oldTimeValue > 0) | 
 |         { | 
 | 		    #ifdef SOFT_TIMER_DEBUG | 
 |             printf("CreateSoftTimer: oldTimeValue=%d\n", oldTimeValue); | 
 | 			#endif | 
 |             getitimer(ITIMER_REAL, &stTimer); | 
 | 			#ifdef SOFT_TIMER_DEBUG | 
 |             printf("CreateSoftTimer: tv_sec=%d, tv_usec=%d\n", stTimer.it_value.tv_sec, stTimer.it_value.tv_usec); | 
 |             #endif | 
 |             leftTimeValue = stTimer.it_value.tv_sec * 1000 + (stTimer.it_value.tv_usec/1000); | 
 |  | 
 |             ulResidue = leftTimeValue % NEW_TIMER_STEP; | 
 |             if (0 != ulResidue) | 
 |             { | 
 |                 ulQuotient = leftTimeValue / NEW_TIMER_STEP; | 
 |                 leftTimeValue = (ulQuotient + 1) * NEW_TIMER_STEP; | 
 |             } | 
 |             #ifdef SOFT_TIMER_DEBUG | 
 |             printf("CreateSoftTimer: oldTimeValue=%d, leftTimeValue=%d\n", oldTimeValue, leftTimeValue); | 
 |             #endif | 
 |             if(oldTimeValue - leftTimeValue >=  NEW_TIMER_STEP) | 
 |             { | 
 |                 SoftTimer_AdjustTimerValue(g_pTimerHeader, oldTimeValue - leftTimeValue); | 
 |             } | 
 |  | 
 |             SoftTimer_Add(g_pTimerHeader, &( g_timer_pool[freeIndex])); | 
 |             SetBasicTimerEx(g_pTimerHeader->next->timerValue); | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * CreateSoftTimer | 
 |  * create a new soft-timer! | 
 |  *******************************************/ | 
 | LONG CreateSoftTimer(USHORT usTimerID, | 
 |                      UCHAR ucFlag, | 
 |                      ULONG ulInterval, | 
 |                      void * (*procCallBack)(void *), | 
 |                      void *args) | 
 | { | 
 |     int ucIndex = 0; | 
 |     int freeIndex = 0; | 
 |  | 
 |     ULONG ulResidue = 0; | 
 |     ULONG ulQuotient = 0; | 
 |  | 
 |     if (NULL == procCallBack) | 
 |     { | 
 | 	    #ifdef SOFT_TIMER_DEBUG | 
 |         printf("CreateSoftTimer failed, procCallBack is NULL!\n"); | 
 | 		#endif | 
 |         return ERROR; | 
 |     } | 
 |  | 
 |     pthread_mutex_lock(&g_timer_mutex); | 
 |  | 
 |     if(OK != SoftTimer_Init()) | 
 |     { | 
 |         pthread_mutex_unlock(&g_timer_mutex); | 
 |         return ERROR; | 
 |     } | 
 |  | 
 |     for(ucIndex = 1, freeIndex = 0; ucIndex < MAX_TIMER_POOL_NUM; ucIndex++) | 
 |     { | 
 |         /*same timer id exist, warning, return error*/ | 
 |         if (g_timer_pool[ucIndex].used && (usTimerID == g_timer_pool[ucIndex].usTimerID)) | 
 |         { | 
 | 		    #ifdef SOFT_TIMER_DEBUG | 
 |             printf("Same timer id 0x%04x exist!\n", usTimerID); | 
 | 			#endif | 
 |             pthread_mutex_unlock(&g_timer_mutex); | 
 |             return ERROR; | 
 |         } | 
 |         if ((0 == g_timer_pool[ucIndex].used) && (0 == freeIndex)) | 
 |         { | 
 |             freeIndex = ucIndex; | 
 |         } | 
 |     } | 
 |  | 
 |     /* No empty timer left */ | 
 |     if (0 == freeIndex) | 
 |     { | 
 | 	    #ifdef SOFT_TIMER_DEBUG | 
 |         printf("StartSoftTimer 0x%04x failed, queue full!\n", usTimerID); | 
 | 		#endif | 
 |         pthread_mutex_unlock(&g_timer_mutex); | 
 |         return ERROR; | 
 |     } | 
 |  | 
 |     /* Adjust interval value, it must multiple of 10 */ | 
 |     ulResidue = ulInterval % NEW_TIMER_STEP; | 
 |     if (0 != ulResidue) | 
 |     { | 
 |         ulQuotient = ulInterval / NEW_TIMER_STEP; | 
 |         ulInterval = (ulQuotient + 1) * NEW_TIMER_STEP; | 
 |     } | 
 |  | 
 |     g_timer_pool[freeIndex].used = 1; | 
 |     g_timer_pool[freeIndex].usTimerID = usTimerID; | 
 |     g_timer_pool[freeIndex].ucFlag = ucFlag; | 
 |     g_timer_pool[freeIndex].ulCurInterval = ulInterval; | 
 |     g_timer_pool[freeIndex].ulNextInterval = ulInterval; | 
 |     g_timer_pool[freeIndex].procCallBack = procCallBack; | 
 |     g_timer_pool[freeIndex].args = args; | 
 |     g_timer_pool[freeIndex].timerValue = ulInterval; | 
 |  | 
 |     SoftTimer_Process(freeIndex); | 
 |     #ifdef SOFT_TIMER_DEBUG | 
 |     printf("CreateSoftTimer index %d success: \n" | 
 |            "usTimerID = %d, \n" | 
 |            "ucFlag = %d, \n" | 
 |            "ulCurInterval = %ld, \n" | 
 |            "ulNextInterval = %ld, \n" | 
 |            "procCallBack = %p, \n" | 
 |            "args =  %p. \n", | 
 |            freeIndex, | 
 |            usTimerID, | 
 |            ucFlag, | 
 |            ulInterval, | 
 |            ulInterval, | 
 |            procCallBack, | 
 |            args); | 
 |     #endif | 
 |     pthread_mutex_unlock(&g_timer_mutex); | 
 |  | 
 |     return OK; | 
 | } | 
 |  | 
 | /******************************************** | 
 |  * DeleteSoftTimer | 
 |  * delete a soft-timer! | 
 |  *******************************************/ | 
 | LONG DeleteSoftTimer(USHORT usTimerID) | 
 | { | 
 |     UCHAR ucIndex = 0; | 
 |  | 
 |     pthread_mutex_lock(&g_timer_mutex); | 
 |  | 
 |     for(ucIndex = 1; ucIndex < MAX_TIMER_POOL_NUM; ucIndex++) | 
 |     { | 
 |         if(g_timer_pool[ucIndex].used && (usTimerID == g_timer_pool[ucIndex].usTimerID)) | 
 |         { | 
 |             break; | 
 |         } | 
 |     } | 
 |  | 
 |     if (ucIndex >= MAX_TIMER_POOL_NUM) | 
 |     { | 
 | 	    #ifdef SOFT_TIMER_DEBUG | 
 |         printf("DeleteSoftTimer 0x%04x failed, not exist!\n", usTimerID); | 
 | 		#endif | 
 |         pthread_mutex_unlock(&g_timer_mutex); | 
 |         return ERROR; | 
 |     } | 
 |  | 
 |     SoftTimer_Delete(g_pTimerHeader, &( g_timer_pool[ucIndex])); | 
 |     memset(&g_timer_pool[ucIndex], 0, sizeof(SOFT_TIMER)); | 
 |     #ifdef SOFT_TIMER_DEBUG | 
 |     printf("DeleteSoftTimer 0x%04x success!\n", usTimerID); | 
 |     #endif | 
 |     pthread_mutex_unlock(&g_timer_mutex); | 
 |     return OK; | 
 | } |