Re: Sometimes the output to the stdout in Windows disappears

Alexander Law <exclusion@gmail.com>

From: Alexander Lakhin <exclusion@gmail.com>
To: Tom Lane <tgl@sss.pgh.pa.us>
Cc: Victor Spirin <v.spirin@postgrespro.ru>, pgsql-hackers@lists.postgresql.org
Date: 2020-10-17T08:00:02Z
Lists: pgsql-hackers

Attachments

16.10.2020 19:18, Tom Lane wrote:
> Oh, very interesting.
> Now that you have it somewhat in captivity, maybe you could determine
> some things:
>
> 1. Is it only stdout that's affected?  What of other stdio streams?
> (Note that testing stderr might be tricky because it's probably
> line-buffered.)
stderr is affected too. Just replacing stdout with stderr in connect.c
and 000_connect.pl gives the same result.

Moreover, the following modification:
...
    outfile = fopen("out", "w");
...
    fprintf(stdout, "stdout test\n");
    fprintf(stderr, "stderr test\n");
    fprintf(outfile, "outfile test\n");
    WSACleanup();
...

---
for (my $i =0; $i < 100000; $i++) {
    unlink('out');
    IPC::Run::run(\@cmd, '>', \$stdout, '2>', \$stderr);
    open(my $fh, '<', 'out') or die $!;
    my $fileout = <$fh>;
    ok(defined $fileout && $fileout ne '');
    close($fh);
}

detects similar failures too. (On a fail the out file exists but has
zero size.)

> 2. Does an fflush() just before, or just after, WSACleanup() fix it?
"fflush(NULL);" just before or after WSACleanup() fixes things.

I've managed to record in ProcMon the activity log for a failed run
(aside normal runs). Excerpts from the log are attached. As we can see,
the failed process doesn't even try to write into IPC-Run's temp file.

> Depending on your answers to the above, maybe some hack like this
> would be acceptable:
>
>  	free(conn);
>  
>  #ifdef WIN32
> +	fflush(NULL);
>  	WSACleanup();
>  #endif
>  }
>
> It's not very nice for a library to be doing global things like that,
> but if the alternative is loss of output, maybe we should.
But now we see that the WSACleanup call is a global thing by itself.
> But wait a minute: I just looked at Microsoft's docs [1] and found
>
>     In a multithreaded environment, WSACleanup terminates Windows Sockets
>     operations for all threads.
>
> This makes me (a) wonder if that explains the side-effects on stdio,
> and (b) question why libpq is calling WSACleanup at all.
> What if we arranged to call WSAStartup just once, during the first
> libpq connection-open in a process, and then never did WSACleanup?
> Surely the OS can cope with that, and it eliminates any risk that
> WSACleanup breaks something.
What bothers me is:

    There must be a call to *WSACleanup* for each successful call to
    WSAStartup
    <https://docs.microsoft.com/en-us/windows/desktop/api/winsock/nf-winsock-wsastartup>.
    Only the final *WSACleanup* function call performs the actual
    cleanup. The preceding calls simply decrement an internal reference
    count in the WS2_32.DLL.

So third-party application developers should understand that when using
libpq they would have to call WSACleanup one more time to perform "the
actual cleanup". (And thus WSAStartup is kind of like a global thing too.)
But may be it's a way better than to have a confirmed risk of losing data.

Best regards,
Alexander

Commits

  1. In libpq for Windows, call WSAStartup once and WSACleanup not at all.

  2. Update the Winsock API version requested by libpq.