unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Paul Eggert <eggert@cs.ucla.edu>
To: Eli Zaretskii <eliz@gnu.org>, Lars Ingebrigtsen <larsi@gnus.org>
Cc: daniele@grinta.net, emacs-devel@gnu.org
Subject: Re: `message' not outputting the newline "atomically"
Date: Wed, 26 Jun 2019 11:27:45 -0700	[thread overview]
Message-ID: <1d550142-8d66-485b-6796-981351718b53@cs.ucla.edu> (raw)
In-Reply-To: <83r27hlkix.fsf@gnu.org>

[-- Attachment #1: Type: text/plain, Size: 1342 bytes --]

Eli Zaretskii wrote:
> a version with a fixed-size automatic buffer and a loop using
> it to write the message in chunks, would be a much cleaner solution
> than allocating the buffer off the heap.

Yes, that would be an improvement. I see other issues with the current master. 
It doesn't fix output-line interleaving on AIX or Solaris in places where 
GNU/Linux works OK, as AIX and Solaris fprintf functions are less buffered 
(i.e., they commonly split Emacs output into 'write' calls that are smaller than 
a line), which means that common fprintf output can be interleaved on AIX and 
Solaris when it is not interleaved on GNU/Linux. Also, the current master 
mishandles the rare case where the string has INT_MAX or more bytes, and also 
mishandles line-interleaving if the string contains newlines.

Like Lars, I'm leery about temporarily trashing the representation of a Lisp 
string in order to do I/O. There will likely be other places where we need to do 
this sort of thing, but where the data are not in Lisp strings. It's not that 
hard to efficiently output even a very long Lisp string without modifying it, so 
let's do that.

Proposed patch attached. This defaults to line buffering on glibc as well as on 
AIX and Solaris, as I've verified that glibc buffers appropriately and have used 
it pretty extensively with Emacs.

[-- Attachment #2: 0001-Avoid-interleaving-stderr-lines.patch --]
[-- Type: text/x-patch, Size: 6114 bytes --]

From 9be73e5106262232a722cbe04fb8e387b1d3511d Mon Sep 17 00:00:00 2001
From: Paul Eggert <eggert@cs.ucla.edu>
Date: Wed, 26 Jun 2019 10:24:15 -0700
Subject: [PATCH] Avoid interleaving stderr lines

Do a better job of avoiding interleaved stderr lines.
The previous approach did not work on AIX or Solaris
because they issue multiple writes for common fprintf usages.
Also, it unnecessarily copied data for large strings,
unnecessarily used the heap, and mishandled the rare case
where the string contains INT_MAX or more bytes.
* src/sysdep.c (LINE_BUFFER_STDERR): New macro.
(init_standard_fds): Line-buffer stderr if LINE_BUFFER_STDERR says so.
(write_stderr): New function, that avoids interleaving stderr
lines when possible.
* src/pdumper.c (print_paths_to_root_1):
* src/xdisp.c (message_to_stderr, Ftrace_to_stderr): Use it.
---
 src/lisp.h    |  1 +
 src/pdumper.c |  3 +-
 src/sysdep.c  | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/xdisp.c   | 15 ++--------
 4 files changed, 81 insertions(+), 15 deletions(-)

diff --git a/src/lisp.h b/src/lisp.h
index 77fc22d118..836b0fcbb2 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4550,6 +4550,7 @@ maybe_disable_address_randomization (bool dumping, int argc, char **argv)
 extern ptrdiff_t emacs_write_sig (int, void const *, ptrdiff_t);
 extern ptrdiff_t emacs_write_quit (int, void const *, ptrdiff_t);
 extern void emacs_perror (char const *);
+extern void write_stderr (void const *, ptrdiff_t, bool);
 extern int renameat_noreplace (int, char const *, int, char const *);
 extern int str_collate (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object);
 extern void syms_of_sysdep (void);
diff --git a/src/pdumper.c b/src/pdumper.c
index c00f8a0af5..68fe4d8ed4 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -1405,8 +1405,7 @@ print_paths_to_root_1 (struct dump_context *ctx,
       Lisp_Object repr = Fprin1_to_string (referrer, Qnil);
       for (int i = 0; i < level; ++i)
         fputc (' ', stderr);
-      fwrite (SDATA (repr), 1, SBYTES (repr), stderr);
-      fputc ('\n', stderr);
+      write_stderr (SDATA (repr), SBYTES (repr), true);
       print_paths_to_root_1 (ctx, referrer, level + 1);
     }
 }
diff --git a/src/sysdep.c b/src/sysdep.c
index 4f89e8aba1..54352022eb 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -231,6 +231,18 @@ force_open (int fd, int flags)
     }
 }
 
+/* Line-buffered stderr works with glibc.  It is required in AIX and
+   Solaris for short single-line printfs to be atomic.  It does not
+   work with MS-Windows.  When in doubt, assume it does not work.
+   Override the default by compiling with -D LINE_BUFFER_STDERR=[0|1].  */
+#ifndef LINE_BUFFER_STDERR
+# if defined __GLIBC__ || defined _AIX || defined __sun
+#  define LINE_BUFFER_STDERR 1
+# else
+#  define LINE_BUFFER_STDERR 0
+# endif
+#endif
+
 /* Make sure stdin, stdout, and stderr are open to something, so that
    their file descriptors are not hijacked by later system calls.  */
 void
@@ -243,6 +255,9 @@ init_standard_fds (void)
   force_open (STDIN_FILENO, O_WRONLY);
   force_open (STDOUT_FILENO, O_RDONLY);
   force_open (STDERR_FILENO, O_RDONLY);
+
+  if (LINE_BUFFER_STDERR)
+    setvbuf (stderr, NULL, _IOLBF, 0);
 }
 
 /* Return the current working directory.  The result should be freed
@@ -2714,6 +2729,68 @@ emacs_perror (char const *message)
     }
   errno = err;
 }
+
+/* Write BUFFER (of size BUFLEN bytes) to standard error.
+   Follow it with a newline if NLFLAG is true.
+   Avoid interleaving output lines with those of other processes,
+   if the output lines contain fewer than PIPE_BUF bytes.  */
+void
+write_stderr (void const *buffer, ptrdiff_t buflen, bool nlflag)
+{
+#if LINE_BUFFER_STDERR
+  fwrite_unlocked (buffer, 1, buflen, stderr);
+  if (nlflag)
+    fputc_unlocked ('\n', stderr);
+#else
+
+  /* This platform is not known to support line-buffering.
+     Line-buffer by hand.  */
+
+  fflush_unlocked (stderr);
+  if (buflen <= -nlflag)
+    return;
+
+  #ifndef PIPE_BUF
+  enum { PIPE_BUF = 512 };
+  #endif
+  verify (PIPE_BUF <= SSIZE_MAX);
+
+  /* Write chunks each containing as many lines as fit into PIPE_BUF bytes.
+     If a line is too long to fit in PIPE_BUF, write it out piecemeal;
+     it might be interleaved by the OS but this is the best we can do.
+     Append a newline to the last chunk if NLFLAG.  */
+
+  while (true)
+    {
+      char const *buf = buffer;
+      ptrdiff_t n = buflen;
+      bool appendnl = nlflag;
+      if (PIPE_BUF < n + appendnl)
+	{
+	  char const *nladdr = memrchr (buf, '\n', PIPE_BUF);
+	  n = nladdr ? nladdr - buf + 1 : PIPE_BUF;
+	  appendnl = false;
+	}
+
+      verify (PIPE_BUF <= MAX_ALLOCA);
+      char stackbuf[PIPE_BUF];
+      if (appendnl)
+	{
+	  memcpy (stackbuf, buf, n);
+	  stackbuf[n++] = '\n';
+	  buf = stackbuf;
+	}
+
+      n = write (STDERR_FILENO, buf, n);
+      if (n < 0)
+	break;
+      buflen -= n;
+      if (buflen <= -nlflag)
+	break;
+      buffer = buf + n;
+    }
+#endif
+}
 \f
 /* Set the access and modification time stamps of FD (a.k.a. FILE) to be
    ATIME and MTIME, respectively.
diff --git a/src/xdisp.c b/src/xdisp.c
index 9f63ef4b18..4329b48cb6 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -10705,18 +10705,7 @@ 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);
-      }
+      write_stderr (SDATA (s), SBYTES (s), true);
     }
   else if (!cursor_in_echo_area)
     fputc ('\n', stderr);
@@ -19850,7 +19839,7 @@ DEFUN ("trace-to-stderr", Ftrace_to_stderr, Strace_to_stderr, 1, MANY, "",
   (ptrdiff_t nargs, Lisp_Object *args)
 {
   Lisp_Object s = Fformat (nargs, args);
-  fwrite (SDATA (s), 1, SBYTES (s), stderr);
+  write_stderr (SDATA (s), SBYTES (s), false);
   return Qnil;
 }
 
-- 
2.21.0


  parent reply	other threads:[~2019-06-26 18:27 UTC|newest]

Thread overview: 108+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-06-19 14:12 `message' not outputting the newline "atomically" Lars Ingebrigtsen
2019-06-19 14:28 ` Andreas Schwab
2019-06-19 15:41 ` Eli Zaretskii
2019-06-19 15:47   ` Lars Ingebrigtsen
2019-06-19 16:05     ` Andreas Schwab
2019-06-19 23:22       ` Paul Eggert
2019-06-20  2:35         ` Eli Zaretskii
2019-06-20  7:47           ` Paul Eggert
2019-06-20  9:35             ` Lars Ingebrigtsen
2019-06-20 12:52             ` Eli Zaretskii
2019-06-20 12:55               ` Lars Ingebrigtsen
2019-06-20 13:13                 ` Eli Zaretskii
2019-06-20 14:05                 ` Andreas Schwab
2019-06-20 16:26               ` Paul Eggert
2019-06-20 16:45                 ` Eli Zaretskii
2019-06-20 17:41                   ` Paul Eggert
2019-06-20 18:06                     ` Eli Zaretskii
2019-06-20 19:33                       ` Paul Eggert
2019-06-21  5:46                         ` Eli Zaretskii
2019-06-21  6:06                           ` Eli Zaretskii
2019-06-22  0:20                           ` Paul Eggert
2019-06-22  7:32                             ` Eli Zaretskii
2019-06-22 19:14                               ` Paul Eggert
2019-06-23  2:25                                 ` Eli Zaretskii
2019-06-23  8:34                                   ` Paul Eggert
2019-06-23 11:37                                     ` Lars Ingebrigtsen
2019-06-23 14:47                                     ` Eli Zaretskii
2019-06-23 17:32                                       ` Paul Eggert
2019-06-23 18:28                                         ` Eli Zaretskii
2019-06-23 12:53                                   ` Stefan Monnier
2019-06-23 14:51                                     ` Eli Zaretskii
2019-06-24  4:09                                       ` Stefan Monnier
2019-06-22  8:26                             ` Andreas Schwab
2019-06-22 18:53                               ` Paul Eggert
2019-06-22 19:00                                 ` Eli Zaretskii
2019-06-22 19:15                                   ` Paul Eggert
2019-06-22 19:48                                 ` Andreas Schwab
2019-06-20 13:32             ` Stefan Monnier
2019-06-20 16:28               ` Paul Eggert
2019-06-23 18:59                 ` Daniele Nicolodi
2019-06-23 20:34                   ` Paul Eggert
2019-06-23 20:42                     ` Lars Ingebrigtsen
2019-06-23 21:00                       ` Paul Eggert
2019-06-23 22:18                         ` Lars Ingebrigtsen
2019-06-23 20:48                     ` Daniele Nicolodi
2019-06-24  2:32                     ` Eli Zaretskii
2019-06-24  2:51                     ` HaiJun Zhang
2019-06-24 19:48 ` Lars Ingebrigtsen
2019-06-24 20:03   ` Daniele Nicolodi
2019-06-24 20:17     ` Lars Ingebrigtsen
2019-06-24 21:11       ` Paul Eggert
2019-06-24 21:33         ` Lars Ingebrigtsen
2019-06-24 22:03           ` Paul Eggert
2019-06-24 22:06             ` Paul Eggert
2019-06-24 22:28             ` Lars Ingebrigtsen
2019-06-24 22:47               ` Lars Ingebrigtsen
2019-06-25 16:03                 ` Eli Zaretskii
2019-06-26  9:15                   ` Lars Ingebrigtsen
2019-06-26 15:22                     ` Eli Zaretskii
2019-06-27 10:52                       ` Lars Ingebrigtsen
2019-06-26 18:27                   ` Paul Eggert [this message]
2019-06-26 18:41                     ` Eli Zaretskii
2019-06-26 18:58                       ` Paul Eggert
2019-06-26 19:11                         ` Eli Zaretskii
2019-06-26 19:36                           ` Daniele Nicolodi
2019-06-27  2:34                             ` Eli Zaretskii
2019-06-27  5:43                               ` Paul Eggert
2019-06-30 20:11                               ` Daniele Nicolodi
2019-07-01  7:41                               ` Daniele Nicolodi
2019-07-01 14:39                                 ` Eli Zaretskii
2019-07-01 17:01                                   ` Daniele Nicolodi
2019-07-02  2:28                                     ` Eli Zaretskii
2019-07-02  7:58                                       ` Daniele Nicolodi
2019-07-02 14:47                                         ` Eli Zaretskii
2019-07-02 20:56                                           ` Daniele Nicolodi
2019-07-03  5:23                                             ` Eli Zaretskii
2019-07-01 17:03                                   ` Daniele Nicolodi
2019-07-02  2:26                                     ` Eli Zaretskii
2019-06-26 19:38                           ` Paul Eggert
2019-06-25 16:06             ` Eli Zaretskii
2019-06-26  9:21               ` Lars Ingebrigtsen
2019-06-26 15:23                 ` Eli Zaretskii
2019-06-27 11:03                   ` Lars Ingebrigtsen
2019-06-27 13:31                     ` Eli Zaretskii
2019-06-28  8:30                       ` Lars Ingebrigtsen
2019-07-03  7:31                       ` Paul Eggert
2019-07-03  7:41                         ` Eli Zaretskii
2019-07-03  7:47                           ` Eli Zaretskii
2019-07-03  7:57                             ` Eli Zaretskii
2019-07-03  8:45                               ` Paul Eggert
2019-07-03  9:30                                 ` Eli Zaretskii
2019-07-03 23:08                                   ` Paul Eggert
2019-07-04 13:24                                     ` Eli Zaretskii
2019-07-07  1:16                                       ` Paul Eggert
2019-07-07 14:51                                         ` Eli Zaretskii
2019-07-08 22:35                                           ` Richard Copley
2019-07-09  2:33                                             ` Eli Zaretskii
2019-07-09 13:45                                               ` Richard Copley
2019-07-09 15:16                                                 ` Eli Zaretskii
2019-07-09  2:47                                           ` Paul Eggert
2019-07-09 16:39                                             ` Eli Zaretskii
2019-07-09 18:12                                               ` Paul Eggert
2019-07-09 18:32                                                 ` Eli Zaretskii
2019-07-09 18:44                                                   ` Lars Ingebrigtsen
2019-07-09 19:17                                                     ` Eli Zaretskii
2019-07-14  0:42                                                   ` Paul Eggert
2019-07-14  6:01                                                     ` Eli Zaretskii
2019-06-25 16:08         ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1d550142-8d66-485b-6796-981351718b53@cs.ucla.edu \
    --to=eggert@cs.ucla.edu \
    --cc=daniele@grinta.net \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    --cc=larsi@gnus.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).