>From 560b8b7cc2ad44f2d2a6e8d43432ed8b28c14e7e Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Tue, 2 Jul 2019 23:44:12 -0700 Subject: [PATCH 2/4] Avoid interleaving stderr in most of C-code Emacs To avoid interleaving stderr messages with those of other processes, line-buffer C-level diagnostics separately where this is safe, i.e., when various parts of a diagnostic are output right away. This change does not affect stderr buffering; that is, the stderr stream still uses its default buffering, which is typically unbuffered (although POSIX allows line buffering). * src/pdumper.c (print_paths_to_root_1): Use errwrite instead of fwrite to stderr; this avoids interleaving. * src/sysdep.c (buferr): New static var. (init_standard_fds) [!DOS_NT]: Initialize it. (errstream): New function. (errputc, errputs, verrprintf): Use it. (errwrite): New function. * src/xdisp.c (message_to_stderr): Use errwrite instead of fwrite to stderr; this avoids interleaving in a simpler and better way than allocating a temporary heap buffer. * src/xdisp.c (vmessage): Simplify; make it more like message_to_stderr. --- src/pdumper.c | 2 +- src/sysdep.c | 49 ++++++++++++++++++++++++++++++++++++++++++++----- src/sysstdio.h | 1 + src/xdisp.c | 27 +++++++-------------------- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/src/pdumper.c b/src/pdumper.c index bfa0dba7f0..ff092e2e49 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -1404,7 +1404,7 @@ print_paths_to_root_1 (struct dump_context *ctx, referrers = XCDR (referrers); Lisp_Object repr = Fprin1_to_string (referrer, Qnil); errprintf ("%*s", level, ""); - fwrite (SDATA (repr), 1, SBYTES (repr), stderr); + errwrite (SDATA (repr), SBYTES (repr)); errputc ('\n'); print_paths_to_root_1 (ctx, referrer, level + 1); } diff --git a/src/sysdep.c b/src/sysdep.c index beccc3c537..2bd7f1afd2 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -231,8 +231,13 @@ force_open (int fd, int flags) } } +/* A stream that is like stderr, except line buffered when possible. + It is NULL during startup or if initializing it failed. */ +static FILE *buferr; + /* Make sure stdin, stdout, and stderr are open to something, so that - their file descriptors are not hijacked by later system calls. */ + their file descriptors are not hijacked by later system calls. + Initialize buferr too. */ void init_standard_fds (void) { @@ -243,6 +248,15 @@ init_standard_fds (void) force_open (STDIN_FILENO, O_WRONLY); force_open (STDOUT_FILENO, O_RDONLY); force_open (STDERR_FILENO, O_RDONLY); + +#ifndef DOS_NT /* _IOLBF does not work on MS-Windows. */ + buferr = fdopen (STDERR_FILENO, "w"); + if (buferr && setvbuf (buferr, NULL, _IOLBF, 0) != 0) + { + fclose (buferr); + buferr = NULL; + } +#endif } /* Return the current working directory. The result should be freed @@ -2767,19 +2781,38 @@ safe_strsignal (int code) return signame; } + +/* Return the error output stream. */ -/* Output to stderr. */ +static FILE * +errstream (void) +{ + FILE *err = buferr; + if (!err) + return stderr; + fflush_unlocked (stderr); + return err; +} + +/* These functions are like fputc, fputs, fprintf, vfprintf, and + fwrite, except that they output to stderr and buffer better on + platforms that support line buffering. This avoids interleaving + output when Emacs and other processes write to stderr + simultaneously, so long as the lines are short enough. When a + single diagnostic is emitted via a sequence of calls of one or more + of these functions, the caller should arrange for the last called + function to output a newline at the end. */ void errputc (int c) { - fputc_unlocked (c, stderr); + fputc_unlocked (c, errstream ()); } void errputs (char const *str) { - fputs_unlocked (str, stderr); + fputs_unlocked (str, errstream ()); } void @@ -2794,7 +2827,13 @@ errprintf (char const *fmt, ...) void verrprintf (char const *fmt, va_list ap) { - vfprintf (stderr, fmt, ap); + vfprintf (errstream (), fmt, ap); +} + +void +errwrite (void const *buf, ptrdiff_t nbuf) +{ + fwrite_unlocked (buf, 1, nbuf, errstream ()); } #ifndef DOS_NT diff --git a/src/sysstdio.h b/src/sysstdio.h index ebe0845d40..87568c86fd 100644 --- a/src/sysstdio.h +++ b/src/sysstdio.h @@ -29,6 +29,7 @@ #define EMACS_SYSSTDIO_H extern void errputs (char const *); extern void errprintf (char const *, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2); extern void verrprintf (char const *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0); +extern void errwrite (void const *, ptrdiff_t); #if O_BINARY # define FOPEN_BINARY "b" diff --git a/src/xdisp.c b/src/xdisp.c index 4dbb183bd5..12ab7d9c03 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -10727,23 +10727,10 @@ message_to_stderr (Lisp_Object m) else s = m; - /* We want to write this out with a single fwrite call so that - output doesn't interleave with other processes writing to - stderr at the same time. */ - { - int length = min (INT_MAX, SBYTES (s) + 1); - char *string = xmalloc (length); - - memcpy (string, SSDATA (s), length - 1); - string[length - 1] = '\n'; - fwrite (string, 1, length, stderr); - xfree (string); - } + errwrite (SDATA (s), SBYTES (s)); } - else if (!cursor_in_echo_area) + if (STRINGP (m) || !cursor_in_echo_area) errputc ('\n'); - - fflush (stderr); } /* The non-logging version of message3. @@ -10888,12 +10875,12 @@ vmessage (const char *m, va_list ap) if (m) { if (noninteractive_need_newline) - errputc ('\n'); - noninteractive_need_newline = false; + { + noninteractive_need_newline = false; + errputc ('\n'); + } verrprintf (m, ap); - if (!cursor_in_echo_area) - errputc ('\n'); - fflush (stderr); + errputc ('\n'); } } else if (INTERACTIVE) -- 2.21.0