How to wait for 2 types of events in a loop (C)?

949 views Asked by At

I am trying to wait on waitpid() and read() in a while-true loop. Specifically, I am waiting for either one of these two events and then process it in each iteration of the loop. Currently, I have the following implementation (which is not I desired).

while (true) {
  pid_t pid = waitpid(...);
  process_waitpid_event(...);

  ssize_t sz = read(socket, ....);
  process_read_event(...);
}

The problem with this implementation is that the processing of the second event depends on the completion of the first event. Instead of processing these two events sequentially, I wish to process whichever event that comes first in each iteration of the loop. How should I do this?

3

There are 3 answers

5
Daniel Porteous On BEST ANSWER

If you don't want to touch threading, you can include this in the options of the call to waitpid:

pid_t pid = waitpid(pid, &status, WNOHANG);

As from the manpage for waitpid:

WNOHANG - return immediately if no child has exited.

As such, if waitpid isn't ready, it won't block and the program will just keep going to the next line.

As for the read, if it is blocking you might want to have a look at poll(2). You can essentially check to see if your socket is ready every set interval, e.g. 250ms, and then call read when it is. This will allow it to not block.

Your code might look a bit like this:

// Creating the struct for file descriptors to be polled.
struct pollfd poll_list[1];
poll_list[0].fd = socket_fd;
poll_list[0].events = POLLIN|POLLPRI;
// POLLIN  There is data to be read
// POLLPRI There is urgent data to be read

/* poll_res  > 0: Something ready to be read on the target fd/socket.
** poll_res == 0: Nothing ready to be read on the target fd/socket.
** poll_res  < 0: An error occurred. */
poll_res = poll(poll_list, 1, POLL_INTERVAL);

This is just assuming that you're reading from a socket, judging from the variable names in your code. As others have said, your problem might require something a bit more heavy duty like threading.

0
Reaz Murshed On

The answer of @DanielPorteous should work too if you don't want to use thread in your program.

The idea is simple, not keeping the waitpid and the read function to wait unless they consumes some time to do their operation. The idea is keeping a timeout mechanism so that, if waitpid has nothing to create an impact to the whole operation, it will return immediately and the same thing goes for the read operation too.

If the read function takes very long time to read the whole buffer, you may restrict the reading manually from the read function so that it doesn't read the whole at once, rather it reads for 2 milliseconds and then pass the cycle to the waitpid function to execute.

But its safe to use threading for your purpose and its pretty easy to implement. Here's a nice guideline about how can you implement threading.

In your case you need to declare two threads.

pthread_t readThread;
pthread_t waitpidThread;

Now you need to create the thread and pass specific function as their parameter.

pthread_create(&(waitpidThread), NULL, &waitpidFunc, NULL);
pthread_create(&(readThread), NULL, &readFunc, NULL);

Now you may have to write your waitpidFunc and readFunc function. They might look like this.

void* waitpidFunc(void *arg)
{
    while(true) {
        pid_t pid = waitpid(...);

        // This is to put an exit condition somewhere. 
        // So that you can finish the thread
        int exit = process_waitpid_event(...);

        if(exit == 0) break;
    }

    return NULL;
}
1
Marian On

I think that the right tool in this situation is select or poll. Both are doing essentially the same job. They allow to select those descriptors where an input is available. Hence you can wait simultaneously on two sockets for example. However, it is not directly usable in your case as you want to wait for a process and socket. The solution will be to create a pipe which will receive something when the waitpid finishes.

You can launch a new thread and connect it with the original one with a pipe. The new thread will invoke waitpid and when it finished it will write its result to the pipe. The main thread will wait either for the socket or pipe using select.