Hi, thanks for your feedback! > This doesn't work the way you want; as much as possible of the base files from mingw-w64-crt are agnostic of the choice of default CRT
Ah, indeed the same CRT library is used regardless of the -mcrtdll argument used in GCC. > [...] It is calling code outside of __native_startup_state guards. Yeah, I am now fixing that patch too! Out of curiosity, what is that lock used for? I can't quite understand > Anyway, for such changes it would be very useful to have an automated test Ok, done! I have attached the patchset V2. Here are the relevant changes: 1. Fixed a few typos in the commit message (s/then/than, C23 section is now correct) 2. Fixed indentation 3. I now check the return value of setvbuf. Let me know if we can skip that... 4. Call to setvbuf has been moved right before the invocation of .CRT$XC callbacks. IMO that's the right time to make the call 5. Added a testcase Slightly off-topic: the mingw-w64-crt testsuite always links against the system-provided mingw-w64-crt rather than the built one. Is there any easy way to change that? Thanks, Luca ________________________________ Da: Pali Rohár <[email protected]> Inviato: sabato 13 dicembre 2025 20:03 A: Luca Bacci <[email protected]>; Martin Storsjö <[email protected]> Cc: Mingw W64 Public <[email protected]> Oggetto: Re: [Mingw-w64-public] Ensure that stderr is not fully buffered On Saturday 13 December 2025 20:38:36 Martin Storsjö wrote: > On Sat, 13 Dec 2025, Luca Bacci wrote: > > > diff --git a/mingw-w64-crt/crt/crtexe.c b/mingw-w64-crt/crt/crtexe.c > > index 94bad6aa..d7497ef0 100644 > > --- a/mingw-w64-crt/crt/crtexe.c > > +++ b/mingw-w64-crt/crt/crtexe.c > > @@ -199,6 +199,17 @@ __tmainCRTStartup (void) > > if (__globallocalestatus == -1) > > _configthreadlocale (-1); > > > > +#if !defined (_UCRT) > > This doesn't work the way you want; as much as possible of the base files > from mingw-w64-crt are agnostic of the choice of default CRT. In particular, > all the files in mingw-w64-crt are built with "-D__MSVCRT_VERSION__=0x600". > Only the files that go into the individual CRT import libraries can assume > things about which CRT they're used with. > > One way of working around this would be to add a call to a function, which > in the libmsvcr*.a libraries does what you want, but in libucrt*.a would be > a dummy no-op function. > > Pali may have other suggestions or opinions about how to deal with this. > > // Martin Yes, ifdef for _UCRT in mingw-w64-crt/* directory does not work, it is never defined. I would suggest to call setvbuf() unconditionally. That is simple solution and would work with any CRT library. I looked at second change https://sourceforge.net/p/mingw-w64/mailman/message/59272390/ and seems that this one is not correct too. It is calling code outside of __native_startup_state guards. Anyway, for such changes it would be very useful to have an automated test. So we can see what is happening without the change and with the change. Lot of different tests are in the mingw-w64-crt/testcases/ dir. For example test t_assert.c is spawning new process and using pipe on stderr and is checking that stderr was properly transferred.
From 7a85d9538917916d6bb5c93f21d823708ba121f2 Mon Sep 17 00:00:00 2001 From: Luca Bacci <[email protected]> Date: Tue, 16 Dec 2025 15:08:57 +0100 Subject: [PATCH 1/3] crt: Add test for stderr buffering mode on startup Signed-off-by: Luca Bacci <[email protected]> --- mingw-w64-crt/testcases/Makefile.am | 1 + mingw-w64-crt/testcases/Makefile.in | 131 +++++++++++-------- mingw-w64-crt/testcases/t_stderr_buffering.c | 58 ++++++++ 3 files changed, 134 insertions(+), 56 deletions(-) create mode 100644 mingw-w64-crt/testcases/t_stderr_buffering.c diff --git a/mingw-w64-crt/testcases/Makefile.am b/mingw-w64-crt/testcases/Makefile.am index 345c525e..14c59abc 100644 --- a/mingw-w64-crt/testcases/Makefile.am +++ b/mingw-w64-crt/testcases/Makefile.am @@ -62,6 +62,7 @@ testcase_progs = \ t_stat_f64 \ t_stat_t64 \ t_stat_t64_f64 \ + t_stderr_buffering \ t_stdint \ t_time \ t_tls1 \ diff --git a/mingw-w64-crt/testcases/Makefile.in b/mingw-w64-crt/testcases/Makefile.in index 226b90cd..af631187 100644 --- a/mingw-w64-crt/testcases/Makefile.in +++ b/mingw-w64-crt/testcases/Makefile.in @@ -194,12 +194,13 @@ am__EXEEXT_2 = tstmainc$(EXEEXT) tstmaincpp$(EXEEXT) \ t_stprintf0_u$(EXEEXT) t_stprintf1_a$(EXEEXT) \ t_stprintf1_u$(EXEEXT) t_setjmp$(EXEEXT) t_sigv$(EXEEXT) \ t_speed_powl$(EXEEXT) t_stat$(EXEEXT) t_stat_f64$(EXEEXT) \ - t_stat_t64$(EXEEXT) t_stat_t64_f64$(EXEEXT) t_stdint$(EXEEXT) \ - t_time$(EXEEXT) t_tls1$(EXEEXT) t_tmpfile$(EXEEXT) \ - t_trycatch$(EXEEXT) t_stat_slash$(EXEEXT) t_utime$(EXEEXT) \ - t_vsscanf$(EXEEXT) t_wcrtomb$(EXEEXT) t_wcsrtombs$(EXEEXT) \ - t_wcstok_s$(EXEEXT) t_wctob$(EXEEXT) t_wreaddir$(EXEEXT) \ - t_fseeko64$(EXEEXT) $(am__EXEEXT_1) + t_stat_t64$(EXEEXT) t_stat_t64_f64$(EXEEXT) \ + t_stderr_buffering$(EXEEXT) t_stdint$(EXEEXT) t_time$(EXEEXT) \ + t_tls1$(EXEEXT) t_tmpfile$(EXEEXT) t_trycatch$(EXEEXT) \ + t_stat_slash$(EXEEXT) t_utime$(EXEEXT) t_vsscanf$(EXEEXT) \ + t_wcrtomb$(EXEEXT) t_wcsrtombs$(EXEEXT) t_wcstok_s$(EXEEXT) \ + t_wctob$(EXEEXT) t_wreaddir$(EXEEXT) t_fseeko64$(EXEEXT) \ + $(am__EXEEXT_1) @LIB32_TRUE@am__EXEEXT_3 = tests32/cimag$(EXEEXT) \ @LIB32_TRUE@ tests32/creal$(EXEEXT) tests32/cabs$(EXEEXT) \ @LIB32_TRUE@ tests32/cacos$(EXEEXT) tests32/cacosh$(EXEEXT) \ @@ -580,6 +581,9 @@ t_stat_t64_LDADD = $(LDADD) t_stat_t64_f64_SOURCES = t_stat_t64_f64.c t_stat_t64_f64_OBJECTS = t_stat_t64_f64.$(OBJEXT) t_stat_t64_f64_LDADD = $(LDADD) +t_stderr_buffering_SOURCES = t_stderr_buffering.c +t_stderr_buffering_OBJECTS = t_stderr_buffering.$(OBJEXT) +t_stderr_buffering_LDADD = $(LDADD) t_stdint_SOURCES = t_stdint.c t_stdint_OBJECTS = t_stdint.$(OBJEXT) t_stdint_LDADD = $(LDADD) @@ -1006,19 +1010,19 @@ am__depfiles_remade = ./$(DEPDIR)/t__fstat_all.Po \ ./$(DEPDIR)/t_speed_powl.Po ./$(DEPDIR)/t_stat.Po \ ./$(DEPDIR)/t_stat_f64.Po ./$(DEPDIR)/t_stat_slash.Po \ ./$(DEPDIR)/t_stat_t64.Po ./$(DEPDIR)/t_stat_t64_f64.Po \ - ./$(DEPDIR)/t_stdint.Po ./$(DEPDIR)/t_stprintf0_a.Po \ - ./$(DEPDIR)/t_stprintf0_u.Po ./$(DEPDIR)/t_stprintf1_a.Po \ - ./$(DEPDIR)/t_stprintf1_u.Po ./$(DEPDIR)/t_stprintf_a.Po \ - ./$(DEPDIR)/t_stprintf_u.Po ./$(DEPDIR)/t_swprintf.Po \ - ./$(DEPDIR)/t_swprintf0.Po ./$(DEPDIR)/t_swprintf1.Po \ - ./$(DEPDIR)/t_time.Po ./$(DEPDIR)/t_tls1.Po \ - ./$(DEPDIR)/t_tmain-t_tmain.Po ./$(DEPDIR)/t_tmpfile.Po \ - ./$(DEPDIR)/t_trycatch.Po ./$(DEPDIR)/t_utime.Po \ - ./$(DEPDIR)/t_vsscanf.Po ./$(DEPDIR)/t_wcrtomb.Po \ - ./$(DEPDIR)/t_wcsrtombs.Po ./$(DEPDIR)/t_wcstok_s.Po \ - ./$(DEPDIR)/t_wctob.Po ./$(DEPDIR)/t_wreaddir.Po \ - ./$(DEPDIR)/tstmain_sys_xxx.Po ./$(DEPDIR)/tstmainc.Po \ - ./$(DEPDIR)/tstmaincpp.Po \ + ./$(DEPDIR)/t_stderr_buffering.Po ./$(DEPDIR)/t_stdint.Po \ + ./$(DEPDIR)/t_stprintf0_a.Po ./$(DEPDIR)/t_stprintf0_u.Po \ + ./$(DEPDIR)/t_stprintf1_a.Po ./$(DEPDIR)/t_stprintf1_u.Po \ + ./$(DEPDIR)/t_stprintf_a.Po ./$(DEPDIR)/t_stprintf_u.Po \ + ./$(DEPDIR)/t_swprintf.Po ./$(DEPDIR)/t_swprintf0.Po \ + ./$(DEPDIR)/t_swprintf1.Po ./$(DEPDIR)/t_time.Po \ + ./$(DEPDIR)/t_tls1.Po ./$(DEPDIR)/t_tmain-t_tmain.Po \ + ./$(DEPDIR)/t_tmpfile.Po ./$(DEPDIR)/t_trycatch.Po \ + ./$(DEPDIR)/t_utime.Po ./$(DEPDIR)/t_vsscanf.Po \ + ./$(DEPDIR)/t_wcrtomb.Po ./$(DEPDIR)/t_wcsrtombs.Po \ + ./$(DEPDIR)/t_wcstok_s.Po ./$(DEPDIR)/t_wctob.Po \ + ./$(DEPDIR)/t_wreaddir.Po ./$(DEPDIR)/tstmain_sys_xxx.Po \ + ./$(DEPDIR)/tstmainc.Po ./$(DEPDIR)/tstmaincpp.Po \ complex/$(DEPDIR)/tests32_cabs-main.Po \ complex/$(DEPDIR)/tests32_cacos-main.Po \ complex/$(DEPDIR)/tests32_cacosh-main.Po \ @@ -1241,34 +1245,35 @@ SOURCES = $(tests32_libnewcomplextests_a_SOURCES) \ t_snprintf.c t_snprintf0.c t_snprintf1.c t_snwprintf.c \ t_snwprintf0.c t_snwprintf1.c t_speed_powl.c t_stat.c \ t_stat_f64.c t_stat_slash.c t_stat_t64.c t_stat_t64_f64.c \ - t_stdint.c t_stprintf0_a.c t_stprintf0_u.c t_stprintf1_a.c \ - t_stprintf1_u.c t_stprintf_a.c t_stprintf_u.c t_swprintf.c \ - t_swprintf0.c t_swprintf1.c t_time.c t_tls1.c t_tmain.c \ - t_tmpfile.c $(t_trycatch_SOURCES) t_utime.c t_vsscanf.c \ - t_wcrtomb.c t_wcsrtombs.c t_wcstok_s.c t_wctob.c t_wreaddir.c \ - $(tests32_cabs_SOURCES) $(tests32_cacos_SOURCES) \ - $(tests32_cacosh_SOURCES) $(tests32_carg_SOURCES) \ - $(tests32_casin_SOURCES) $(tests32_casinh_SOURCES) \ - $(tests32_catan_SOURCES) $(tests32_catanh_SOURCES) \ - $(tests32_ccos_SOURCES) $(tests32_ccosh_SOURCES) \ - $(tests32_cexp_SOURCES) $(tests32_cimag_SOURCES) \ - $(tests32_clog_SOURCES) $(tests32_conj_SOURCES) \ - $(tests32_cpow_SOURCES) $(tests32_cproj_SOURCES) \ - $(tests32_creal_SOURCES) $(tests32_csin_SOURCES) \ - $(tests32_csinh_SOURCES) $(tests32_csqrt_SOURCES) \ - $(tests32_ctan_SOURCES) $(tests32_ctanh_SOURCES) \ - $(tests64_cabs_SOURCES) $(tests64_cacos_SOURCES) \ - $(tests64_cacosh_SOURCES) $(tests64_carg_SOURCES) \ - $(tests64_casin_SOURCES) $(tests64_casinh_SOURCES) \ - $(tests64_catan_SOURCES) $(tests64_catanh_SOURCES) \ - $(tests64_ccos_SOURCES) $(tests64_ccosh_SOURCES) \ - $(tests64_cexp_SOURCES) $(tests64_cimag_SOURCES) \ - $(tests64_clog_SOURCES) $(tests64_conj_SOURCES) \ - $(tests64_cpow_SOURCES) $(tests64_cproj_SOURCES) \ - $(tests64_creal_SOURCES) $(tests64_csin_SOURCES) \ - $(tests64_csinh_SOURCES) $(tests64_csqrt_SOURCES) \ - $(tests64_ctan_SOURCES) $(tests64_ctanh_SOURCES) \ - tstmain_sys_xxx.c tstmainc.c $(tstmaincpp_SOURCES) + t_stderr_buffering.c t_stdint.c t_stprintf0_a.c \ + t_stprintf0_u.c t_stprintf1_a.c t_stprintf1_u.c t_stprintf_a.c \ + t_stprintf_u.c t_swprintf.c t_swprintf0.c t_swprintf1.c \ + t_time.c t_tls1.c t_tmain.c t_tmpfile.c $(t_trycatch_SOURCES) \ + t_utime.c t_vsscanf.c t_wcrtomb.c t_wcsrtombs.c t_wcstok_s.c \ + t_wctob.c t_wreaddir.c $(tests32_cabs_SOURCES) \ + $(tests32_cacos_SOURCES) $(tests32_cacosh_SOURCES) \ + $(tests32_carg_SOURCES) $(tests32_casin_SOURCES) \ + $(tests32_casinh_SOURCES) $(tests32_catan_SOURCES) \ + $(tests32_catanh_SOURCES) $(tests32_ccos_SOURCES) \ + $(tests32_ccosh_SOURCES) $(tests32_cexp_SOURCES) \ + $(tests32_cimag_SOURCES) $(tests32_clog_SOURCES) \ + $(tests32_conj_SOURCES) $(tests32_cpow_SOURCES) \ + $(tests32_cproj_SOURCES) $(tests32_creal_SOURCES) \ + $(tests32_csin_SOURCES) $(tests32_csinh_SOURCES) \ + $(tests32_csqrt_SOURCES) $(tests32_ctan_SOURCES) \ + $(tests32_ctanh_SOURCES) $(tests64_cabs_SOURCES) \ + $(tests64_cacos_SOURCES) $(tests64_cacosh_SOURCES) \ + $(tests64_carg_SOURCES) $(tests64_casin_SOURCES) \ + $(tests64_casinh_SOURCES) $(tests64_catan_SOURCES) \ + $(tests64_catanh_SOURCES) $(tests64_ccos_SOURCES) \ + $(tests64_ccosh_SOURCES) $(tests64_cexp_SOURCES) \ + $(tests64_cimag_SOURCES) $(tests64_clog_SOURCES) \ + $(tests64_conj_SOURCES) $(tests64_cpow_SOURCES) \ + $(tests64_cproj_SOURCES) $(tests64_creal_SOURCES) \ + $(tests64_csin_SOURCES) $(tests64_csinh_SOURCES) \ + $(tests64_csqrt_SOURCES) $(tests64_ctan_SOURCES) \ + $(tests64_ctanh_SOURCES) tstmain_sys_xxx.c tstmainc.c \ + $(tstmaincpp_SOURCES) DIST_SOURCES = $(am__tests32_libnewcomplextests_a_SOURCES_DIST) \ $(am__tests32_libnewcomplextestsf_a_SOURCES_DIST) \ $(am__tests32_libnewcomplextestsld_a_SOURCES_DIST) \ @@ -1285,12 +1290,12 @@ DIST_SOURCES = $(am__tests32_libnewcomplextests_a_SOURCES_DIST) \ t_snprintf.c t_snprintf0.c t_snprintf1.c t_snwprintf.c \ t_snwprintf0.c t_snwprintf1.c t_speed_powl.c t_stat.c \ t_stat_f64.c t_stat_slash.c t_stat_t64.c t_stat_t64_f64.c \ - t_stdint.c t_stprintf0_a.c t_stprintf0_u.c t_stprintf1_a.c \ - t_stprintf1_u.c t_stprintf_a.c t_stprintf_u.c t_swprintf.c \ - t_swprintf0.c t_swprintf1.c t_time.c t_tls1.c t_tmain.c \ - t_tmpfile.c $(t_trycatch_SOURCES) t_utime.c t_vsscanf.c \ - t_wcrtomb.c t_wcsrtombs.c t_wcstok_s.c t_wctob.c t_wreaddir.c \ - $(am__tests32_cabs_SOURCES_DIST) \ + t_stderr_buffering.c t_stdint.c t_stprintf0_a.c \ + t_stprintf0_u.c t_stprintf1_a.c t_stprintf1_u.c t_stprintf_a.c \ + t_stprintf_u.c t_swprintf.c t_swprintf0.c t_swprintf1.c \ + t_time.c t_tls1.c t_tmain.c t_tmpfile.c $(t_trycatch_SOURCES) \ + t_utime.c t_vsscanf.c t_wcrtomb.c t_wcsrtombs.c t_wcstok_s.c \ + t_wctob.c t_wreaddir.c $(am__tests32_cabs_SOURCES_DIST) \ $(am__tests32_cacos_SOURCES_DIST) \ $(am__tests32_cacosh_SOURCES_DIST) \ $(am__tests32_carg_SOURCES_DIST) \ @@ -1711,9 +1716,9 @@ testcase_progs = tstmainc tstmaincpp tstmain_sys_xxx t__fstat_all \ t_snwprintf t_snwprintf0 t_snwprintf1 t_stprintf_a \ t_stprintf_u t_stprintf0_a t_stprintf0_u t_stprintf1_a \ t_stprintf1_u t_setjmp t_sigv t_speed_powl t_stat t_stat_f64 \ - t_stat_t64 t_stat_t64_f64 t_stdint t_time t_tls1 t_tmpfile \ - t_trycatch t_stat_slash t_utime t_vsscanf t_wcrtomb \ - t_wcsrtombs t_wcstok_s t_wctob t_wreaddir t_fseeko64 \ + t_stat_t64 t_stat_t64_f64 t_stderr_buffering t_stdint t_time \ + t_tls1 t_tmpfile t_trycatch t_stat_slash t_utime t_vsscanf \ + t_wcrtomb t_wcsrtombs t_wcstok_s t_wctob t_wreaddir t_fseeko64 \ $(am__append_1) EXTRA_DIST = \ t_snprintf_tmpl.h \ @@ -2541,6 +2546,10 @@ t_stat_t64_f64$(EXEEXT): $(t_stat_t64_f64_OBJECTS) $(t_stat_t64_f64_DEPENDENCIES @rm -f t_stat_t64_f64$(EXEEXT) $(AM_V_CCLD)$(LINK) $(t_stat_t64_f64_OBJECTS) $(t_stat_t64_f64_LDADD) $(LIBS) +t_stderr_buffering$(EXEEXT): $(t_stderr_buffering_OBJECTS) $(t_stderr_buffering_DEPENDENCIES) $(EXTRA_t_stderr_buffering_DEPENDENCIES) + @rm -f t_stderr_buffering$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(t_stderr_buffering_OBJECTS) $(t_stderr_buffering_LDADD) $(LIBS) + t_stdint$(EXEEXT): $(t_stdint_OBJECTS) $(t_stdint_DEPENDENCIES) $(EXTRA_t_stdint_DEPENDENCIES) @rm -f t_stdint$(EXEEXT) $(AM_V_CCLD)$(LINK) $(t_stdint_OBJECTS) $(t_stdint_LDADD) $(LIBS) @@ -2954,6 +2963,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_stat_slash.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_stat_t64.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_stat_t64_f64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_stderr_buffering.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_stdint.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_stprintf0_a.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_stprintf0_u.Po@am__quote@ # am--include-marker @@ -6257,6 +6267,13 @@ t_stat_t64_f64.log: t_stat_t64_f64$(EXEEXT) --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +t_stderr_buffering.log: t_stderr_buffering$(EXEEXT) + @p='t_stderr_buffering$(EXEEXT)'; \ + b='t_stderr_buffering'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) t_stdint.log: t_stdint$(EXEEXT) @p='t_stdint$(EXEEXT)'; \ b='t_stdint'; \ @@ -6822,6 +6839,7 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/t_stat_slash.Po -rm -f ./$(DEPDIR)/t_stat_t64.Po -rm -f ./$(DEPDIR)/t_stat_t64_f64.Po + -rm -f ./$(DEPDIR)/t_stderr_buffering.Po -rm -f ./$(DEPDIR)/t_stdint.Po -rm -f ./$(DEPDIR)/t_stprintf0_a.Po -rm -f ./$(DEPDIR)/t_stprintf0_u.Po @@ -7110,6 +7128,7 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/t_stat_slash.Po -rm -f ./$(DEPDIR)/t_stat_t64.Po -rm -f ./$(DEPDIR)/t_stat_t64_f64.Po + -rm -f ./$(DEPDIR)/t_stderr_buffering.Po -rm -f ./$(DEPDIR)/t_stdint.Po -rm -f ./$(DEPDIR)/t_stprintf0_a.Po -rm -f ./$(DEPDIR)/t_stprintf0_u.Po diff --git a/mingw-w64-crt/testcases/t_stderr_buffering.c b/mingw-w64-crt/testcases/t_stderr_buffering.c new file mode 100644 index 00000000..82938c24 --- /dev/null +++ b/mingw-w64-crt/testcases/t_stderr_buffering.c @@ -0,0 +1,58 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <process.h> +#include <io.h> +#include <fcntl.h> +#include <windows.h> + +#define STRING "hello world!" + +int main(int argc, char *argv[]) { + if (argc != 2 || strcmp(argv[1], "stderr_buffering_test") != 0) { + int exit_code; + int pipefd[2]; + int back_errfd; + intptr_t process; + ssize_t size; + char buf[512]; + + assert(_pipe(pipefd, sizeof(buf), _O_NOINHERIT) == 0); + + /* set stderr fd to write side of pipe, will be used by _spawnl() */ + assert((back_errfd = dup(STDERR_FILENO)) >= 0); + assert(dup2(pipefd[1], STDERR_FILENO) == 0); + assert(close(pipefd[1]) == 0); + + process = _spawnl(_P_NOWAIT, _pgmptr, argv[0], "stderr_buffering_test", NULL); + + /* revert back stderr fd */ + assert(dup2(back_errfd, STDERR_FILENO) == 0); + assert(close(back_errfd) == 0); + + assert(process != -1); + + size = read(pipefd[0], buf, sizeof(buf)); + close(pipefd[0]); + + /* wait until child process exits */ + assert(_cwait(&exit_code, process, _WAIT_CHILD) == process); + assert(exit_code == 0); + + assert(size > 0); /* some data were written by child process */ + assert(strncmp(buf, STRING, strlen (STRING)) == 0); + + return 0; + } + + /* stderr must not be fully-buffered on startup */ + fputs(STRING "\n", stderr); + assert(!ferror (stderr)); + + /* could also use _Exit here... */ + TerminateProcess(GetCurrentProcess(), 0); + + /* unreachable */ + assert(0); +} -- 2.49.0.windows.1
From 0fc043eb79cc55e303509077e07639545ba67a1c Mon Sep 17 00:00:00 2001 From: Luca Bacci <[email protected]> Date: Tue, 16 Dec 2025 15:32:03 +0100 Subject: [PATCH 2/3] crt: Fix comment Signed-off-by: Luca Bacci <[email protected]> --- mingw-w64-crt/crt/crtexe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mingw-w64-crt/crt/crtexe.c b/mingw-w64-crt/crt/crtexe.c index 94bad6aa..439d1aea 100644 --- a/mingw-w64-crt/crt/crtexe.c +++ b/mingw-w64-crt/crt/crtexe.c @@ -215,8 +215,8 @@ __tmainCRTStartup (void) if (ret != 0) _amsg_exit (8); /* _RT_SPACEARG */ - _initterm (__xc_a, __xc_z); - __main (); /* C++ initialization. */ + _initterm (__xc_a, __xc_z); /* C++ initialization */ + __main (); __native_startup_state = __initialized; } -- 2.49.0.windows.1
From 80886dc4cdf8295c391eac7fb3ba75af52a1b124 Mon Sep 17 00:00:00 2001 From: Luca Bacci <[email protected]> Date: Sat, 13 Dec 2025 14:22:58 +0100 Subject: [PATCH 3/3] crt: Ensure that stderr is not fully buffered CRT libraries other than the UCRT can open stderr in full-buffering mode. This happens for example when output goes to a pipe. The C standard disallow such mode on stderr (C23 7.23.3 pt. 7) [1]: > as initially opened, the standard error stream is not fully buffered Here we ensure that stderr is unbuffered. Note that we can't use line buffering since it's the same as full buffering [2]: > _IOLBF: For some systems this mode provides line buffering. However on Win32 the behavior is the same as _IOFBF - Full Buffering. References: 1. https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf#subsection.7.23.3 2. https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/setvbuf?view=msvc-170 3. https://sourceforge.net/p/mingw/mailman/message/27121137/ Signed-off-by: Luca Bacci <[email protected]> --- mingw-w64-crt/crt/crtexe.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mingw-w64-crt/crt/crtexe.c b/mingw-w64-crt/crt/crtexe.c index 439d1aea..03a5d083 100644 --- a/mingw-w64-crt/crt/crtexe.c +++ b/mingw-w64-crt/crt/crtexe.c @@ -10,6 +10,7 @@ #include <signal.h> #include <math.h> #include <stdlib.h> +#include <stdio.h> #include <tchar.h> #include <sect_attribs.h> #include <locale.h> @@ -215,6 +216,17 @@ __tmainCRTStartup (void) if (ret != 0) _amsg_exit (8); /* _RT_SPACEARG */ + /* Before the UCRT stderr could be opened in full buffering + * mode, for example when output goes to a pipe. + * + * The C standard disallow full buffering on stderr. Note + * that line buffering is the same as full buffering in the + * Windows CRT, so we have to disable buffering altogether. + */ + ret = setvbuf (stderr, NULL, _IONBF, 0); + if (ret != 0) + return 255; + _initterm (__xc_a, __xc_z); /* C++ initialization */ __main (); -- 2.49.0.windows.1
_______________________________________________ Mingw-w64-public mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mingw-w64-public
