Re: [PATCH 1/1] vreportf(): avoid buffered write in favor of unbuffered one
- From
Johannes Schindelin <johannes.schindelin@gmx.de>
- Date
- Oct 29, 2019, 12:30 UTC
- Message-ID
- <nycvar.QRO.7.76.6.1910291222500.46@tvgsbejvaqbjf.bet>
- In-Reply-To
- <xmqqeeyw6xyr.fsf@gitster-ct.c.googlers.com>
Hi Junio,
On Tue, 29 Oct 2019, Junio C Hamano wrote:
Show 8 quoted lines
> "Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com> > writes: > > > Also please note that we `fflush(stderr)` here to help when running in a > > Git Bash on Windows: in this case, `stderr` is not actually truly > > unbuffered, and needs the extra help. > > Yuck. So on all systems, vreportf() now totally bypasses stdio?
Yep ;-)
Show 5 quoted lines
> Also, this is only to help output from us that goes via vreportf() and > other codepaths in us that use stdio to write to the standard error > stream can still get mixed on Windows (I think the answer is yes, > because we wouldn't need fflush() in this patch if we are covering all > writes to the standard error stream)?
Yes, `write()` can get interrupted, so there is still a slight chance of interleaving.
However, with `fprintf()`, apparently the MSVC runtime essentially writes and flushes one character at a time, which will make it _much_ more likely that two competing processes write interleaved messages to `stderr`.
Show 8 quoted lines
> By the way, I'd retitle the patch to highlight that we are still doing > buffered write, if I were doing this topic. We are just avoiding some > implementations of stdio that do not give us buffering and doing the > buffering ourselves. > > Subject: vreportf(): don't rely on stdio buffering > > or something like that.
Good idea.
Show 21 quoted lines
> > Co-authored-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> > ---
> > usage.c | 12 +++++++++---
> > 1 file changed, 9 insertions(+), 3 deletions(-)
> >
> > diff --git a/usage.c b/usage.c
> > index 2fdb20086b..4328894dce 100644
> > --- a/usage.c
> > +++ b/usage.c
> > @@ -10,13 +10,19 @@ void vreportf(const char *prefix, const char *err, va_list params)
> > {
> > char msg[4096];
> > char *p;
> > -
> > - vsnprintf(msg, sizeof(msg), err, params);
> > + size_t off = strlcpy(msg, prefix, sizeof(msg));
>
> Like snprintf(3) the strlcpy() and strlcat() functions return the
> total length of the string they tried to create. For strlcpy() that
> means the length of src.True (I misread `compat/strlcpy.c` and forgot to consult the documentation). This length can be longer than `msg`, of course.
> So "off" would be strlen(prefix), which could be longer than > sizeof(msg)? Then what happens to this vsnprintf() we see below?
A problem.
I `git grep`ed and saw that only very short `prefix`es are hard-coded. So that is a hypothetical concern.
However, Alex also indicated his discomfort with this, so I will change the code to account for a `prefix` that is too long (the entire error message will be clipped away in that case, which is unfortunate, but to be expected).
Show 12 quoted lines
> > + int ret = vsnprintf(msg + off, sizeof(msg) - off, err, params);
> > for (p = msg; *p; p++) {
> > if (iscntrl(*p) && *p != '\t' && *p != '\n')
> > *p = '?';
> > }
> > - fprintf(stderr, "%s%s\n", prefix, msg);
>
> Strictly speaking this is a breaking change in that control
> sequences in prefix are now clobbered. Does any caller call this
> function with prefix like "^M\033[K<some string>" to overwrite the
> last output line with the new message? If not, then probably we do
> not have to worry about it (and reusing msg[] does feel attractive).Such a sequence would not exactly be a prefix, but okay, I changed the code to replace only characters in the non-prefix part. For good measure, I also detect `NUL`s in that part and shorten `ret` in that case (think `die("This was an\0unintentional NUL")`).
Thanks for the review! Dscho
Show 11 quoted lines
> > + if (ret > 0) {
> > + if (off + ret > sizeof(msg) - 1)
> > + ret = sizeof(msg) - 1 - off;
> > + msg[off + ret] = '\n'; /* we no longer need a NUL */
> > + fflush(stderr);
> > + xwrite(2, msg, off + ret + 1);
> > + }
> > }
> >
> > static NORETURN void usage_builtin(const char *err, va_list params)
>