0

I am writing a program to send pulse outputs at every second, according to my system clock. It's important that this is as accurate as I can make it.

I am using XCode as my debugger at the moment before I independently run this on a different system.

I am using a detached pthread which sends the output via a (not written by me) function.

I know about the sleep(int seconds) call, however this is not synced on the clock, and I'm not too sure about the accuracy...

I searched around for a better solution, but have only come across C's time.h tm struct. Is there a way to block my thread until the second on a tm changes, or any other better solutions?

Here is my thread function for reference.

/*One PPS*/
void *_THD_One_PPS(void *args) {
    /* Description: Send a 1 millisecond pulse every second */
    selfargs_t *self = args;
    pthread_detach(pthread_self());

    double dutyCycle = 0.001;

    long unsigned onTime = dutyCycle * 1000000000;      // Time signal high, ns

    while (self->myself->ThreadActiveStatus != THD_DONE) {
        sleep(1);   //TODO: Sync with clock as much as possible
        printf("/\\ ");
        setPinValue(BBB_GPIO_PIN_ONE_PPS, ON);
        pauseNanoSec(onTime);
        setPinValue(BBB_GPIO_PIN_ONE_PPS, OFF);
    }
    printf("Pulse Per Sec DIED");
    _T_ThreadsDied++;
    _T_ThreadsAlive--;
    self->myself->ThreadActiveStatus = THD_DONE;
    free(self->arguments);
    free(self);
    return NULL;
}

Is there an easy or better way to do this?

dovedevic
  • 673
  • 2
  • 14
  • 33
  • Is it really that the signals' correspondence with the system clock is important, or would it be sufficient for the *interval* between signals to be accurate and consistent? – John Bollinger Jan 04 '17 at 21:23
  • "however this causes a runtime error". What error exactly? – kaylum Jan 04 '17 at 21:24
  • @JohnBollinger I would say more so the latter that former, but the former would be a great addition if at all possible *with* the former. – dovedevic Jan 04 '17 at 21:25
  • @kaylum, I am using XCode at the moment for debugging, and it informs me only "Thread 5: signal SIGKILL" on the line of the `sleep(1);`. I'll edit the question to clarify. – dovedevic Jan 04 '17 at 21:27
  • 2
    That's not an error. It's expected behaviour. Any signal will interrupt a `sleep`. You won't even see it if you had not been attached with the debugger (except the `sleep` will terminate early). So I'm not sure that really is a problem that would require looking for another solution. The question is, what alternate behaviour do you require upon SIGKILL? – kaylum Jan 04 '17 at 21:29
  • 2
    See [Are there any well-behaved POSIX interval timers?](http://stackoverflow.com/questions/11338899/are-there-any-well-behaved-posix-interval-timers). – John Bollinger Jan 04 '17 at 21:31
  • @kaylum, Oh..., I didn't think that XCode would then throw something like that, but I have just confirmed your comment and edited my post. Thanks. Also, in regards to the latter part of your comment, none, I just want to cancel the thread without problem, but also have the thread be as close to a one second pulse as I can and as accurate as possible. – dovedevic Jan 04 '17 at 21:32
  • What kind of "second" are you looking for? Do you want wall-clock seconds? Real-time seconds? CPU seconds? Process seconds? – EOF Jan 05 '17 at 00:08

1 Answers1

1

You can use the sleep-until-absolute-time functionality of clock_nanosleep() to do this:

const double dutyCycle = 0.001;
struct timespec deadline;

clock_gettime(CLOCK_MONOTONIC, &deadline);

while (self->myself->ThreadActiveStatus != THD_DONE) {
    /* Sleep until start of next second tick */
    deadline.tv_sec += 1;
    deadline.tv_nsec = 0;
    while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL) != 0)
        if (errno != EINTR) return NULL;

    setPinValue(BBB_GPIO_PIN_ONE_PPS, ON);

    /* Sleep until duty cycle expired */
    deadline.tv_nsec = dutyCycle * 1000000000;
    while (clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &deadline, NULL) != 0)
        if (errno != EINTR) return NULL;

    setPinValue(BBB_GPIO_PIN_ONE_PPS, OFF);
}
caf
  • 233,326
  • 40
  • 323
  • 462