Click here to Skip to main content
15,881,173 members
Articles / Programming Languages / C++

Create Cross-platform Thread-Independent Event Loops

Rate me:
Please Sign up or sign in to vote.
4.89/5 (5 votes)
12 May 20076 min read 45.7K   687   28  
This article discloses what is behind the GetMessage() and PostThreadMessage() Windows API, and implements them on Linux and Windows platforms using basic operation system functions.
/* ==============================================================================================================================
 * This notice must be untouched at all times.
 *
 * Copyright  IntelliWizard Inc. 
 * All rights reserved.
 * LICENSE: LGPL. 
 * Redistributions of source code modifications must send back to the Intelliwizard Project and republish them. 
 * Web: http://www.intelliwizard.com
 * eMail: info@intelliwizard.com
 * We provide technical supports for UML StateWizard users. The StateWizard users do NOT have to pay for technical supports 
 * from the Intelliwizard team. We accept donation, but it is not mandatory.
 * ==============================================================================================================================*/

// Platform.cpp
// The cross-platform operation system functions 

#include "sme_cross_platform.h"
#include "sme_ext_event.h"

int XCreateThread(XTHREAD_PROC_T start_routine, void *arg,
                          XTHREADHANDLE *new_thread_handle)
{

#if defined LINUX 
	pthread_attr_t attr;
#endif

    XTHREADHANDLE  thr_handle = 0;

    int   ret_code = 0; 

    if (start_routine == NULL) {
		return -1;
    }

	memset(&thr_handle, 0, sizeof(XTHREADHANDLE));

#if defined LINUX 
    pthread_attr_init(&attr);
	// When a thread is created detached (PTHREAD_CREATE_DETACHED), its thread ID and other resources can be reused as soon as 
	// the thread terminates.
	// When a thread is created nondetached (PTHREAD_CREATE_JOINABLE), it is assumed that you will be waiting for it. 
	// That is, it is assumed that you will be executing a pthread_join() on the thread. 
	// Whether a thread is created detached or nondetached, the process does not exit until all threads have exited.
    if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
		return -1;
    
    if ((ret_code = pthread_create(&thr_handle, &attr, start_routine, arg)) == 0) { 
#elif defined WIN32
    thr_handle = (HANDLE)_beginthread((void (__cdecl *)(void *))start_routine, 0, arg);
    if (-1 != (int)thr_handle) {
		ret_code = errno; 
#endif 

        if (new_thread_handle != NULL)
            *new_thread_handle = thr_handle;

        return 0;
    }
    else {
        return -1;
    }
}

void XEndThread()
{
#if defined LINUX 
    pthread_exit((void*)0);

#elif defined WIN32
    _endthread();

#endif 
}

XTHREADID XGetCurrentThreadId()
{
#if defined LINUX 
    return pthread_self();

#elif defined WIN32
    return GetCurrentThreadId();

#endif 
}


BOOL XIsThreadRunning(XTHREADHANDLE thread_handle)
{
#if defined LINUX 
    if (pthread_kill(thread_handle, 0) == 0)
#elif defined WIN32
		if (GetThreadPriority(thread_handle) != THREAD_PRIORITY_ERROR_RETURN)
#endif 
        return TRUE;
    else
        return FALSE;
}

int XWaitForThread(XTHREADHANDLE ThreadHandle)
{
#if defined LINUX 
	do{
		XSleep(100);
	} while (XIsThreadRunning(ThreadHandle));
	return 0;
#elif defined WIN32
	return WaitForSingleObject(ThreadHandle,INFINITE);
#endif 
}

/////////////////////////////////////////////////////////////////////////////////////////////////////

BOOL XCreateProcess(const char* pProgramPath,int* ppid)
{
#ifdef WIN32
  //## begin OSRelatedSpawnProcess%42660B6200A9.body preserve=yes
	BOOL bRet;
	STARTUPINFO	sinfo;
	PROCESS_INFORMATION pinfo;
	char CurDir[MAX_PATH];
	memset(&sinfo,0,sizeof(sinfo));
	memset(&pinfo,0,sizeof(pinfo));

	GetStartupInfo(&sinfo);

	strcpy(CurDir, pProgramPath);
	PathRemoveFileSpec(CurDir); // Get the file path.
	//*(strrchr(CurDir, '\\'))=0;
	bRet = CreateProcess(pProgramPath, NULL, NULL, NULL, FALSE, CREATE_NO_WINDOW,
		NULL, CurDir,  &sinfo, &pinfo);
	if(bRet && ppid){
		*ppid=(int)(pinfo.dwProcessId);
	}
	return bRet?TRUE:FALSE;
#else
	int pid=fork();
	switch((int)pid)
	{
	case 0:
		// Child process
		{
			char *dir_buf=new char[strlen(pProgramPath)+1];
			strcpy(dir_buf, pProgramPath);
			//chdir(dirname(dir_buf)); //???
			execl(pProgramPath, pProgramPath, NULL);
			_exit(-1);
		}
		break;
	case -1:
		// Error
		break;
	default:
		// Parent process
		break;
	}
	if(ppid) *ppid=(int)pid;
	return TRUE;
#endif
}

void XKillProcess(int pid)
{
#ifdef WIN32
	HANDLE hProc; 
	if (pid==-1)
		return;
	hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
	if(hProc)
	{
		TerminateProcess(hProc, 0);
		CloseHandle(hProc);
	}
#else
	if (pid==-1)
		return;
	kill(pid, SIGKILL);
#endif
}

BOOL XIsProcessRunning(int pid)
{
#ifdef WIN32
	HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
	if(hProc)
	{
		CloseHandle(hProc);
		return TRUE;
	} else
		return FALSE;
#else
	int nRet = kill(pid, 0);
	if (nRet==0)
		return TRUE;
	else 
		return FALSE;
#endif
}

/****************************************************************************************************
 Time and Clock 
*****************************************************************************************************/
void XSleep(unsigned int milliseconds)
{
#ifdef WIN32
	Sleep(milliseconds);
#else
	usleep(milliseconds * 1000);
#endif
}

int XGetTick(void)
{
#ifdef WIN32
  return (int)GetTickCount();
#else
  struct timeval val;
  gettimeofday(&val, NULL);
  return (val.tv_sec *1000 + val.tv_usec / 1000);
#endif
}

char* XGetTimeStr(time_t nTime, char *szBuf, int nLen, const char* szFmt)
{
	const struct tm *pTime =localtime(&nTime);
	char s_sTime[256];
	memset(s_sTime,0,sizeof(s_sTime));

	if (nTime!=0)
		strftime(s_sTime, sizeof(s_sTime), szFmt, pTime);
	
	// Return an blank string if nTime is 0.
	if (szBuf && nLen>0)
	{
		strncpy(szBuf,s_sTime, nLen);
		szBuf[nLen-1] = 0;
	}

	return szBuf;
}


char* XGetCurrentTimeStr(char *szBuf, int nLen, const char* szFmt)
{
	time_t nTime=0;
	const struct tm *pTime; 
	char s_sTime[256];
	time(&nTime);
	pTime =localtime(&nTime);
	memset(s_sTime,0,sizeof(s_sTime));
	strftime(s_sTime, sizeof(s_sTime), szFmt, pTime);

	if (szBuf && nLen>0)
	{
		strncpy(szBuf,s_sTime, nLen);
		szBuf[nLen-1] = 0;
	}

	return szBuf;
}

#define STR_FMT_ELAPSED_TIME "%M:%S"

char* XGetElapsedTimeStr(time_t nTime, char* szBuf, int nLen)
{
	// If the default time zone is n. If nTime is 0, the time with the format "%H:%M:%S" will be "n:0:0"
	const struct tm *pTime =localtime(&nTime);
	char s_sTime[256];
	memset(s_sTime,0,sizeof(s_sTime));
	strftime(s_sTime, sizeof(s_sTime), STR_FMT_ELAPSED_TIME, pTime);

	if (szBuf && nLen>0)
	{
		strncpy(szBuf,s_sTime, nLen);
		szBuf[nLen-1] = 0;
	}

	return szBuf;
}

///////////////////////////////////////////////////////////////////////
// Mutext
///////////////////////////////////////////////////////////////////////
int XCreateMutext(XMUTEX  *mutex_ptr)
{
    int  ret_code = 0; 

    if (mutex_ptr == NULL) {
        return -1;
    }

    // the mutex can be used to sync. threads in this process only.
#if defined LINUX
	// A mutex has two possible states: unlocked (not owned by  any  thread),
    // and  locked  (owned  by one thread). A mutex can never be owned by two
    // different threads simultaneously. A thread attempting to lock a  mutex
    // that is already locked by another thread is suspended until the owning
    // thread unlocks the mutex first.
    if ((ret_code = pthread_mutex_init(mutex_ptr,NULL)) == 0) 
#elif defined WIN32
	// The state of a mutex object is signaled when it is not owned by any thread.
	// The creating thread can use the bInitialOwner flag to request immediate ownership of the mutex. 
	// Otherwise, a thread must use one of the wait functions to request ownership. 
	// When the mutex's state is signaled, one waiting thread is granted ownership, the mutex's state changes to nonsignaled, 
	// and the wait function returns. Only one thread can own a mutex at any given time. 
	// The owning thread uses the ReleaseMutex function to release its ownership.	

	// bInitialOwner: [in] If this value is TRUE and the caller created the mutex, the calling thread obtains initial ownership of the mutex object. Otherwise, the calling thread does not obtain ownership of the mutex. To determine if the caller created the mutex, see the Return Values section.
    *mutex_ptr = CreateMutex(NULL, /*bInitialOwner*/FALSE, NULL);
    if ( *mutex_ptr != NULL)
#endif
        return 0;
    else {
        return -1;
    }
}

int XMutexLock(XMUTEX *mutex_ptr)
{
    int  ret_code = 0; 

    if (mutex_ptr == NULL) {
        return -1;
    }

#if defined LINUX 
    if ((ret_code = pthread_mutex_lock(mutex_ptr)) == 0) 
#elif defined WIN32
    if (WaitForSingleObject(*mutex_ptr, INFINITE)!= WAIT_FAILED)
#endif
        return 0; // the mutex's state changes to nonsignaled
    else {
        return -1;
    }
}

int XMutexUnlock(XMUTEX *mutex_ptr)
{
	int  ret_code = 0; 
    
    if (mutex_ptr == NULL){
        return -1;
    }

#if defined LINUX 
    if ((ret_code = pthread_mutex_unlock(mutex_ptr)) == 0) 
#elif defined WIN32
	// Releases ownership of the specified mutex object.
    if (ReleaseMutex(*mutex_ptr) != 0)
#endif
        return 0; // Release the ownership. The mutex's state changes to signaled. 
    else {
        return -1;
    }
}

int XMutexDestroy(XMUTEX  *mutex_ptr)
{
    int  ret_code = 0; 

    if (mutex_ptr == NULL) {
        return -1;
    }

    // the mutex can be used to sync. threads in this process only.
#if defined LINUX 
    if ((ret_code = pthread_mutex_destroy(mutex_ptr)) == 0)
#elif defined WIN32
    if (CloseHandle(*mutex_ptr) != 0)
#endif
        return 0;
    else {
        return -1;
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Event
///////////////////////////////////////////////////////////////////////////////////////////////////
int XCreateEvent(XEVENT *pEvent)
{
	if (pEvent==NULL)
		return -1;
#ifdef WIN32
	*pEvent = GetCurrentThreadId();
	return 0;
#else
	return pthread_cond_init(pEvent, NULL);
#endif
}

int XDestroyEvent(XEVENT *pEvent)
{
	if (pEvent==NULL)
		return -1;

#ifdef WIN32

	return 0;
#else
	return pthread_cond_destroy(pEvent);
#endif
}

// Wait for an event signaled and then take some thread-safe actions.
int XWaitForEvent(XEVENT *pEvent, XMUTEX *pMutex, unsigned long MiliSec, XIS_CODITION_OK_T pIsConditionOK, void *pCondParam,
				  XTHREAD_SAFE_ACTION_T pAction, void *pActionParam)
{
#ifdef WIN32

	DWORD nRet=WAIT_OBJECT_0;
	MSG WinMsg;

	if (pEvent==NULL || pMutex==NULL || pIsConditionOK==NULL)
		return -1;

	// GetMessage()
	// If the function retrieves a message other than WM_QUIT, the return value is nonzero.
	// If the function retrieves the WM_QUIT message, the return value is zero. 
	while (GetMessage(&WinMsg, NULL, 0, 0))
	{
		if (WinMsg.message == WM_EXT_EVENT_ID)
		{
			// External event is triggered. App go to running state.
			if (pAction)
			{
				XMutexLock(pMutex);
				(*pAction)(pActionParam);
				XMutexUnlock(pMutex);
			}
			return  0; 
		} 
		// Handle Windows messages in MMI thread, for example audio messages.
		else //if (handle_win_msg_in_mmi_thread(&WinMsg) == FALSE)
		{
			// MSDN: DispatchMessage API
			// The DispatchMessage function dispatches a message to a window procedure.

			// MSDN: DefWindowProc API
			// The DefWindowProc function calls the default window procedure to provide default 
			// processing for any window messages that an application does not process. This 
			// function ensures that every message is processed. 

			// MSDN: WM_TIMER
			// The WM_TIMER message is posted to the installing thread's message queue 
			// when a timer expires. You can process the message by providing a WM_TIMER case 
			// in the window procedure. Otherwise, the default window procedure (DefWindowProc) 
			// will call the TimerProc callback function specified in the call to the SetTimer 
			// function used to install the timer. 

			// Handle Windows messages by the default window procedure in MMI thread  
			// for example calling the TimerProc callback function for timer messages.
			DispatchMessage(&WinMsg);
		};
	}

	return 0;
#else

	if (pEvent==NULL || pMutex==NULL || pIsConditionOK==NULL)
		return -1;
	// pthread_cond_wait() atomically unlocks the mutex (as per pthread_unlock_mutex) and waits for the condition variable cond to be  signaled.  The  thread
    //   execution is suspended and does not consume any CPU time until the condition variable is signaled. The mutex must be locked by the calling thread on
    //   entrance to pthread_cond_wait.  Before returning to the calling thread, pthread_cond_wait re-acquires mutex (as per pthread_lock_mutex).

	pthread_mutex_lock(pMutex);

	struct timespec timeout;
	time_t now;
	int rc=0;  

	while (!(*pIsConditionOK)(pCondParam))
	{
		if (time(&now) < 0)
			return -1;
		timeout.tv_sec = now + (MiliSec / 1000);
		timeout.tv_nsec = 0;
		rc =  pthread_cond_timedwait(pEvent, pMutex, &timeout);
	}

	if (rc != ETIMEDOUT)
	{
		if (pAction)
			(*pAction)(pActionParam);
	}

	pthread_mutex_unlock(pMutex);

	if (rc == ETIMEDOUT)
		return XWAIT_TIMEOUT; // Actually XWAIT_TIMEOUT is ETIMEDOUT
	else
		return rc;
#endif
}

// Take some thread-safe actions before signal the event.
int XSignalEvent(XEVENT *pEvent, XMUTEX *pMutex, XTHREAD_SAFE_ACTION_T pAction, void *pActionParam)
{
	int ret=0; 
	if (pEvent==NULL || pMutex==NULL)
		return -1;
#ifdef WIN32

	if (pAction)
	{
		XMutexLock(pMutex);
		(*pAction)(pActionParam); // At this time, (*pIsConditionOK)() should return TRUE.
		XMutexUnlock(
			pMutex);
	}

	if (0==*pEvent) return -1;

	PostThreadMessage(*pEvent, WM_EXT_EVENT_ID, 0, 0);
	return 0;
#else

	if (pMutex==NULL)
		return -1;

	pthread_mutex_lock(pMutex);
	// Modifications on the shared resources, which are protected by the mutex, meet the condition and should signal the if needed.
	if (pAction)
		(*pAction)(pActionParam);

	// pthread_cond_signal restarts one of the threads that are waiting on the condition variable condition.
	// pthread_cond_broadcast restarts all of the threads that are waiting on the condition variable condition.
	ret = pthread_cond_broadcast(pEvent);
	pthread_mutex_unlock(pMutex);

	return ret;

#endif
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Thread Local Storage.
#ifdef WIN32
	static unsigned long g_dwTlsIndex=0xFFFFFFFF;
#else
	typedef struct TLS_DATA_T_TAG
	{
		pthread_t nPThreadID;
		SME_THREAD_CONTEXT_PT pThreadContext;
	} TLS_DATA_T;

	#define SME_MAX_TLS_NUM  10
	static TLS_DATA_T g_TlsData[SME_MAX_TLS_NUM];
#endif


int XTlsAlloc()
{ 
#ifdef WIN32
	g_dwTlsIndex = TlsAlloc();
#else
	memset(g_TlsData,0,sizeof(g_TlsData));
#endif
	return 0;
}

BOOL XSetThreadContext(SME_THREAD_CONTEXT_PT p)
{
#ifdef WIN32
	if (0xFFFFFFFF != g_dwTlsIndex)
		return TlsSetValue(g_dwTlsIndex, p);
	else
		return FALSE;
#else
	/* A version of get_thread_area() first appeared in Linux 2.5.32.
	struct user_desc u_info;
	memset(&u_info,0,sizeof(u_info));
	u_info.entry_number =1;
	u_info.base_addr = (void*)p;
	return (0==set_thread_area (&u_info));
	*/
	int i=0;
	for (i=0; i<SME_MAX_TLS_NUM; i++)
	{
		if (NULL==g_TlsData[i].pThreadContext)
		{
			g_TlsData[i].nPThreadID = pthread_self();
			g_TlsData[i].pThreadContext = p;
			return TRUE;
		}
	}
	return FALSE;
#endif
}

SME_THREAD_CONTEXT_PT XGetThreadContext()
{
#ifdef WIN32
	if (0xFFFFFFFF != g_dwTlsIndex)
		return (SME_THREAD_CONTEXT_T*)TlsGetValue(g_dwTlsIndex);
	else
		return NULL;
#else
	/* A version of get_thread_area() first appeared in Linux 2.5.32.
	struct user_desc u_info;
	int nRet;
	memset(&u_info,0,sizeof(u_info));
	nRet = get_thread_info(&u_info);
	if (0==nRet)
		return u_info.base_addr;
	else
		return NULL;
	*/
	int i=0;
	pthread_t nSelf = pthread_self();
	for (i=0; i<SME_MAX_TLS_NUM; i++)
	{
		if (nSelf==g_TlsData[i].nPThreadID)
		{
			return g_TlsData[i].pThreadContext;
		}
	}
	return NULL;
#endif
}

BOOL XFreeThreadContext(SME_THREAD_CONTEXT_PT p)
{
#ifdef WIN32
	return FALSE;
#else
	int i=0;
	for (i=0; i<SME_MAX_TLS_NUM; i++)
	{
		if (p==g_TlsData[i].pThreadContext)
		{
			g_TlsData[i].pThreadContext=NULL;
			return TRUE;
		}
	}
	return FALSE;
#endif
}
///////////////////////////////////////////////////////////////////////////////////////////////////
/******************************************************************************************
*  Built-in Timer
******************************************************************************************/
static unsigned long g_nTimerSeqNum =1;

typedef struct tagXTIMER_T
{
	unsigned int nTimerID;
	unsigned int nTimeOut;
	int nLeft;
	unsigned long nSequenceNum;
	SME_TIMER_PROC_T pfnTimerFunc;
	SME_APP_T *pDestApp;
	SME_THREAD_CONTEXT_T *pDestThread;
	struct tagXTIMER_T *pNext;
} XTIMER_T;

static XTIMER_T *g_pTimerList=NULL;



#ifdef WIN32

// The running thread of this function is as same as the running thread of the XSetTimer() namely application thread.
void CALLBACK WinTimerProc(HWND hwnd, UINT uMsg, UINT_PTR nTimerID, DWORD dwTime)
{
	XTIMER_T *pTimerData = g_pTimerList;

	//printf("WinTimerProc\n");

	while (pTimerData)
	{
		if (pTimerData->nTimerID == nTimerID)
		{
			if (pTimerData->pfnTimerFunc)
			{
				// Invoke the call back function.
				(*(pTimerData->pfnTimerFunc))(pTimerData->pDestApp, pTimerData->nSequenceNum);
			} else
			{
				// Create an external timer event and post it .
				XPostThreadExtIntEvent(pTimerData->pDestThread, SME_EVENT_TIMER, pTimerData->nSequenceNum,pTimerData->nTimeOut,
					pTimerData->pDestApp,
					pTimerData->nSequenceNum,
					SME_EVENT_CAT_OTHER
					);
			}
			return;
		}
		pTimerData = pTimerData->pNext;
	}
}
	
#else // WIN32

#define LINUX_BASIC_TIMER_INT   100 // 0.1 sec.

/* Signal Handling in Linux
 If one signal is generated, one signal is delivered, so any single signal will only be delivered to a single thread.
 If it is a synchronous signal, the signal is delivered to the thread that generated it. 

This is normally handled quite happily by using mutex, as follows:

void signal_handler( int sig )
{
        ...
        pthread_mutex_lock( &mutex1 );
        ...
        pthread_mutex_unlock( &mutex1 );
        ...
}

Looks fine at first sight. However, what if the thread that was interrupted by the signal had just itself locked mutex1? 
The signal_handler() function will block, and will wait for the mutex to be unlocked. And the thread that is currently holding 
the mutex will not restart, and so will not be able to release the mutex until the signal handler exits. A nice deadly embrace.

So a common way of handling asynchronous signals in a multi-threaded program is to mask signals in all the threads, and then 
create a separate thread (or threads) whose sole purpose is to catch signals and handle them. The signal-handler thread catches 
signals by calling the function sigwait() with details of the signals it wishes to wait for. 
*/

// The running thread of this function is NOT as same as the running thread of the XSetTimer().
static void LinuxTimerProc(int sig)
{

	XTIMER_T *pTimerData = g_pTimerList;

	if (SIGALRM!=sig)
		return;
	while (pTimerData)
	{
		if (pTimerData->nLeft <= LINUX_BASIC_TIMER_INT)
		{
			// Time out
			// printf("TimeOut\n");
			if (pTimerData->pfnTimerFunc)
			{
				XPostThreadExtIntEvent(pTimerData->pDestThread, SME_EVENT_TIMER, SME_TIMER_CALLBACK, (int)(pTimerData->pfnTimerFunc), 
					pTimerData->pDestApp,
					pTimerData->nSequenceNum,
					SME_EVENT_CAT_OTHER);
			} else
			{
				// Create an external timer event and post it .
				// printf("Post TimeOut event\n");
				XPostThreadExtIntEvent(pTimerData->pDestThread, SME_EVENT_TIMER, SME_TIMER_EVENT,0, 
					pTimerData->pDestApp,
					pTimerData->nSequenceNum,
					SME_EVENT_CAT_OTHER);
			}
			pTimerData->nLeft = pTimerData->nTimeOut; // Restart timer
			return;
		} else
			pTimerData->nLeft -= LINUX_BASIC_TIMER_INT;

		pTimerData = pTimerData->pNext;
	}

	return;
}

static BOOL g_bInitedTimer = FALSE;

static int LinuxInitTimer(unsigned int nBasicTimeOut) //milli-seconds
{
	/* Linux Timer 
	#include <sys/time.h>
	int getitimer(int which, struct itimerval *value);
	int setitimer(int which, const struct itimerval *value,
				  struct itimerval *ovalue); //old value

	The system provides each process with three interval timers, each decrementing in a distinct time domain. 
	When any timer expires, a signal is sent to the process, and the timer (potentially) restarts.

	ITIMER_REAL
		decrements in real time, and delivers SIGALRM upon expiration. 
	ITIMER_VIRTUAL
		decrements only when the process is executing, and delivers SIGVTALRM upon expiration. 
	ITIMER_PROF
		decrements both when the process executes and when the system is executing on behalf of the process. 
		Coupled with ITIMER_VIRTUAL, this timer is usually used to profile the time spent by the application in user and kernel space. 
		SIGPROF is delivered upon expiration.

	Timer values are defined by the following structures:

		struct itimerval {
			struct timeval it_interval; // next value 
			struct timeval it_value;    // current value 
		};
		struct timeval {
			long tv_sec;                // seconds 
			long tv_usec;               // microseconds 
		};

	The function getitimer() fills the structure indicated by value with the current setting for the timer indicated by which 
	(one of ITIMER_REAL, ITIMER_VIRTUAL, or ITIMER_PROF). The element it_value is set to the amount of time remaining on the timer, 
	or zero if the timer is disabled. Similarly, it_interval is set to the reset value. The function setitimer() sets the indicated 
	timer to the value in value. If ovalue is non-zero, the old value of the timer is stored there.

	Timers decrement from it_value to zero, generate a signal, and reset to it_interval. A timer which is set to zero (it_value is 
	zero or the timer expires and it_interval is zero) stops.

	Both tv_sec and tv_usec are significant in determining the duration of a timer.

	Timers will never expire before the requested time, but may expire some (short) time afterwards, which depends on the system timer 
	resolution and on the system load. (But see BUGS below.) Upon expiration, a signal will be generated and the timer reset. 
	If the timer expires while the process is active (always true for ITIMER_VIRTUAL) the signal will be delivered immediately 
	when generated. Otherwise the delivery will be offset by a small time dependent on the system loading. 

	if it_interval is set too big,like 100 sec,real_handl() won't be invoked.because SIGALRM is send out every 100 seconds.
	if it_interval is set too small,like 1000 usec,real_handle() will be invoked too frequently!
	*/
	
	struct itimerval tv;
	int iRet = -1;
	tv.it_interval.tv_sec = (int)(nBasicTimeOut / 1000);
	tv.it_interval.tv_usec = (nBasicTimeOut % 1000) *1000;
	tv.it_value.tv_sec = (int)(nBasicTimeOut / 1000);
	tv.it_value.tv_usec = (nBasicTimeOut % 1000) *1000;
	iRet = setitimer(ITIMER_REAL, &tv, (struct itimerval *)0); 

	if (iRet!=0) return iRet; //Failed.

	struct sigaction siga;
	siga.sa_handler = &LinuxTimerProc;
	siga.sa_flags  = SA_RESTART; //????
	memset (&siga.sa_mask, 0, sizeof(sigset_t));
	iRet = sigaction (SIGALRM, &siga, NULL);

	if (0==iRet)
		g_bInitedTimer = TRUE;

	return iRet; //Failed.
}

#endif

// Initialize timer at the specific thread. The timer signal handler will be called at this calling thread.
int XInitTimer()
{
#ifdef WIN32
	return 0;
#else
	return LinuxInitTimer(LINUX_BASIC_TIMER_INT);
#endif
}

unsigned int XSetTimer(SME_APP_T *pDestApp, unsigned int nTimeOut, SME_TIMER_PROC_T pfnTimerFunc)
{
	XTIMER_T *pTimerData;

#ifdef WIN32
	UINT_PTR nTimerID;

	nTimerID = SetTimer(NULL, 0, nTimeOut, (TIMERPROC)WinTimerProc); 
	if (nTimerID==0)
		return 0;

    pTimerData = (XTIMER_T*)malloc(sizeof(XTIMER_T));
	if (!pTimerData)
		return 0;
	pTimerData->nTimerID = nTimerID;
	pTimerData->nLeft = 0; // For Linux only
		
#else //WIN32
	if (!g_bInitedTimer)
		return 0;

    pTimerData = (XTIMER_T*)malloc(sizeof(XTIMER_T));
	if (!pTimerData)
		return 0;
	pTimerData->nTimerID = g_nTimerSeqNum;
	pTimerData->nLeft = nTimeOut; // For Linux only
#endif

    
	pTimerData->pDestApp = pDestApp;
	pTimerData->pDestThread = XGetThreadContext(); /* The time-out event destination thread is the current calling thread. */
	pTimerData->nTimeOut = nTimeOut;
	pTimerData->nSequenceNum = g_nTimerSeqNum;
	pTimerData->pfnTimerFunc = pfnTimerFunc;
	pTimerData->pNext = g_pTimerList;
	g_pTimerList = pTimerData;

	g_nTimerSeqNum++;

	return pTimerData->nSequenceNum;
}

unsigned int XSetEventTimer(SME_APP_T *pDestApp, unsigned  int nTimeOut)
{
	return XSetTimer(pDestApp,nTimeOut,NULL);
}

BOOL XKillTimer(unsigned int nSequenceNum)
{
	BOOL bRet=FALSE;

	XTIMER_T *pTimerData = g_pTimerList;
	XTIMER_T *pPre = NULL;

	while (pTimerData)
	{
		if (pTimerData->nSequenceNum == nSequenceNum)
		{
#ifdef WIN32
			bRet = KillTimer(NULL, pTimerData->nTimerID);
#endif

			// Remove the timer entity from the timer list.
			if (pTimerData == g_pTimerList)
			{
				g_pTimerList =g_pTimerList->pNext;
			} else
			{
				pPre->pNext = pTimerData->pNext;
			}

			free(pTimerData);
			return bRet;
		}
		pPre = pTimerData;
		pTimerData = pTimerData->pNext;
	}
    return bRet;	
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
Alex "Question is more important than the answer."

Comments and Discussions