Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable buffering on redirected stdout Pipe (Win32 API, C++)

I'm spawning a process from Win32 using CreateProcess, setting the hStdOutput and hStdError properties of STARTUPINFO to pipe handles created with CreatePipe. I've got two threads reading the pipes, waiting for data to become available (or the process to complete, at which point it checks that there is no data left before terminating the thread).
As data becomes available, I write the output out to effectively a big textbox.

What's happening is the output is being buffered, so a slow running process just gets chunks of data thrown at the text box, but not "as it happens".

I'm not sure if it's the pipe that's doing the buffering, or something to do with the redirection.

Is there any way to either set the pipe to be unbuffered, or start the process in such a way that the stdout is sent as soon as possible?

I'm testing with a test app that prints lines one second apart

Here is line one
(waits one second)
Here is line two
(waits one second)
... etc
like image 601
Dave Brotherstone Avatar asked Aug 02 '10 06:08

Dave Brotherstone


3 Answers

The buffering is probably in the C runtime (printf etc) and there is not much you can do about it (IIRC it does a isatty() check to determine a buffering strategy)

like image 66
Anders Avatar answered Oct 22 '22 14:10

Anders


There's SetNamedPipeHandleState, but it only controls buffering for remote pipes, not when both ends are on the same computer.

like image 22
Ben Voigt Avatar answered Oct 22 '22 14:10

Ben Voigt


In my case the buffering was in the output of the client (as @Anders wrote), which uses normal printf. Maybe this also depends on the implementation of the C runtime (Visual Studio 2019), maybe the runtime detects 'not a console' and enables buffering.

So I disabled the buffering with this call setvbuf(stdout, (char*)NULL, _IONBF, 0); in my client, now I get the output immediately in the pipe in the server.

Just for completeness: Here's how I read the pipe in the server

HANDLE in;
CreatePipe(&in, &startup.hStdOutput, &sa, 0);            // Pipe for stdout of the child

...

char buffer[16384];
DWORD read, total;
while (PeekNamedPipe(in, NULL, 0, &read, &total, NULL))
{
    if (total == 0)
    {
        if (WaitForSingleObject(info.hProcess, 0) == WAIT_OBJECT_0)
        {
            if (PeekNamedPipe(in, NULL, 0, &read, &total, NULL) && total == 0)
                break;
            continue;
        }
        Sleep(10);
        continue;
    }

    if (total > sizeof(buffer))
        total = sizeof(buffer);
    ReadFile(in, buffer, total, &read, NULL);

    ...
}

like image 37
some guy Avatar answered Oct 22 '22 14:10

some guy