4

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?

Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
cstjh
  • 153
  • 1
  • 2
  • 8
  • You want to do it in asynchronous way right? – Akhter Al Amin Dec 11 '16 at 05:15
  • 1
    This is more like a threading problem. You can search for how threading can be implemented in C. – Reaz Murshed Dec 11 '16 at 05:16
  • Please elaborate on your problem. What is the waitpid for and what are you reading from. In particular, is it the case that you simply have one child process and want to read its output but just need to know when it is over? –  Dec 11 '16 at 13:20

3 Answers3

3

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.

Daniel Porteous
  • 5,536
  • 3
  • 25
  • 44
  • Can you elaborate why are you suggesting something like this as opposed to handling SIGCHLD? –  Dec 11 '16 at 10:24
  • Because the user was originally using waitpid, not handling signals, though this would of course be a valid option given that their program is waiting on a child process. – Daniel Porteous Dec 11 '16 at 10:35
  • cmon. Installing a handler for SIGCHLD and handling EINTR from read is the standard thing to do suitable in most cases. The solution as proposed is inferior in that it adds more syscalls to the loop and makes the thread wake up even when there is nothing to do. More importantly though, it is very unclear if OP has any reason to waitpid in the loop in the first place. Most likely they simply spawned a child and just want to stop reading after it exits. The solution is to just keep reading until there is nothing left and collect the exit status with waitpid afterwards. –  Dec 11 '16 at 10:54
  • That said, "they are not handling signals" does not sound like a valid reason. They are not using poll either. It is possible the standard solution described above cannot be used here and perhaps the code you presented may be used as a reasonable workaround. Unfortunately the answer was presented without asking OP to clarify the problem, similarly to other answers here. This one at least does not straight up go to threads. –  Dec 11 '16 at 10:55
  • 1
    No need to be snarky, I'm just fitting the most obvious interpretation of the OPs question. Feel free to propose your own answer. – Daniel Porteous Dec 11 '16 at 11:28
1

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;
}
Reaz Murshed
  • 23,691
  • 13
  • 78
  • 98
0

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.

Marian
  • 7,402
  • 2
  • 22
  • 34
  • Excuse my ignorance, can you contrast this with the use of SIGCHLD? What you are presenting seems to be significantly more involved and does not provide any benefit that I would see. Also it is unclear if OP needs anything of the sort in the first place, chances are good they actually have a pipe and just need to wait for it to close, after which point then can just waitpid to get the exit status. –  Dec 11 '16 at 10:20