ChildProcess and output (windows)


#1

I have a simple thread that starts an infinite process (ping -t 127.0.0.1), the code below

void EyeBallChild::run()
{
	ChildProcess child;

	if (!child.start("ping -t 127.0.0.1"))
	{
		return;
	}
	else
	{
		for (;;)
		{
			if (wait (50))
			{
				if (threadShouldExit())
				{
					DBG("Thread signalled to exit");
					return;
				}
				else
				{
					DBG("notified but no exit needed");
				}
			}
			else
			{
				DBG("Timeout");
				char buffer [8192];
				const int num = child.readProcessOutput (buffer, sizeof (buffer));
			}
		}
	}
}

The problem is that if the line child.readProcessOutput is there the thread disappears into the void, i mean it’s there i can see it in VS but i have no way of talking to it, i try to do notify on it but nothing happens, the last message on the console is “Timeout” and that’s it, my application keeps running but i can never wake the thread up, i wanted to read the output in an infinite loop like you’ve done in the readEntireOutput method but that did not worked.

I’m guessing there is a problem with still running processes, everything is fine if the process exits but while it’s running it’s in some “LimBo” state. Could anyone help with that ?


#2

Any ideas on that, maybe someone has a workaround class for this ?


#3

Are you waiting long enough for the buffer to fill? In Windows, Jules uses named pipes when he starts the child process. The read then looks like this:

    int read (void* dest, int numNeeded) const
    {
        int total = 0;

        while (ok && numNeeded > 0)
        {
            DWORD available = 0;

            if (! PeekNamedPipe ((HANDLE) readPipe, nullptr, 0, nullptr, &available, nullptr))
                break;

            const int numToDo = jmin ((int) available, numNeeded);

            if (available == 0)
            {
                if (! isRunning())
                    break;

                Thread::yield();
            }
            else
            {
                DWORD numRead = 0;
                if (! ReadFile ((HANDLE) readPipe, dest, numToDo, &numRead, nullptr))
                    break;

                total += numRead;
                dest = addBytesToPointer (dest, numRead);
                numNeeded -= numRead;
            }
        }

        return total;
    }

“ok” is a class member, which will go false when the process ends. So, if the process terminates, the call will return the number of bytes read. But if the process is going forever, the call won’t return until the requested number has been filled (it calls Thread::yield which translates into Win32 Sleep(0)).

You could use readAllProcessOutput(), which uses a series of 512 byte requests, but even that will take awhile with pings once a second (or whatever the repeat rate under Windows is). You could look at readAllProcessOutput and mimic it with a much smaller buffer still.

Under Windows, you could poll the pipe (just like read does above), but I’m not sure that is something that could be supported on all platforms. I’d have to look at how readProcessOutput is implemented on Linux, Mac, etc.