1

Good day everyone !

I am trying to get a 2nd thread (the serial thread) to run as near real time as possible.

Within my 2nd spawned serial thread I select() with timeout of 3 mS on a serial port.

I also get real time before select() ... and then after to get select() delta time.

Problem is that sometimes I get no indication of select returning 0 (what I call a timeout due to the 3 mS elapsing) ... but I do occasionally get a total time much greater than the 3 mS (4.447 for example).

I have to conclude that the serial thread is getting preempted?

Any ideas?

What Linux commands can I use to see if the thread is preempted?

Thanks!

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>



//-----------------------------------
// signal Handler stuff.
//-----------------------------------
static
struct  sigaction mySigActTerm;

volatile
int     myTerminate = 0;

void terminateHandler(int signum, siginfo_t *info, void *ptr)
{
  // set a flag here and get out.
  myTerminate = 1;
}



void getNowTime(char* str)
{
  time_t    rawtime;
  time(&rawtime);
  ctime_r(&rawtime, str);

  // clobber the unwanted newline.
  str[24] = '\0';
}



void myResLimit()
{
  struct
  rlimit    procLimit;

  char      strNowTime[26];

  getrlimit(RLIMIT_RTTIME, &procLimit);
  getNowTime(strNowTime);
  fprintf(stderr, "%s - RLIMIT_RTTIME: soft=%lld, hard=%lld\n", strNowTime, (long long) procLimit.rlim_cur, (long long)procLimit.rlim_max);

  getrlimit(RLIMIT_RTPRIO, &procLimit);
  getNowTime(strNowTime);
  fprintf(stderr, "%s - RLIMIT_RTPRIO: soft=%lld, hard=%lld\n", strNowTime, (long long) procLimit.rlim_cur, (long long) procLimit.rlim_max);

  getrlimit(RLIMIT_CPU, &procLimit);
  getNowTime(strNowTime);
  fprintf(stderr, "%s - RLIMIT_CPU: soft=%lld, hard=%lld\n", strNowTime, (long long) procLimit.rlim_cur, (long long) procLimit.rlim_max);
}



void*   serialThread(void* arg)
{
    int     sfd;    // serial file descriptor.

    char    myChar;

    int     rtn;

    fd_set  myfds;

    struct
    timeval tm_out,
            start,
            end,
            delta;

    struct
    termios oldtio,
            newtio;

    sfd = open("/dev/ttyUSB0", O_RDWR | O_NOCTTY);

    tcgetattr(sfd, &oldtio);

    newtio = oldtio;

    cfmakeraw(&newtio);

    newtio.c_cflag |= CREAD;
    newtio.c_cflag |= CLOCAL;
    newtio.c_cflag &= ~CSTOPB;
    newtio.c_cflag &= ~CRTSCTS;
    newtio.c_cflag &= ~CSIZE;
    newtio.c_cflag |= CS7;
    newtio.c_cflag |= PARENB;
    newtio.c_cflag &= ~PARODD;

    newtio.c_cc[VTIME] = 1;
    newtio.c_cc[VMIN] = 1;

    tcflush(sfd, TCIFLUSH);


    while (1) {
        FD_ZERO(&myfds);
        FD_SET(sfd, &myfds);

        tm_out.tv_sec = 0;
        tm_out.tv_usec = 3000;

        // get sys call start time.
        gettimeofday(&start, NULL);

        rtn = select(sfd+1 ,&myfds, NULL, NULL, &tm_out);

        // get sys call end time.
        gettimeofday(&end, NULL);

        timersub(&end, &start, &delta);

        if (rtn == 0) {
            fprintf(stderr, "tm_out = %02d.%06d    delta = %02d.%06d\n", tm_out.tv_sec, tm_out.tv_usec, delta.tv_sec, delta.tv_usec);
        }
        else
          read(sfd, &myChar, 1);
    }
}



//-----------------------------------
// the one and only MAIN.
//-----------------------------------
int main()
{
  //-----------------------------------------------
  // locals.
  int               rtn;

  char              strNowTime[26];

  pthread_t         serialThdID;

  pthread_attr_t    serialAttr;

  struct
  sched_param       serialParam;


  //-----------------------------------------------
  // Log OS resource limits.
  myResLimit();

  //-----------------------------------------------
  // initialize the signals struct.
  // ... and setup signals.
  memset(&mySigActTerm, 0, sizeof(mySigActTerm));
  mySigActTerm.sa_sigaction = terminateHandler;
  mySigActTerm.sa_flags = SA_SIGINFO;

  sigaction(SIGTERM, &mySigActTerm, NULL);


  //-----------------------------------------------
  // set initial default pthread attr values.
  if ((rtn = pthread_attr_init(&serialAttr)) != 0) {
    getNowTime(strNowTime);
    fprintf(stderr, "%s - main() - pthread_attr_init()\n%s\n", strNowTime, strerror(rtn));
    exit(EXIT_FAILURE);
  }

  //-----------------------------------------------
  // set for best near real time policy.
  if ((rtn = pthread_attr_setschedpolicy(&serialAttr, SCHED_FIFO)) !=0) {
    getNowTime(strNowTime);
    fprintf(stderr, "%s - main() - pthread_attr_setschedpolicy()\n%s\n", strNowTime, strerror(rtn));
    exit(EXIT_FAILURE);
  }

  //-----------------------------------------------
  // set to explicit inherit or attr obj will be ignored.
  if ((rtn = pthread_attr_setinheritsched(&serialAttr, PTHREAD_EXPLICIT_SCHED)) !=0) {
    getNowTime(strNowTime);
    fprintf(stderr, "%s - main() - pthread_attr_setinheritsched()\n%s\n", strNowTime, strerror(rtn));
    exit(EXIT_FAILURE);
  }

  //-----------------------------------------------
  // set to un-limited thread priority.
  serialParam.sched_priority = 0;
  if ((rtn = pthread_attr_setschedparam(&serialAttr, &serialParam)) !=0) {
    getNowTime(strNowTime);
    fprintf(stderr, "%s - main() - pthread_attr_setschedparam()\n%s\n", strNowTime, strerror(rtn));
    exit(EXIT_FAILURE);
  }

  //-----------------------------------------------
  // start the new thread.
  if ((rtn = pthread_create(&serialThdID, &serialAttr, serialThread, NULL)) == 0) {
    getNowTime(strNowTime);
    fprintf(stderr, "%s - starting serial thread.\n", strNowTime);
  }
  else {
    getNowTime(strNowTime);
    fprintf(stderr, "%s - main() - pthread_create() returned %d\n%s\n", strNowTime, rtn, strerror(rtn));
    exit(EXIT_FAILURE);
  }

  //-----------------------------------------------
  // no need to keep this junk if pthread_create() succeeded.
  if ((rtn = pthread_attr_destroy(&serialAttr)) != 0) {
    getNowTime(strNowTime);
    fprintf(stderr, "%s - main() - pthread_attr_destroy()\n%s\n", strNowTime, strerror(rtn));
  }




  while (myTerminate == 0) {

  }
}

  • Most of the code presented is unlikely to have much to do with the problem, whereas some of the code omitted is highly relevant. As we usually do, we really need to see a [mre]. – John Bollinger Feb 03 '21 at 00:51
  • That may take me a while to condense down. – keith bradley Feb 04 '21 at 02:50
  • I have added a few lines to illustrate my point. the code is not tested but I don't think it needs to be as my issue is really a question of how the delta can be more than 3000 uS when another device is spitting out one char well within 3000 uS? – keith bradley Feb 04 '21 at 03:42
  • Made additional corrections and the code compiles OK with four warnings. There is no way to reproduce w/o something to send a char every 1000 uS. I just need some direction on how to find out why the time does not add up. – keith bradley Feb 04 '21 at 04:18

1 Answers1

0

Well no one seems to know why this is happening to me but ... ... after nearly a year struggling ... ... I found a post that is exactly what is happening to me.

I post a link so no one else will suffer my fate.

C - select() seems to block for longer than timeout

  • A year? I am surprised that you did not start reducing the timeout a little bit at a time until you got the result you wanted. – Zan Lynx Feb 09 '21 at 20:45
  • That's not it ... the time out was 3mS (3000 uS). select() occasionally ran past that and the link answers why. – keith bradley Feb 10 '21 at 18:44