4

I am using WiringPi on a raspberry pi. With it I assign an interrupt function which is called later. I'm not sure what to do when waiting for the interupt to be called.

Examples use a (spinlock?) for (;;) e.g.

int main()
{
    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
        // really?
    }
    return 0;
}

And I noticed that sleep works too. The interrupt is called regardless of the sleep

int main() 
{
    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
        sleep(1000000);
    }
    return 0;
}

What is the best practise to keep the program running, using minimal resources (say if this were for a background demon)?

Coming from other languages, I would have thought for(;;) would eat up resources. I would like to know what to do or pointers on what to do (threads etc).

Vorac
  • 8,726
  • 11
  • 58
  • 101
Ross
  • 14,266
  • 12
  • 60
  • 91
  • 1
    A `for(;;)` loop that just sleeps will not consume significant resources, because as soon as an iteration of the loop executes it yields back to the scheduler. If you don't necessarily need to do any processing in the main loop after the interrupt has been triggered, why not leave the main loop as-is (in your second example) and do the interrupt processing in the ISR? – Martin Törnwall Jan 22 '16 at 11:41
  • 1
    Compilers are allowed to assume that `for(;;) { }` (with empty body) terminates immediately. It has happened. – Joker_vD Jan 22 '16 at 11:49
  • 3
    @Joker_vD: Why should an infinite loop terminate?? – too honest for this site Jan 22 '16 at 11:50
  • That behaves the same in any language. Why do you think `sleep` behaves differently in e.g. Python? – too honest for this site Jan 22 '16 at 11:52
  • @Joker_vD: This is different than an endless loop! It would change the behaviour of the abstract machine. Read: http://port70.net/~nsz/c/c11/n1570.html#6.8.5p6 carefully (see footnote 156). – too honest for this site Jan 22 '16 at 12:00

6 Answers6

3

WiringPi sets up a separate thread and calls your isr function from that thread.

You can then use a pthread condition variable to block another thread, main() in this case, and have the isr function wake it up when an interrupt occurs.

#include <pthread.h>

pthread_cond_t isr_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t isr_mtx = PTHREAD_MUTEX_INITIALIZER;
unsigned int isr_count = 0;

void myInterrupt(void)
{
    pthread_mutex_lock(&isr_mtx);
    isr_count++;
    pthread_cond_signal(&isr_cond);    
    pthread_mutex_unlock(&isr_mtx);

}

int main() 
{
    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
       pthread_mutex_lock(&isr_mtx);
       while (isr_count == 0) {
          pthread_cond_wait(&isr_cond, &isr_mtx);
       }
       //add logic here to handle the ISR, isr_count
       //tells you how many ISRs occured.
       //heavy work should be moved outside the mutex.

      isr_count = 0;
      pthread_mutex_unlock(&isr_mtx);
    }

    return 0;
}

You need to compile and link your code with the -pthread flag.

Martin James
  • 24,453
  • 3
  • 36
  • 60
nos
  • 223,662
  • 58
  • 417
  • 506
  • 3
    You must not call anything that might block, eg. pthread_mutex_lock(), from interrupt context. It is extremely dangerous, and WILL fail disastrously! – Martin James Jan 22 '16 at 12:24
  • @MartinJames You are 100% correct. But Please note that we are talking about the WiringPI library. This isn't a real ISR. It is a library that hooks up to interrupts on Linux delivered to user space via sysfs. The WiringPI library spawns a very normal pthread that gets fed the notification that an interrupt happened via pipe and calls the supplied "ISR" function - all in userspace, no interrupt context, no funny business, no unix signal handling, etc. The real ISR is done in the kernel. – nos Jan 22 '16 at 14:12
  • OK, @nos, I removed my downvote:) Note that I made, and immediately reversed, an edit, (I just added a period and removed it), in order to do so 'cos vote timeout. – Martin James Jan 22 '16 at 14:19
3

I recently had to do exactly this. My theory was KISS. sleep is written to use minimal resources by definition - and using it means I don't have to care about threads.

Simple, readable and no measurable performance hit on an original Raspberry Pi B:

int main() 
{
    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
        sleep(UINT_MAX);
    }
    return 0;
}

Note the use of UINT_MAX to minimise number of for loop calls - this assumes an unsigned 32-bit timer delay, which is what WiringPi uses.

Ross
  • 14,266
  • 12
  • 60
  • 91
stefandz
  • 60
  • 11
3

You can also use a semaphore. sem_post() must be async-signal-safe:

#include <semaphore.h>

static sem_t staticSem;

void myInterupt( void )
{
    sem_post( &staticSem );
}

int main() 
{
    sem_init( &staticSem, 0, 0 );

    // register interrupt
    wiringPiISR( 18, INT_EDGE_BOTH, &myInterrupt );

    for (;;) {
        sem_wait( &staticSem );
        ...
    }
    return 0;
}

Error checking and handling potential spurious wakeups from sem_wait() aren't included.

Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
2

It depends on whether you require notification at user level. There are some cases, then:

1) 'I need no notification at all since I do everythng in interrupt state'. Fine, wait in main() using a long Sleep() loop, or Sleep(INFINITE) if available, or wait on some synchro object that is never signaled, or loop round some 'set low power state' or 'HALT' instruction. This removes the neeed for execution from user state and just leaves your processor waiting for interrupts.

2) 'I need notification in user state, but I don't care about latency'. OK, set some atomic int or boolean from interrupt state, and poll it from whichever main() or thread required to notice that interrupts can happen. Such polling can be wasteful and responds slowly, but if you don't need to care in your app, fine:)

3) 'I need notification in user state as fast as is praticable'. The 'classic' manner to implement such signaling is to have a handler thread wait on a semaphore, signal the semaphore from the interrupt-handler and 'instruct' the OS that it must perform a reschedule as soon as the interrupt-handler is over. so making the waiting thread ready/running. Other signaling mechanisms, eg. events/condvars, may also be safe to signal from interrupt-state, but you should check with your OS documentation.

What can I not do? You cannot, and /or must not call anything in interrupt state that might try to block. That is disastrous and your OS will probably fall over hard:(

Martin James
  • 24,453
  • 3
  • 36
  • 60
0

I'm not sure about WiringPi in particular, but how about using a pthread_cond_t that waits to be signalled by your interrupt handler when finished with the task?

This is a reference.

Box Box Box Box
  • 5,094
  • 10
  • 49
  • 67
alediaferia
  • 2,537
  • 19
  • 22
  • No. To properly signal a `pthread_cond_t` with `pthread_cond_signal()` requires locking a mutex. That is about as [async-signal-**unsafe**](http://stackoverflow.com/questions/8493095/what-constitutes-asynchronous-safeness) as possible. See http://stackoverflow.com/questions/4544234/calling-pthread-cond-signal-without-locking-mutex – Andrew Henle Jan 22 '16 at 12:41
  • @AndrewHenle what's wrong between my answer and locking a mutex? I've never said it shouldn't be done. – alediaferia Jan 22 '16 at 13:15
  • 2
    Read the links in my first comment. To safely and reliably use `pthread_cond_signal()` requires locking a shared mutex. Locking a mutex is not async-signal-safe and thus can not be safely done in a signal handler. – Andrew Henle Jan 22 '16 at 13:20
  • @AndrewHenle oh, that's right! Sorry for the misunderstanding. – alediaferia Jan 22 '16 at 13:21
  • @AndrewHenle There's no async-signal-unsafe stuff being done with the WiringPI library. This isn't a signal handler, it's driven by a normal poll() event loop. – nos Jan 22 '16 at 14:15
0

I would (and do, when coding embedded) rather use a spinlock while(1) ;. It is straightforward and expresses intent - never go past this point.

Not only does sleep have an expiration time, which might not be a problem right away, but become a problem after years. It also has to perform some computations to actually count the time.

Here is a comparison between sleep(0xFFFFFFFF); on a Intel Core i3-4330TE:

        .file   "test.c"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        movl    $-1, %edi
        movl    $0, %eax
        call    sleep
        movl    $0, %eax
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
        .section        .note.GNU-stack,"",@progbits

and a while(1); approach:

        .file   "test.c"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
.L2:
        jmp     .L2
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Debian 4.9.2-10) 4.9.2"
        .section        .note.GNU-stack,"",@progbits

So less work is done if time is not kept track of. I am not sure if the linux scheduler can recognise this pattern until an ISR arrives.


Having said this, the proper way to "keep the program running, using minimal resources" is to look into the sleep modes (page 2-14 or 36), that the processor provides.

Vorac
  • 8,726
  • 11
  • 58
  • 101
  • ' It also has to perform some computations to actually count the time.' - sure, once, then it's done, and no more CPU cycles or memory-bandwidth get wasted on pointless looping. If you are actually worried about an expiration time, put the Sleep() call in a loop. Waiting on an event/emaphore that is never signaled is another no-CPU/bandwitch waste solution, if no HALT/lowPower/whatever instruction is available on the processor. – Martin James Jan 22 '16 at 14:26