On Sun, 7 Mar 1999, Stephane Pierre Bordas wrote:

> I am confronted to persons that think that RTL is not reliable because it
> is a freeware and thus would prefer to use Windows for applications
> needing calculations of robot arm trajectories requiring a data treatment
> as fast as one millisecond (at least 10 milliseconds). 
> 
> I don't think this would be a good idea to use Windows with such small
> durations, but I am facing the reluctancy of my partners and would like to
> know your advice on that.

Unlike many of the very smart people on this list who have always used
RT-Linux, or another RTOS, I started my project as your peers suggest,
with a Redmond OS known as NT.

This was a bad idea.

>From this experience I can say NT won't work. But, hopefully I can do a
little better and offer some code tidbits so that you can write some NT
code to show that it can not work.

First you need access to NT performance counters - the same can be
done in RT-Linux using the rdtsc() call of the kernel as suggested by
Mantegazza to examine jitter (Note: you will need to replace the DEBUG()
macro with printf() or whatever - this is what's in GError.h):

GPerfCounter.h
/*
/* Quick & Dirty Performance Counter Routines for Windows NT
/*
/*      QP_START        - starting point for timing
/*      QP_END          - ending point for timing
/*      QP_SECONDS      - returns a double giving duration in seconds
/*
/* Macros set QPCStartMark() and QPCEndMark() in the code and
/* QPCDurationInSeconds returns the time between the end
/* and start marks in seconds.
*/

// GPerfCounter.h
#ifndef G_PERFCOUNTER_HEADER
#define G_PERFCOUNTER_HEADER

#ifdef NT_PERFORMANCE_TIMING
BOOL QPCStartMark();
BOOL QPCEndMark();
double QPCDurationInSeconds();

#define QP_START                QPCStartMark()
#define QP_STOP                 QPCEndMark()
#define QP_SECONDS              QPCDurationInSeconds()

#else

#define QP_START
#define QP_STOP
#define QP_SECONDS              -1.0

#endif
/* End Query Performace Counter Routines
*/

#endif
// GPerfCounter.h



GPerCounter.cpp:
/*
/* Quick & Dirty Performance Counter Routines for Windows NT
/*
/*      QP_START        - starting point for timing
/*      QP_END          - ending point for timing
/*      QP_SECONDS      - returns a double giving duration in seconds
/*
/* Macros set QPCStartMark() and QPCEndMark() in the code and
/* QPCDurationInSeconds returns the time between the end
/* and start marks in seconds.
*/
#include "GError.h"

static  _int64  liQPCounter1 = 0;
static  _int64  liQPCounter2 = 0;
BOOL QPCStartMark()
{
        return QueryPerformanceCounter( (LARGE_INTEGER *)&liQPCounter1 );
        
}
BOOL QPCEndMark()
{
        return QueryPerformanceCounter( (LARGE_INTEGER *)&liQPCounter2 );
}
double QPCDurationInSeconds()
{
        /*Variables for the NT Performance Counter system*/
        static  double  dSecondsPerCount = 0.0;
        _int64  liQPFrequency = 0;
        _int64  liQPCountDifference = 0;

        if ( 0.0 == dSecondsPerCount ) {  /*frequency not yet set*/

                /*Get the frequency of the performance counter
                (counts/sec)*/
                if ( 0 == QueryPerformanceFrequency( \ 
                                (LARGE_INTEGER*)&liQPFrequency) ) {
                        /*No performance counter is available*/
                        return -1.0;
                }
                DEBUG(10, ("QueryPerformanceFrequency returns %I64d counts/second\n", 
liQPFrequency));          
                dSecondsPerCount = ( 1.0/(double )liQPFrequency );
        }

        liQPCountDifference = liQPCounter2 - liQPCounter1;
        return liQPCountDifference * dSecondsPerCount;
}
/* End Query Performace Counter Routines
*/

If you imagine your RT task running in a service, as I did, so that it is
not subject to the tampering of the user, then you will need some other
routines as well. In any case, you will have a high priority loop
servicing the task, perhaps something like:

        //Setup the Thread...

        //Make this service the most important thing in user-space;
        //this will not prevent ISRs, kernel level DPCs and whatnot from
        //blowing this thread away. GUI routines may invert the priority
        //scheme and make a mess (minimize/maximize a window).
        //NT is _not_ a Real Time Operating System.
        if ( !SetPriority( REALTIME_PRIORITY_CLASS, 
                THREAD_PRIORITY_TIME_CRITICAL) )
                return DAQSVC_ERR_PRIORITY_NOT_SET;

        /* the main service loop will run until the RunServiceMutex is
        released */
        do {
        
                //do what you need to ...               

                //This is a REALTIME priority thread so give some CPU time
                //to other threads; this loop will run up CPU time to 100%
                //under the NT Task Manager (shift-ctrl-esc).
                YieldProcessorTime(0);

        } 
        /* Repeat while RunServiceMutex is still taken. */
        while ( (WAIT_TIMEOUT == dwRunMutex) && (bExecuteLoop) );
        
This provides for a "polling mode" type of task, of course, NT has a rich
object set, so you can wait on a variety of other object types instead.
For example, you could wait for input from some client task via a shared
mutex, or whatever. Such a wait will not make a difference as far as the
task timing is concerned, ISR/DPCs in the NT kernel will still insert 
lags in your regularly scheduled task (try minimizing then maximizing
windows, it's amazing how long this will delay a top priority task;
recall that under NT 4.0 the display is integrated into the kernel). 

These are some routines to help you with priorities and sleeping:

///////////////////////////////////////////////////////////////////////////////
//
// BOOL SetPriority( DWORD dwPriorityClass, int nPriority )
//
// Purpose:
//      Sets the priority and priority class of the current process and 
//      thread.
//
// Parameters:
//      dwPriorityClass         -The process class
//      nPriority               -The priority within the given
//                              process class
//
// Returns:
//      TRUE on success, FALSE otherwise.
// 
BOOL SetPriority( DWORD dwPriorityClass, int nPriority )
{
        DWORD   dwReturnedPriorityClass = 0;
        int             nReturnedPriority = 0;

        //Run this process with the specified priority class
    if (FALSE == SetPriorityClass(GetCurrentProcess(), \
                dwPriorityClass)) {
                //Could not set the priority class
                return FALSE;
        }

        //Double check that the was actually given; it is possible
        //that SetPriorityClass might simply set the highest priority it
        //can, but fail to provide the requested (e.g. REALTIME) priority.
    dwReturnedPriorityClass = GetPriorityClass(GetCurrentProcess());
        if (dwReturnedPriorityClass != dwPriorityClass) {
                //Could not set the specified priority class
                return FALSE;
        }

        //Set the thread priority within the specified process class
        return SetThreadPriority(GetCurrentThread(), nPriority);        
}

///////////////////////////////////////////////////////////////////////////////
//
// void YieldProcessorTime( DWORD dwMilliseconds )
//
// Purpose:
//      Surrender time slice to other ready threads. Calls SwitchToThread
//      if nSeconds equals 0 or Sleep( nTime ).
//
// Parameters:
//      nTime           -Time to sleep in milliseconds.
//
// Returns:
//      void.
//
// Comments:
//      Sleep is not guaranteed to return after nTime milliseconds, the
//      time to return may be longer.
//
void YieldProcessorTime( DWORD dwMilliseconds )
{
        QP_START;
        if ( 0 < dwMilliseconds ) Sleep(dwMilliseconds);        
                                                //May sleep longer!
        else if ( 0 == dwMilliseconds ) SwitchToThread();       
                                                //On the current CPU only
        QP_STOP;
        DEBUG(10, ("YieldProcessorTime: %f\n", QP_SECONDS));
}



NT is really a pretty nice general purpose OS, it just isn't suited to
hard real-time work. When you look at all the advantages of Linux, and
throw RT into the mix, technically, you really don't have a choice.

Good luck,
-Don

--- [rtl] ---
To unsubscribe:
echo "unsubscribe rtl" | mail [EMAIL PROTECTED] OR
echo "unsubscribe rtl <Your_email>" | mail [EMAIL PROTECTED]
----
For more information on Real-Time Linux see:
http://www.rtlinux.org/~rtlinux/

Reply via email to