QNX 6.4.1 timer utility

I run a timer utility in QNX 6.4.1. The utility will print the duration for which the timer has been configured and the actual duration in which the timer got elapsed. The elapse in timer will be intimated by a signal or a pulse.

For the timer configuration of 100 ms for a single shot timer , the elapse in time is 234 ms in the case of a pulse intimation
For the timer configuration of 200 ms for a periodical timer , the elapse in time is 244,156,287,112 ms in the case of a pulse intimation and signal intimation.

In the range of seconds, it is within acceptable limits i.e 1001 for 1000 ms.

I run the same utility in QNX 6.2.0 and got the elapse time as 101 for 100ms and 199 for 200 ms which is very much acceptable.

I want to know, why there is so much variation in QNX 6.4.1? Is it the way it will respond? Is it a bug in QNX 6.4.1?

Something is wrong in your code, or in the way you measure time. Can you post your code?

code is attached and also the output

//System header files
#include
#include <stdio.h>
#include <errno.h>
#include <pthread.h> //to use thread functions
#include <sys/netmgr.h>
#include <sys/neutrino.h> //to use ‘ChannelCreate’ function
#include <time.h>
#include <unistd.h>
#include <sys/siginfo.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>

#include <sys/syspage.h>
#include <stdint.h>
using namespace std;
//Constants
const int MAX_TIMER = 10;
const int CLOCKS_PER_MS = CLOCKS_PER_SEC / 1000;

//Global variables used across the functions
int nCHNL_ID = NULL; //Channel Id
int nCONN_ID = NULL; //Connection Id
pthread_t nTHRD_ID = NULL; //Thread Id

char cStr[500] = “”; //To store lengthy string
char cStr1[500] = “”; //The string which will be used by Signal handler

timer_t Timers[MAX_TIMER] = { NULL }; //To store the timer ids
int nTimerVal[MAX_TIMER][2] = { { NULL } }; //To store the timer value for all the timers, it stores the one shot as well as interval
double nLastClock_ts[MAX_TIMER] = { 0.0 };
unsigned int nLastClock[MAX_TIMER] = { NULL }; //to store the last clock cycles which will be used to calculate the elapsed time

const unsigned long FULL_RECORD_SIZE = 64 *1024;
const unsigned long SINGLE_RECORD_SIZE = 56;

unsigned long uiTop =0;
unsigned long uiBottom=0;

const double dTwoPower32 = 4.0 * (double)(1<<30);

double dClockSpeed;

void setClockSpeed(double dCSpeed){
if (0.0 < dCSpeed)
{
dClockSpeed = dCSpeed;
}

}

double GetTimeStampInSeconds(void){

asm (“push %eax;”
“push %edx;”
“rdtsc;”
“mov %edx,uiTop;”
“mov %eax,uiBottom;”
“pop %edx;”
“pop %eax;”);

return(((double)uiBottom+(double)uiTop * dTwoPower32)/ dClockSpeed);

}

double GetTimeStampInMilliSeconds(void){

return(GetTimeStampInSeconds() * 1000.0);

}
// *****************************************************************************
// Function : Print
// Description : This function creates a thread which will block on MsgReceive.
// Notes : None
// : NA
// *****************************************************************************
void Print(int TimerNum, char cStr[])
{
char cFileName[30] = “”;
sprintf(cFileName, “Timer_%d_Output”, TimerNum);
int fd = NULL;
int nSizeWritten = NULL;

// open  file for output           
// replace existing file if it exists
// with read/write perms for owner
if ( ( fd = open( cFileName, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR ) ) != 0)
{
	nSizeWritten = write( fd, cStr, strlen(cStr) );
	if ( nSizeWritten != strlen(cStr) )
	{
		printf("\nError on writing to the file %s. Retry!\n", cFileName);
		exit(0);
	}

	close(fd);
}

return;

}

// *****************************************************************************
// Function : CreateConnection
// Description : This function creates channel and attaches this process to the channel so that the
// timer pulses shall be received through this channel.
// Notes : None
// : NA
// *****************************************************************************
void CreateConnection(void)
{
int nChannelId = NULL;

//The ChannelCreate kernel call create a channel that can be used to receive messages and pulses
if ( ( nCHNL_ID = ChannelCreate(NULL) ) < 0 )
{
	printf("\nChannel Create failed. Retry!");
	exit(0);
}

//Treating a connection as a file descriptor can lead to unexpected behavior. Therefore, 
//you should have _NTO_SIDE_CHANNEL in index when you create a connection. If you do 
//this, the connection ID is returned from a different space than file descriptors; 
//the ID is greater than any valid file descriptor
if ( ( nCONN_ID = ConnectAttach(ND_LOCAL_NODE , NULL, nCHNL_ID, _NTO_SIDE_CHANNEL, NULL) ) == -1 )
{
	printf("\nConnectAttach failed. Retry!");
	exit(0);
}	

return;

}

// *****************************************************************************
// Function : Thrd_ReceivePulse
// Description : This is a thread function which will be waiting on the channel to receive the pulse .
// Notes : None
// : NA
// *****************************************************************************
void * Thrd_ReceivePulse(void* ptr)
{
struct _pulse stPulse = { NULL };
unsigned int nElapseTime = NULL;
double nElapseTime_ts = NULL;

//Wait for a message or pulse on a channel
while( MsgReceive(nCHNL_ID, &stPulse, sizeof(stPulse), NULL) == 0 )
{
	
	nElapseTime = clock() - nLastClock[stPulse.code];
	double fFinaltime=GetTimeStampInMilliSeconds();
	nElapseTime_ts=fFinaltime-nLastClock_ts[stPulse.code];
	nLastClock_ts[stPulse.code]=GetTimeStampInMilliSeconds();
	nLastClock[stPulse.code] = clock();
	
	if ( nTimerVal[stPulse.code][1] == 0 )
	{
		sprintf(cStr, "\nConfigured Elapse time: %d,timestamp diff: %f", nTimerVal[stPulse.code][0],nElapseTime_ts);
		Print(stPulse.code + 1, cStr);
		Timers[stPulse.code] = NULL;
	}
	else
	{
		sprintf(cStr, "\nConfigured one shot value: %d, Configured interval value: %d,timestamp diff: %f", \
				nTimerVal[stPulse.code][0], nTimerVal[stPulse.code][1],nElapseTime_ts);
		Print(stPulse.code + 1, cStr);
	}
}

return NULL;

}

// *****************************************************************************
// Function : CreateRxThread
// Description : This function creates a thread which will block on MsgReceive.
// Notes : None
// : NA
// *****************************************************************************
void CreateRxThread(void)
{
if ( pthread_create(&nTHRD_ID, NULL, &Thrd_ReceivePulse, NULL) != EOK )
{
printf("\nThread Create failed. Retry!");
exit(0);
}

return;

}

// *****************************************************************************
// Function : IsTimerRunning
// Description : This function is used to find out whether the mentioned timer is running or not.
// Notes : None
// : NA
// *****************************************************************************
unsigned char IsTimerRunning(int nTimerId)
{
if ( nTimerId != 0 )
{
struct itimerspec timer = { { NULL } };
(void)timer_gettime(nTimerId, &timer);

	return(! ((timer.it_value.tv_sec  == 0)  &&
         		(timer.it_value.tv_nsec == 0)));
}
return false;

}

// *****************************************************************************
// Function : StopTimer
// Description : This function stops the timer
// Notes : None
// : NA
// *****************************************************************************
void StopTimer(int nTimerId)
{
if ( nTimerId != 0 )
{
struct itimerspec timer; // values of 0 stops the timer
timer.it_value.tv_sec = NULL;
timer.it_value.tv_nsec = NULL;
timer.it_interval.tv_sec = NULL;
timer.it_interval.tv_nsec = NULL;

	// stop the timer
	if ( timer_settime(nTimerId, NULL, &timer, NULL) < 0 )
	{
		printf("\n\nTimer Stop failed. Retry!\n");
		exit(0);
	}
}
return;

}

// *****************************************************************************
// Function : DeleteTimers
// Description : This function deletes all the timers which are currently running.
// Notes : None
// : NA
// *****************************************************************************
void DeleteTimers(int nSigNumber)
{
for(int i = NULL; i < MAX_TIMER; ++i)
{
if ( Timers[i] != 0 )
{
StopTimer(Timers[i]);
if ( timer_delete(Timers[i]) != 0 )
{
printf("\nTimer Delete Failed. Retry!\n");
exit(0);
}
}
}
printf("\nExited Normally\n");
exit(0);
}

// *****************************************************************************
// Function : HandleSignal
// Description : This function handles the signals raised by the timers.
// Notes : None
// : NA
// *****************************************************************************
void HandleSignal(int nTimerNum)
{
nTimerNum = ( nTimerNum == SIGUSR1 ) ? 2 : ( ( nTimerNum == SIGUSR2 ) ? 9 : nTimerNum);

int nElapseTime = clock() - nLastClock[nTimerNum - 1];
double fFinaltime=GetTimeStampInMilliSeconds();

double nElapseTime_ts=fFinaltime-nLastClock_ts[nTimerNum -1];
nLastClock_ts[nTimerNum -1]=GetTimeStampInMilliSeconds();
nLastClock[nTimerNum - 1] = clock();

if ( nTimerVal[nTimerNum - 1][1] == 0 )
{
	sprintf(cStr1, "\nConfigured Elapse time: %d, timestamp difference: %f", nTimerVal[nTimerNum -1][0], nElapseTime_ts);
	Print(nTimerNum, cStr1);
	Timers[nTimerNum - 1] = NULL;
}
else
{
	sprintf(cStr1, "\nConfigured one shot value: %d, timestamp difference: %f",\
			nTimerVal[nTimerNum -1][1],nElapseTime_ts)  ;
	Print(nTimerNum, cStr1);
}
return;

}

// *****************************************************************************
// Function : main
// Description : This is the main funtion.
// Notes : None
// : NA
// *****************************************************************************
int main(void)
{
(void)signal(SIGINT, DeleteTimers); //Configure CTRL + C;

(void)signal(SIGHUP,  HandleSignal); // SIGHUP = 1	
(void)signal(SIGQUIT, HandleSignal);	// SIGQUIT = 3	
(void)signal(SIGILL,  HandleSignal);   	// SIGILL = 4  	 
(void)signal(SIGTRAP, HandleSignal);   // SIGTRAP = 5 	  
(void)signal(SIGABRT, HandleSignal);   // SIGABRT = 6	
(void)signal(SIGEMT,  HandleSignal);   // SIGEMT = 7  	
(void)signal(SIGFPE,  HandleSignal);   // SIGFPE = 8  	
(void)signal(SIGBUS,  HandleSignal);   // SIGBUS = 10 
(void)signal(SIGUSR1,  HandleSignal);   // SIGUSR1 = 16
(void)signal(SIGUSR2,  HandleSignal);   // SIGUSR2 = 17

int nContinue = NULL;	//to hold the value whether the user wants to try one more time or not
int nTimerType = NULL;	//to hold the the value for timer type
char nLoopInd = NULL;		//to count the number of timers created
struct sigevent stTimerCreate = { NULL };	//Structure to be filled for creating a timer
struct itimerspec stTimerSettime = { { NULL} };	//Structure to be filled to start a timer
int nNotifyType[MAX_TIMER] = { NULL };	//to hold the value for the notification type

uint64_t un64CpuSpeed = SYSPAGE_ENTRY(qtime) ->cycles_per_sec;
setClockSpeed((double)un64CpuSpeed);
CreateConnection();
CreateRxThread();

do
{		
	do
	{
		printf("\nEnter the timer type: ");
		printf("\n1. Single shot timer");
		printf("\n2. Periodic timer");
		/*printf("\n\nYour choice: ");
		scanf("%d", &nTimerType);*/
		cout<<"\n\nYour choice:";
		cin >> nTimerType;
	}while( ( nTimerType < 1 ) || ( nTimerType > 2 ) );

	if ( nTimerType == 1 )
	{
		do
		{
			/*printf("\nEnter the timer value in milliseconds: ");
			scanf("%d", &nTimerVal[nLoopInd][0]);*/
			cout << "\nEnter the timer value in milliseconds:";
			cin >> nTimerVal[nLoopInd][0]; 
		}while(nTimerVal[nLoopInd][0] < 0);

		nTimerVal[nLoopInd][1] = NULL;
	}
	else
	{
		do
		{
			/*printf("\nEnter the one shot value in milliseconds: ");
			scanf("%d", &nTimerVal[nLoopInd][0]);*/
		cout<<"\nEnter the one shot value in milliseconds: ";
			cin>>nTimerVal[nLoopInd][0];
		}while(nTimerVal[nLoopInd][0] < 0);

		do
		{
			/*printf("\nEnter the interval value in milliseconds: ");
			scanf("%d", &nTimerVal[nLoopInd][1]);*/
			cout<<"\nEnter the interval value in milliseconds: ";
			cin>>nTimerVal[nLoopInd][1];
		}while(nTimerVal[nLoopInd][1] < 0);
	}

	do
	{
		printf("\nChoose the notification scheme you want to test: ");
		printf("\n1. Send a Pulse");
		printf("\n2. Send a Signal");
		/*printf("\n\nYour choice: ");
		scanf("%d", &nNotifyType[nLoopInd]);*/
		cout<<"\n\nYour choice: ";
		cin>>nNotifyType[nLoopInd];
	}while( ( nNotifyType[nLoopInd] < 1 ) || ( nNotifyType[nLoopInd] > 3 ) );

	sprintf(cStr, "\nTimer: %d, Timer value (msec): %d, Timer type: %s, Notification scheme: %s\n", 
		nLoopInd + 1, nTimerVal[nLoopInd][0], ( nTimerType == 1 ) ? "Single Shot" : "Periodic", 
		( nNotifyType[nLoopInd] == 1 ) ? "Pulse" : ( ( nNotifyType[nLoopInd] == 2 ) ? "Signal" : "Thread" ) );
	Print(nLoopInd + 1, cStr);
	
	nLoopInd++;

/*	printf("\nDo you want to create one more timer? (Yes -1, No -0): ");
	scanf("%d", &nContinue);*/
	cout<<"\nDo you want to create one more timer? (Yes -1, No -0): ";
	cin>>nContinue;
}while( ( nContinue == 1 ) && ( nLoopInd < MAX_TIMER ) );

if ( ( nLoopInd == MAX_TIMER ) && (nContinue == 1 ) )
{
	printf("\n No more timers can be created.");
}

for(int i = NULL; i < nLoopInd; ++i)
{
	switch( nNotifyType[i] )
	{
		case 1:	//Pulse notification
		{
			//Initialize the sigevent structure for pulse notification				
			stTimerCreate.sigev_notify = SIGEV_PULSE;
			stTimerCreate.sigev_coid   = nCONN_ID;
			stTimerCreate.sigev_value.sival_int = NULL;
			nLastClock[i] = clock();
			nLastClock_ts[i]=GetTimeStampInMilliSeconds();
			stTimerCreate.sigev_code = i;
			stTimerCreate.sigev_priority = getprio(NULL);
			
			//Create the timer
			if ( timer_create(CLOCK_REALTIME, &stTimerCreate, &Timers[i]) == -1 )
			{
				printf("\nError on creating timer. Retry!");
				exit(0);
			}

			//Fill the structure to set the timer value
			stTimerSettime.it_value.tv_sec = (long) nTimerVal[i][0] / 1000;	//One shot value
			stTimerSettime.it_value.tv_nsec = (long) ( ( nTimerVal[i][0] % 1000 ) * 1000  * 1000);

			stTimerSettime.it_interval.tv_sec = (long) nTimerVal[i][1] / 1000;	//Interval value
			stTimerSettime.it_interval.tv_nsec = (long) ( ( nTimerVal[i][1] % 1000 ) * 1000 * 1000);

		
			//Start the timer, this timer is a relative timer
			timer_settime(Timers[i], NULL, &stTimerSettime, NULL);				
		}
		break;
		
		case 2:	//Signal
		{
			//Initialize the sigevent structure for pulse notification
			nLastClock[i] = clock();
			nLastClock_ts[i]=GetTimeStampInMilliSeconds();
			stTimerCreate.sigev_signo = ( i == 1 ) ? SIGUSR1 :
				( ( i == 8 ) ? SIGUSR2 : i + 1 ); 
			stTimerCreate.sigev_notify = SIGEV_SIGNAL;
			
			//Create the timer
			if ( timer_create(CLOCK_REALTIME, &stTimerCreate, &Timers[i]) == -1 )
			{
				printf("\nError on creating timer. Retry!");
				exit(0);
			}

			//Fill the structure to set the timer value
			stTimerSettime.it_value.tv_sec = (long) nTimerVal[i][0] / 1000;	//One shot value
			stTimerSettime.it_value.tv_nsec = (long) ( ( nTimerVal[i][0] % 1000 ) * 1000  * 1000);

			stTimerSettime.it_interval.tv_sec = (long) nTimerVal[i][1] / 1000;	//Interval value
			stTimerSettime.it_interval.tv_nsec = (long) ( ( nTimerVal[i][1] % 1000 ) * 1000 * 1000);

		
			//Start the timer, this timer is a relative timer
			timer_settime(Timers[i], NULL, &stTimerSettime, NULL);				
		}
		break;
		
		default:
		{
			printf("Code never reaches here");
		}
		break;
	}			
}

printf("\nPress CTRL+C to terminate the program...\n");
while(1);

return NULL;

}

Timer: 3, Timer value (msec): 200, Timer type: Periodic, Notification scheme: Pulse

Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 88,clock time 100984,timestamp diff 268.518675
Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 41,clock time 142978,timestamp diff 245.667143
Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 11,clock time 154976,timestamp diff 155.645749
Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 165,clock time 320951,timestamp diff 198.393205
Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 13,clock time 334948,timestamp diff 647.216988
Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 27,clock time 362944,timestamp diff 28.525873
Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 20,clock time 383941,timestamp diff 196.625392
Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 0,clock time 383941,timestamp diff 74.132304
Configured one shot value: 200, Configured interval value: 200, Actual Elapsed time: 83,clock time 467928,timestamp diff 599.769003

Works fine for me, even at 10ms.

The main thread is doing a busy loop. On a single process machine that can affect the ability of the timer thread to get the pulse properly I’m using a quad core machine, if I force the program to run on the same core, I get lots of jitters. Put a delay(1000) in the while loop or don’t even use a thread.

Second you are using unbuffered file operation which for small writes create lots of overhead. If for some reason the device you write too can’t keep up it make affect the amount of time the operation takes.

Instead of assembly code check out ClockCycles(). Since you use C++ you might want to check the Poco C++ library which has nice Timer classes/threads capability.

But the same code works well in QNX 6.2.0 in a Pentium III machine.That is why I had a confusion.

Even If I print the output on the screen rather than a file, I get same errors. ClockCycles() also gives same problem.

Timer handling using signals also poses the same problem. Sorry for so many replies.