Search code examples
cwindowsmonitoringwaitstatus

What is the closest Windows equivalent to the POSIX wait mechanism?


Linux supports the POSIX wait mechanism defined in "sys/wait.h". The methods wait, waitid, waitpid might be used to exchange status information between parent and child processes that have been created using fork.

Windows neither does provide (native) support for fork nor the POSIX wait mechanism. Instead there are other means available to spwan child processes i.e. CreateProcess.

When porting linux applications written in C or C++ using fork/wait to Windows what would the most proper native* way to monitor state changes (namely WEXITED, WSTOPPED, WCONTINUED) of child processes in the parent process?

*native meaning using no additional libraries, frameworks, programs (like cygwin, minGW) that do not ship with windows or are provided directly by MS in form of runtime environments.

Edit: As requested in the comments I did provide some more information about what problem should be solved in form of pseudo code:

//creates a new child process that is a copy of the parent (compare 
//POSIX fork()) and returns some sort of handle to it.
function spawnChild() 

// returns TRUE if called from the master process FALSE otherwise
function master()

// return TRUE if called from a child process FALSE otherwise
function child()

// returns TRUE if child process has finished its work entirely, 
// FALSE otherwise.
function completelyFinished()

//sends signal/message "sig" to receive where receiver is a single 
//handle or a set of handles to processes that shall receive sig
function sendSignal(sig, receiver)

// terminates the calling process
function exit()

// returns a handle to the sender of signal "sig"
function senderOf(sig)

function masterprocess()
  master //contains handle to the master process
  children = {}   //this is an empty set of handles to child processes
  buf[SIZE]  //some memory area of SIZE bytes available to master process and all children
  FOR i = 0 TO n - 1
    //spawn new child process and at its handle to the list of running 
    //child processes.
    children <- children UNION spawnChild() 
  IF(master())
    <logic here>
    sendSignal(STARTWORKING, children) //send notification to children
    WHILE(signal = wait())  // wait for any child to respond (wait is blocking) 
      IF signal == IMDONE
        <logic here (involving reads/writes to buf)>
        sendSignal(STARTWORKING, senderOf(signal))
      ELSEIF signal == EXITED
        children <- children \ signal.sender //remove sender from list of children
  ELSEIF(child())
    WHILE(wait() != STARTWORKING);
       <logic here (involving reads/writes to buf)>
       IF completelyFinished()
         sendSignal(EXITED, master)
         exit()
       ELSE
         sendSignal(IMDONE, master)

Solution

  • Before I answer the actual question, I'm going to recommend a better solution: you should consider simplifying the relationship between the parent and children.

    Based on the pseudocode, the signals between parent and children are serving as a crude form of cross-process mutex, i.e., all they do is to prevent the code here:

      IF signal == IMDONE
        <logic here (involving reads/writes to buf)>
        sendSignal(STARTWORKING, senderOf(signal))
    

    from running multiple instances simultaneously. Instead, <logic here> should be moved into the corresponding child process, protected by a mutex so that only one child can run it at a time.

    At that point, all the parent needs to do is to launch the children and wait for them all to exit. That is easily done in Windows by waiting on the process handle.

    (I would imagine that modern POSIX also supports some sort of cross-process mutex somewhat more sophisticated than signals.)


    It would also be worth reconsidering whether you really really need multiple processes. Multiple threads would be more efficient, and if the code is properly written, it should not be difficult to adapt it.


    Be that as it may, if for some reason you absolutely must retain as much of the original program structure as possible, pipes are probably going to be your best bet.

    • Sending a signal becomes writing a single byte.

    • In a child, waiting for a signal from the parent becomes reading a single byte.

    • Waiting in the parent for a message from any of the children is a little trickier. It is still a single-byte read (for each child) but you'll need to use overlapped I/O and, if you need to support more than 64 children, IOCP.

    (Alternatively, you could use multiple threads, but that might involve too much of a structural change.)

    • If the pipes are implemented correctly, when a child exits or dies the corresponding read operation in the parent will terminate with the ERROR_BROKEN_PIPE error. So there is no need for a separate mechanism to monitor the health of the children.

    In this context, I think anonymous pipes would be the most appropriate choice. These are simplex, so you'll need two pipes for each child. You can pass the child's end of the pipe handles as the standard input and output for the child process.

    For anonymous pipes, you will need to make sure that you close the parent's copy of the handles once each child has been started, and also that each child only inherits the handles corresponding to its own pipe. If there are any additional handles left open to the child's end of its pipe, the parent will not receive any notification when the child exits.


    None of this is particularly complicated, but be aware that named pipe I/O has a bit of a learning curve. Asynchronous I/O even more so, particularly if you are coming from a UNIX background. Note in particular that to use asynchronous I/O, you issue an operation and then wait for it to complete, as opposed to the UNIX model where you wait for the I/O to be ready and then issue the operation.