Simon Elliott
Simon Elliott

Reputation: 2117

Wait for events based on time, child process ending, data arriving on file descriptor

An app needs to be able to set callbacks on various events. The events can be any of:

I need to write a function that will dispatch the events. The app will run on an embedded platform and should take reasonable steps to minimise CPU utilisation.

One approach would be a busy loop:

#define MAX_EVENT 10

typedef enum tagEventType {EV_DATA, EV_CHILD, EV_TIME} tEventType;
typedef void(*tEventCallback)(int);
typedef struct tagEvent
{
    int sequence;
    tEventType type;
    tEventCallback callback;
    time_t time;
    int fd;
    pid_t pid;    
} tEvent;

static tEvent eventsTable[MAX_EVENT];    

static void processEvents(void)
{
    int i;
    for ( i=0; i<MAX_EVENT; ++i )
    {
        if ( eventsTable[i].sequence > 0 )
        {
            switch(eventsTable[i].type)
            {
                case EV_DATA:
                    if ( checkForDataUsingPoll( eventsTable[i].fd) )
                    {
                        eventsTable[i].callback(eventsTable[i].sequence);
                    }                    
                    break;
                case EV_CHILD:
                    if ( kill(eventsTable[i].pid, 0) == -1 )
                    {
                        eventsTable[i].callback(eventsTable[i].sequence);
                        eventsTable[i].sequence = 0;                        
                    }
                    break;
                case EV_TIME:
                    {
                        if ( time(NULL) > eventsTable[i].time )
                        {
                            eventsTable[i].callback(eventsTable[i].sequence);
                            eventsTable[i].sequence = 0;
                        }
                    }
                    break;
            }
        }
    }
}

int main(  int argc, char* argv[] )
{
    signal(SIGCHLD, SIG_IGN);
    while(1)
    {
        processEvents();
        usleep(1000);
    }
    return 0;    
}

checkForDataUsingPoll() uses poll() with a timeout of zero to check whether data has arrived at the file descriptor. I've omitted it for brevity.

Is there a way of doing this which avoids the busy loop? Some mixture of poll, signals, and alarm perhaps?

Upvotes: 1

Views: 694

Answers (1)

Some programmer dude
Some programmer dude

Reputation: 409136

For file descriptors, there is poll (which you already use) or select or epoll or libevent. You can also check the SIGIO signal, or the functions prefixed with aio_.

For the time, you can check the alarm system call.

For termination of child processes, check the SIGCHLD signal.

Edit To answer the comment: For the signals, you don't really have to do anything, the signal handlers you set will be called automatically by the system when they occur. If they happen set a flag and possible other variables to inform your program what happened.

When a signal is received, poll will return with an error and errno set to EINTR. If that is the case, check the flags set by the signal handlers to see what happened, and call whatever functions you need. If it was an alarm, then you have to restart the alarm for the next event. Then call poll again, waiting for the next event to occur.

Unless you have other things to do, you can even call poll with an infinite timeout. It will return on signals or file descriptor ready.

Upvotes: 1

Related Questions