[PATCH 0/1] MR10199: msvcrt: Fix no buffering detection in _fwrite_nolock().
From: Paul Gofman <pgofman@codeweavers.com> --- dlls/msvcrt/file.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dlls/msvcrt/file.c b/dlls/msvcrt/file.c index 01853625c23..c096e821b0b 100644 --- a/dlls/msvcrt/file.c +++ b/dlls/msvcrt/file.c @@ -4147,10 +4147,15 @@ size_t CDECL fwrite(const void *ptr, size_t size, size_t nmemb, FILE* file) size_t CDECL _fwrite_nolock(const void *ptr, size_t size, size_t nmemb, FILE* file) { size_t wrcnt=size * nmemb; + BOOL no_buffer = FALSE; int written = 0; if (size == 0) return 0; + if(!(file->_flag & (MSVCRT__NOBUF | _IOMYBUF | MSVCRT__USERBUF))) + no_buffer = !msvcrt_alloc_buffer(file); + no_buffer = no_buffer || (file->_flag & MSVCRT__NOBUF); + while(wrcnt) { if(file->_cnt < 0) { WARN("negative file->_cnt value in %p\n", file); @@ -4164,13 +4169,13 @@ size_t CDECL _fwrite_nolock(const void *ptr, size_t size, size_t nmemb, FILE* fi written += pcnt; wrcnt -= pcnt; ptr = (const char*)ptr + pcnt; - } else if((file->_flag & MSVCRT__NOBUF) + } else if(no_buffer || ((file->_flag & (_IOMYBUF | MSVCRT__USERBUF)) && wrcnt >= file->_bufsiz) || (!(file->_flag & (_IOMYBUF | MSVCRT__USERBUF)) && wrcnt >= MSVCRT_INTERNAL_BUFSIZ)) { size_t pcnt; int bufsiz; - if(file->_flag & MSVCRT__NOBUF) + if(no_buffer) bufsiz = 1; else if(!(file->_flag & (_IOMYBUF | MSVCRT__USERBUF))) bufsiz = MSVCRT_INTERNAL_BUFSIZ; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10199
When _fwrite_nolock() decides that buffered output is used but the amount of data is not big enough to flush buffer and write directly, it relays buffered write to _flsbuf, byte by byte. The assumptions when buffering is disabled in _fwrite_nolock() disagree with those in _flsbuf. There are addiotional checks in msvcrt_alloc_buffer() which, in particular, auto-disable buffering for STDOUT or TTY file. That results in the app's output in those cases to be written byte by byte, which is especially unfortunate when the output handle is console and each single output roundtrip is huge. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10199#note_130659
The changes are looking OK to me but I think it can be simplified by moving `bufsiz` calculation before the loop, I'm thinking about something along these lines: ```c int bufsize = !(file->_flag & (MSVCRT__NOBUF | _IOMYBUF | MSVCRT__USERBUF)) && !msvcrt_alloc_buffer(file) ? 1 : file->_bufsiz; ``` and the if condition in the loop can be changed to: ```c } else if(wrcnt >= bufsiz) { ``` It was not tested so I might be missing something. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10199#note_130704
_IOMYBUF / MSVCRT__NOBUF may be set inside msvcrt_alloc_buffer(), so probably this won't work as is. But I will try to figure something nicer with those flags and buf sizes. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10199#note_130727
On Fri Feb 27 16:24:33 2026 +0000, Paul Gofman wrote:
_IOMYBUF / MSVCRT__NOBUF may be set inside msvcrt_alloc_buffer(), so probably this won't work as is. But I will try to figure something nicer with those flags and buf sizes. Yes, but when it sets IOMYBUF or NOBUF (and returns TRUE) it also sets file-\>\_bufsiz. I don't see anything wrong in proposed change.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10199#note_130732
participants (3)
-
Paul Gofman -
Paul Gofman (@gofman) -
Piotr Caban (@piotr)