Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
FYI: Example Capp for xenomai FIFO multithreading...
#1
I haven't prettied up this code yet, but I thought I'd post it as it could be real useful for someone doing general purpose Xenomai programming that just needs to create a bunch of periodic threads. I found that there weren't alot of Xenomai example programs that were simple and used the POSIX skin.

This code gives the following:

- C routine for getting time in seconds (same result as in script languages)
- "Sendf()" ... DT's "Send()" meets "Printf()"
- CreateThread() routine that deals with the nasty POSIX calls for you

Any feedback is welcome as I would like to improve my code if there are mistakes.

To use this code copy it into your "Background Programs" section of the IDE and select "Build and download". Once the code is executed you should see some output in the unsolicited messages window such as this:

Port 1: Told to run 103 loops at 1ms periods, Elapsed time: 0.1030 seconds
Port 1: Told to run 201 loops at 1ms periods, Elapsed time: 0.2010 seconds

Ultimately I will be stripping this code into its own "C" file and only exposing the few routines in a header file that will get used in a machine program. Right now it is ugly all in one file but I thought it would be simple to post to the forum this way.

***** Start of file ****
//--------------------------------------------------------------------------------
#include <gplib.h>
#include <RtGpShm.h> // Global Rt/Gp Shared memory pointers

//-------------------------------------------------------------
// The following is a projpp created file from the User defines
//-------------------------------------------------------------
#include "../../Include/pp_proj.h"
#define SEND_MAX 128

//Define some global variables...
static const int ThreadRetOK = 1;
static const int ThreadRetFail = -1;

//Global pointer to shared memory which is useful for Delta Tau
//data structure retrieval.
SHM *pSHM;

//This structure is used to pass this information to our thread shell routine.
struct thread_parameter
{
void (*func)(void *);
void * arg;
const char * name;
};

//Returns current time in 64 bit floating point 'seconds'
//Ths is useful for general purpose timing..
double GetTimeSec(void)
{
return 1e-6*pSHM->ClockSF*u64btod(fclock());
}

//Formats a message to send to the Send port, up to 'SEND_MAX' chars...
void Sendf(int SendPort, const char *fmt, ...)
{
char msg_buf[SEND_MAX];
va_list args;

/* format string */
va_start(args, fmt);
vsnprintf(msg_buf, SEND_MAX, fmt,args);
va_end(args);

Send(SendPort, msg_buf);
}

//This function is passed to pthread_create(). It is a wrapper around the
//user called function. This wrapper is important so that normal users need
//not be concerned with the POSIX services, etc.
//Since pthread_create() expects ths function prototype we had to abide
//by it. The argument passed to it is in fact a structure pointer
//so we are able to use that to pass this routine alot of information such as
//the user function name and arguments, as well as the thread name so we can
//make more meaningful error messages.

void * thread_shell(void * arg)
{
struct timespec start;
struct timespec period;
int retval=0;
struct thread_parameter *param;

param = (struct thread_parameter *)arg;


//Set up some time parameters needed to make thread periodic...
start.tv_sec = 0;
start.tv_nsec = 0;
period.tv_sec = 0;
period.tv_nsec = 1000000; //Wake thread every ms

//Now that thread is created we want to make it a periodic task
//so that it wakes up every period if it is suspended.
if(retval = pthread_make_periodic_np (pthread_self(), &start, &period))
Sendf(1, "Failed to make thread '%s' periodic. ret: %d, ESRCH: %d, ETIMEDOUT: %d\n", param->name, retval, ESRCH, ETIMEDOUT);

(*param->func)(param->arg); //execute the real thread function now that we have gotten it ready...

return ((void *) &ThreadRetOK);
}

int CreateThread(pthread_t *ThreadID, const char* name, void (*ThreadFunction)(void *), void * arg)
{
struct thread_parameter param;
int retval=0;
pthread_attr_t attr;
size_t stackdefault;

//Copy parameters in so we can pass it to thread shell function...
param.func = ThreadFunction;
param.arg = arg;
param.name = name;

//Set attributes of thread, such as stacksize and scheduler type
pthread_attr_init(&attr);

//Set Scheduler policy
retval = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if(retval != 0)
Sendf(1, "pthread_attr_setschedpolicy()' for thread: %s. Returned: %d\n", name, retval);

//Do the actual thread creation...
retval = pthread_create(ThreadID, &attr, thread_shell, (void *)&param);
if(retval != 0)
Sendf(1, "Failed 'pthread_create()' for thread: %s. Returned: %d\n", name, retval);

//Give the thread a name so that we can identify it...
retval = pthread_set_name_np (*ThreadID, name);
if(retval != 0)
Sendf(1, "Failed 'pthread_set_name_np()' for thread:%s. Returned: %d", name, retval);

//return the threadID so that calling routine can reference it
return retval;
}

void InitMain(void)
{
/* Avoids memory swapping for this program */
mlockall(MCL_CURRENT|MCL_FUTURE);

InitLibrary(); //Init DT library.
pSHM = GetSharedMemPtr(); //Initializing the pointer to the shared memory
}

void CloseMain(void)
{
CloseLibrary(); //Close DT library
}

//Example test thread
void ThreadCode(void * arg)
{
double StartMeasure;
int i;
int retval;
int NumLoops = (int)arg;

StartMeasure = GetTimeSec();
for(i=0; i<NumLoops; i++)
{
pthread_wait_np(NULL);
}

Sendf(1, "Told to run %d loops at 1ms periods, Elapsed time: %0.4f seconds", NumLoops, (GetTimeSec() - StartMeasure));
}

//Example test program...
int main(void)
{
//Thread ID handles used for POSIX routines...
pthread_t thread1, thread2;

InitMain();

CreateThread(&thread1, "Thread1", ThreadCode, (void *)201);
CreateThread(&thread2, "Thread2", ThreadCode, (void *)103);

pthread_join(thread1, NULL);
pthread_join(thread2, NULL);

CloseMain();

return 0;
}



Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)