public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [Patch, libfortran] Thread safety and simplification of error printing
@ 2011-05-07 17:07 Janne Blomqvist
  2011-05-08 13:44 ` Janne Blomqvist
  0 siblings, 1 reply; 9+ messages in thread
From: Janne Blomqvist @ 2011-05-07 17:07 UTC (permalink / raw)
  To: Fortran List, GCC Patches

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

Hi,

the error printing functionality (in io/unix.c) st_printf and
st_vprintf are not thread-safe as they use a static buffer. However,
since these routines are used when something has gone wrong, we
shouldn't use malloc() to allocate thread-specific buffers or anything
fancy like that. The way the buffer is used is that it is filled with
vs(n)printf() and then the buffer is written using write(); the
simplest way to fix this is to just print directly using vfprintf(),
which is what this patch does. Also, the patch uses
flockfile/funlockfile in a few places to increase the likelihood of
multiple related messages to end up together on the output.

As stderr is unbuffered and stdout is buffered, this patch also gets
rid of the functionality to choose where to send the error output, as
we don't want to deal with buffering in the error handling path. IMHO
this is no loss, as all targets/shells/batch schedulers/ etc. where
this might be relevant, offer functionality to redirect stderr to
wherever stdout goes. So there is no need for a gfortran-specific
mechanism for this.

Regtested on x86_64-unknown-linux-gnu, Ok for trunk?

2011-05-07  Janne Blomqvist  <jb@gcc.gnu.org>

        * gfortran.texi: Remove GFORTRAN_USE_STDERR documentation.

2011-05-07  Janne Blomqvist  <jb@gcc.gnu.org>

        * config.h.in: Regenerated.
        * configure: Likewise.
        * configure.ac: Check for flockfile, funlockfile.
        * io/unix.c (st_vprintf): Remove.
        (st_printf): Use vfprintf() instead of st_vprintf.
        * libgfortran.h (flockfile,funlockfile): Define to empty if
        functions not available.
        (struct options_t): Remove use_stderr field.
        (st_vprintf): Remove prototype.
        * runtime/environ.c (variable_table[]): Remove GFORTRAN_USE_STDERR
        entry.
        * runtime/error.c (runtime_error): Use vfprintf instead of
        st_vprintf.
        (runtime_error_at): Likewise.
        (runtime_warning_at): Likewise.


-- 
Janne Blomqvist

[-- Attachment #2: st_printf.diff --]
[-- Type: text/x-patch, Size: 8071 bytes --]

diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi
index 1284c3d..c810fe2 100644
--- a/gcc/fortran/gfortran.texi
+++ b/gcc/fortran/gfortran.texi
@@ -579,7 +579,6 @@ Malformed environment variables are silently ignored.
 * GFORTRAN_STDIN_UNIT:: Unit number for standard input
 * GFORTRAN_STDOUT_UNIT:: Unit number for standard output
 * GFORTRAN_STDERR_UNIT:: Unit number for standard error
-* GFORTRAN_USE_STDERR:: Send library output to standard error
 * GFORTRAN_TMPDIR:: Directory for scratch files
 * GFORTRAN_UNBUFFERED_ALL:: Don't buffer I/O for all units.
 * GFORTRAN_UNBUFFERED_PRECONNECTED:: Don't buffer I/O for preconnected units.
@@ -613,14 +612,6 @@ This environment variable can be used to select the unit number
 preconnected to standard error.  This must be a positive integer.
 The default value is 0.
 
-@node GFORTRAN_USE_STDERR
-@section @env{GFORTRAN_USE_STDERR}---Send library output to standard error
-
-This environment variable controls where library output is sent.
-If the first letter is @samp{y}, @samp{Y} or @samp{1}, standard
-error is used.  If the first letter is @samp{n}, @samp{N} or
-@samp{0}, standard output is used.
-
 @node GFORTRAN_TMPDIR
 @section @env{GFORTRAN_TMPDIR}---Directory for scratch files
 
diff --git a/libgfortran/config.h.in b/libgfortran/config.h.in
index 30da5fa..a48381d 100644
--- a/libgfortran/config.h.in
+++ b/libgfortran/config.h.in
@@ -393,6 +393,9 @@
 /* Define to 1 if you have the <float.h> header file. */
 #undef HAVE_FLOAT_H
 
+/* Define to 1 if you have the `flockfile' function. */
+#undef HAVE_FLOCKFILE
+
 /* libm includes floor */
 #undef HAVE_FLOOR
 
@@ -441,6 +444,9 @@
 /* Define to 1 if you have the `ftruncate' function. */
 #undef HAVE_FTRUNCATE
 
+/* Define to 1 if you have the `funlockfile' function. */
+#undef HAVE_FUNLOCKFILE
+
 /* Define to 1 if you have the `getcwd' function. */
 #undef HAVE_GETCWD
 
diff --git a/libgfortran/configure b/libgfortran/configure
index 833a0e1..9fa2b7f 100755
--- a/libgfortran/configure
+++ b/libgfortran/configure
@@ -16458,7 +16458,7 @@ _ACEOF
 fi
 done
 
-for ac_func in clock_gettime strftime
+for ac_func in clock_gettime strftime flockfile funlockfile
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
diff --git a/libgfortran/configure.ac b/libgfortran/configure.ac
index cf38fb0..e39ae17 100644
--- a/libgfortran/configure.ac
+++ b/libgfortran/configure.ac
@@ -264,7 +264,7 @@ AC_CHECK_FUNCS(sleep time ttyname signal alarm clock access fork execl)
 AC_CHECK_FUNCS(wait setmode execvp pipe dup2 close fdopen strcasestr getrlimit)
 AC_CHECK_FUNCS(gettimeofday stat fstat lstat getpwuid vsnprintf dup getcwd)
 AC_CHECK_FUNCS(localtime_r gmtime_r strerror_r getpwuid_r ttyname_r)
-AC_CHECK_FUNCS(clock_gettime strftime)
+AC_CHECK_FUNCS(clock_gettime strftime flockfile funlockfile)
 
 # Check for glibc backtrace functions
 AC_CHECK_FUNCS(backtrace backtrace_symbols)
diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c
index 4e4bc3b..3b80e36 100644
--- a/libgfortran/io/unix.c
+++ b/libgfortran/io/unix.c
@@ -1353,48 +1353,7 @@ error_stream (void)
 }
 
 
-/* st_vprintf()-- vprintf function for error output.  To avoid buffer
-   overruns, we limit the length of the buffer to ST_VPRINTF_SIZE.  2k
-   is big enough to completely fill a 80x25 terminal, so it shuld be
-   OK.  We use a direct write() because it is simpler and least likely
-   to be clobbered by memory corruption.  Writing an error message
-   longer than that is an error.  */
-
-#define ST_VPRINTF_SIZE 2048
-
-int
-st_vprintf (const char *format, va_list ap)
-{
-  static char buffer[ST_VPRINTF_SIZE];
-  int written;
-  int fd;
-
-  fd = options.use_stderr ? STDERR_FILENO : STDOUT_FILENO;
-#ifdef HAVE_VSNPRINTF
-  written = vsnprintf(buffer, ST_VPRINTF_SIZE, format, ap);
-#else
-  written = vsprintf(buffer, format, ap);
-
-  if (written >= ST_VPRINTF_SIZE-1)
-    {
-      /* The error message was longer than our buffer.  Ouch.  Because
-	 we may have messed up things badly, report the error and
-	 quit.  */
-#define ERROR_MESSAGE "Internal error: buffer overrun in st_vprintf()\n"
-      write (fd, buffer, ST_VPRINTF_SIZE-1);
-      write (fd, ERROR_MESSAGE, strlen(ERROR_MESSAGE));
-      sys_exit(2);
-#undef ERROR_MESSAGE
-
-    }
-#endif
-
-  written = write (fd, buffer, written);
-  return written;
-}
-
-/* st_printf()-- printf() function for error output.  This just calls
-   st_vprintf() to do the actual work.  */
+/* st_printf()-- printf() function for error output.  */
 
 int
 st_printf (const char *format, ...)
@@ -1402,7 +1361,7 @@ st_printf (const char *format, ...)
   int written;
   va_list ap;
   va_start (ap, format);
-  written = st_vprintf(format, ap);
+  written = vfprintf (stderr, format, ap);
   va_end (ap);
   return written;
 }
diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h
index 6cccaca..ca1f462 100644
--- a/libgfortran/libgfortran.h
+++ b/libgfortran/libgfortran.h
@@ -125,6 +125,13 @@ extern int __mingw_snprintf (char *, size_t, const char *, ...)
 #define snprintf(str, size, ...) sprintf (str, __VA_ARGS__)
 #endif
 
+#ifndef HAVE_FLOCKFILE
+#define flockfile(fhandle)
+#endif
+
+#ifndef HAVE_FUNLOCKFILE
+#define funlockfile(fhandle)
+#endif
 
 /* For a library, a standard prefix is a requirement in order to partition
    the namespace.  IPREFIX is for symbols intended to be internal to the
@@ -508,7 +515,7 @@ typedef struct
   int separator_len;
   const char *separator;
 
-  int use_stderr, all_unbuffered, unbuffered_preconnected, default_recl;
+  int all_unbuffered, unbuffered_preconnected, default_recl;
   int fpe, dump_core, backtrace;
 }
 options_t;
@@ -796,9 +803,6 @@ extern int st_printf (const char *, ...)
   __attribute__ ((format (gfc_printf, 1, 2)));
 internal_proto(st_printf);
 
-extern int st_vprintf (const char *, va_list);
-internal_proto(st_vprintf);
-
 extern char * filename_from_unit (int);
 internal_proto(filename_from_unit);
 
diff --git a/libgfortran/runtime/environ.c b/libgfortran/runtime/environ.c
index a6ce645..d70103c 100644
--- a/libgfortran/runtime/environ.c
+++ b/libgfortran/runtime/environ.c
@@ -281,10 +281,6 @@ static variable variable_table[] = {
    "Unit number that will be preconnected to standard error\n"
    "(No preconnection if negative)", 0},
 
-  {"GFORTRAN_USE_STDERR", 1, &options.use_stderr, init_boolean,
-   show_boolean,
-   "Sends library output to standard error instead of standard output.", 0},
-
   {"GFORTRAN_TMPDIR", 0, NULL, init_string, show_string,
    "Directory for scratch files.  Overrides the TMP environment variable\n"
    "If TMP is not set " DEFAULT_TEMPDIR " is used.", 0},
diff --git a/libgfortran/runtime/error.c b/libgfortran/runtime/error.c
index 06c144a..694d668 100644
--- a/libgfortran/runtime/error.c
+++ b/libgfortran/runtime/error.c
@@ -249,11 +249,13 @@ runtime_error (const char *message, ...)
   va_list ap;
 
   recursion_check ();
+  flockfile (stderr);
   st_printf ("Fortran runtime error: ");
   va_start (ap, message);
-  st_vprintf (message, ap);
+  vfprintf (stderr, message, ap);
   va_end (ap);
   st_printf ("\n");
+  funlockfile (stderr);
   sys_exit (2);
 }
 iexport(runtime_error);
@@ -267,12 +269,14 @@ runtime_error_at (const char *where, const char *message, ...)
   va_list ap;
 
   recursion_check ();
+  flockfile (stderr);
   st_printf ("%s\n", where);
   st_printf ("Fortran runtime error: ");
   va_start (ap, message);
-  st_vprintf (message, ap);
+  vfprintf (stderr, message, ap);
   va_end (ap);
   st_printf ("\n");
+  funlockfile (stderr);
   sys_exit (2);
 }
 iexport(runtime_error_at);
@@ -283,12 +287,14 @@ runtime_warning_at (const char *where, const char *message, ...)
 {
   va_list ap;
 
+  flockfile (stderr);
   st_printf ("%s\n", where);
   st_printf ("Fortran runtime warning: ");
   va_start (ap, message);
-  st_vprintf (message, ap);
+  vfprintf (stderr, message, ap);
   va_end (ap);
   st_printf ("\n");
+  funlockfile (stderr);
 }
 iexport(runtime_warning_at);
 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch, libfortran] Thread safety and simplification of error printing
  2011-05-07 17:07 [Patch, libfortran] Thread safety and simplification of error printing Janne Blomqvist
@ 2011-05-08 13:44 ` Janne Blomqvist
  2011-05-08 17:35   ` N.M. Maclaren
  2011-05-08 23:01   ` Janne Blomqvist
  0 siblings, 2 replies; 9+ messages in thread
From: Janne Blomqvist @ 2011-05-08 13:44 UTC (permalink / raw)
  To: Fortran List, GCC Patches

On Sat, May 7, 2011 at 19:35, Janne Blomqvist <blomqvist.janne@gmail.com> wrote:
> Hi,
>
> the error printing functionality (in io/unix.c) st_printf and
> st_vprintf are not thread-safe as they use a static buffer. However,
> since these routines are used when something has gone wrong, we
> shouldn't use malloc() to allocate thread-specific buffers or anything
> fancy like that. The way the buffer is used is that it is filled with
> vs(n)printf() and then the buffer is written using write(); the
> simplest way to fix this is to just print directly using vfprintf(),
> which is what this patch does. Also, the patch uses
> flockfile/funlockfile in a few places to increase the likelihood of
> multiple related messages to end up together on the output.
>
> As stderr is unbuffered and stdout is buffered, this patch also gets
> rid of the functionality to choose where to send the error output, as
> we don't want to deal with buffering in the error handling path. IMHO
> this is no loss, as all targets/shells/batch schedulers/ etc. where
> this might be relevant, offer functionality to redirect stderr to
> wherever stdout goes. So there is no need for a gfortran-specific
> mechanism for this.

While this patch makes error printing thread-safe, it's no longer
async-signal-safe as the stderr lock might lead to a deadlock. So I'm
retracting this patch and thinking some more about this problem.



-- 
Janne Blomqvist

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch, libfortran] Thread safety and simplification of error printing
  2011-05-08 13:44 ` Janne Blomqvist
@ 2011-05-08 17:35   ` N.M. Maclaren
  2011-05-08 22:50     ` N.M. Maclaren
  2011-05-09  2:31     ` Janne Blomqvist
  2011-05-08 23:01   ` Janne Blomqvist
  1 sibling, 2 replies; 9+ messages in thread
From: N.M. Maclaren @ 2011-05-08 17:35 UTC (permalink / raw)
  To: Fortran List, GCC Patches

On May 8 2011, Janne Blomqvist wrote:
>>
>> the error printing functionality (in io/unix.c) st_printf and
>> st_vprintf are not thread-safe as they use a static buffer. ...
>
>While this patch makes error printing thread-safe, it's no longer
>async-signal-safe as the stderr lock might lead to a deadlock. So I'm
>retracting this patch and thinking some more about this problem.

It's theoretically insoluble, given the constraints you are working
under.  Sorry.  It is possible to do reasonably well, but there will
always be likely scenarios where all you can do is to say "Aargh!
I give up."

Both I and the VMS people adopted the ratchet design.  You have N
levels of error recovery, each level allocates all of the resources
it needs before startup, and any exception during level K increases
the level to K+1 and calls the level K+1 error handler.  When you
have an exception at level N, you just die.

That imposes the constraint that all diagnostics have a fixed upper
bound on the resources they need (not just buffer space, but that's
the main one).  It's a real bummer when the system has some critical
resources that you can't reserve, so you have to treat an allocation
failure as an exception, but buffer space is not one such.

That also tackles the thread problem, not very satisfactorily.  If a
resource needs to be locked, you can try to get it for a bit, and
then raise a higher exception if you can't.  And, typically, one or
more of the highest levels are for closing down the process, and
simply suspend any subsequent threads that call them (i.e. just leave
them waiting for a lock they won't get).

Yes, it's not pretty.  But I don't know how to do better.

Regards,
Nick Maclaren.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch, libfortran] Thread safety and simplification of error printing
  2011-05-08 17:35   ` N.M. Maclaren
@ 2011-05-08 22:50     ` N.M. Maclaren
  2011-05-09  2:31     ` Janne Blomqvist
  1 sibling, 0 replies; 9+ messages in thread
From: N.M. Maclaren @ 2011-05-08 22:50 UTC (permalink / raw)
  To: Fortran List, GCC Patches

Sorry - I should have clarified that ANYTHING that can't be used 
independently in multiple threads and at multiple levels in the same thread 
counts as a resource, and that includes stderr.

Regards,
Nick Maclaren.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch, libfortran] Thread safety and simplification of error printing
  2011-05-08 13:44 ` Janne Blomqvist
  2011-05-08 17:35   ` N.M. Maclaren
@ 2011-05-08 23:01   ` Janne Blomqvist
  2011-05-13 11:00     ` Janne Blomqvist
  2011-05-14 10:26     ` Steve Kargl
  1 sibling, 2 replies; 9+ messages in thread
From: Janne Blomqvist @ 2011-05-08 23:01 UTC (permalink / raw)
  To: Fortran List, GCC Patches

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

On Sun, May 8, 2011 at 14:40, Janne Blomqvist <blomqvist.janne@gmail.com> wrote:
> On Sat, May 7, 2011 at 19:35, Janne Blomqvist <blomqvist.janne@gmail.com> wrote:
>> Hi,
>>
>> the error printing functionality (in io/unix.c) st_printf and
>> st_vprintf are not thread-safe as they use a static buffer. However,
>> since these routines are used when something has gone wrong, we
>> shouldn't use malloc() to allocate thread-specific buffers or anything
>> fancy like that. The way the buffer is used is that it is filled with
>> vs(n)printf() and then the buffer is written using write(); the
>> simplest way to fix this is to just print directly using vfprintf(),
>> which is what this patch does. Also, the patch uses
>> flockfile/funlockfile in a few places to increase the likelihood of
>> multiple related messages to end up together on the output.
>>
>> As stderr is unbuffered and stdout is buffered, this patch also gets
>> rid of the functionality to choose where to send the error output, as
>> we don't want to deal with buffering in the error handling path. IMHO
>> this is no loss, as all targets/shells/batch schedulers/ etc. where
>> this might be relevant, offer functionality to redirect stderr to
>> wherever stdout goes. So there is no need for a gfortran-specific
>> mechanism for this.
>
> While this patch makes error printing thread-safe, it's no longer
> async-signal-safe as the stderr lock might lead to a deadlock. So I'm
> retracting this patch and thinking some more about this problem.

So here is an updated patch. Compared to the status quo the main
differences are that it uses a stack allocated buffer to do the
formatting instead of a static one. This prevents conflicts from
multiple threads and in contrast to the approach in the previous patch
should also be async-signal-safe (or at least, less likely to go
belly-up when called from within a signal handler than using
vfprintf() directly). It also introduces a new function to write a
string to standard error without any formatting (estr_write), which is
simpler and less likely to cause problems than the general purpose
vs(n)printf() used before. This can be used in lieu of st_printf in
the majority of cases where no formatting is done.

Regtested on x86_64-unknown-linux-gnu, Ok for trunk?

frontend ChangeLog:

2011-05-08  Janne Blomqvist  <jb@gcc.gnu.org>

	* gfortran.texi: Remove GFORTRAN_USE_STDERR documentation.


library ChangeLog:

2011-05-08  Janne Blomqvist  <jb@gcc.gnu.org>

	* io/unix.c (st_vprintf,st_printf): Move to runtime/error.c.
	* libgfortran.h (struct options_t): Remove use_stderr field.
	(st_vprintf,st_printf): Move prototypes.
	(estr_write): New prototype.
	* runtime/error.c (sys_exit): Use estr_write instead of st_printf.
	(estr_write): New function.
	(st_vprintf): Move from io/unix.c, use stack allocated buffer,
	always output to stderr.
	(st_printf): Move from io/unix.c.
	(show_locus): Use a local variable instead of static.
	(os_error): Use estr_write instead of st_printf.
	(runtime_error): Likewise.
	(runtime_error_at): Likewise.
	(runtime_warning_at): Likewise.
	(internal_error): Likewise.
	(generate_error): Likewise.
	(generate_warning): Likewise.
	(notify_std): Likewise.
	* runtime/pause.c (do_pause): Likewise.
	(pause_string): Likewise.
	* runtime/stop.c (stop_string): Likewise.
	(error_stop_string): Likewise.
	* config/fpu_aix.h (set_fpu): Likewise.
	* config/fpu_generic.h (set_fpu): Likewise.
	* config/fpu_glibc.h (set_fpu): Likewise.
	* config/fpu-sysv.h (set_fpu): Likewise.
	* runtime/backtrace.c (dump_glibc_backtrace): Likewise.
	(show_backtrace): Likewise.
	* runtime/environ.c (print_spaces): Likewise.
	(show_string): Likewise.
	(show_variables): Likewise.
	(variable_table[]): Remove GFORTRAN_USE_STDERR entry.


-- 
Janne Blomqvist

[-- Attachment #2: st_printf2.diff --]
[-- Type: text/x-patch, Size: 29683 bytes --]

diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi
index 1284c3d..c810fe2 100644
--- a/gcc/fortran/gfortran.texi
+++ b/gcc/fortran/gfortran.texi
@@ -579,7 +579,6 @@ Malformed environment variables are silently ignored.
 * GFORTRAN_STDIN_UNIT:: Unit number for standard input
 * GFORTRAN_STDOUT_UNIT:: Unit number for standard output
 * GFORTRAN_STDERR_UNIT:: Unit number for standard error
-* GFORTRAN_USE_STDERR:: Send library output to standard error
 * GFORTRAN_TMPDIR:: Directory for scratch files
 * GFORTRAN_UNBUFFERED_ALL:: Don't buffer I/O for all units.
 * GFORTRAN_UNBUFFERED_PRECONNECTED:: Don't buffer I/O for preconnected units.
@@ -613,14 +612,6 @@ This environment variable can be used to select the unit number
 preconnected to standard error.  This must be a positive integer.
 The default value is 0.
 
-@node GFORTRAN_USE_STDERR
-@section @env{GFORTRAN_USE_STDERR}---Send library output to standard error
-
-This environment variable controls where library output is sent.
-If the first letter is @samp{y}, @samp{Y} or @samp{1}, standard
-error is used.  If the first letter is @samp{n}, @samp{N} or
-@samp{0}, standard output is used.
-
 @node GFORTRAN_TMPDIR
 @section @env{GFORTRAN_TMPDIR}---Directory for scratch files
 
diff --git a/libgfortran/config/fpu-aix.h b/libgfortran/config/fpu-aix.h
index 7d6f8df..262557b 100644
--- a/libgfortran/config/fpu-aix.h
+++ b/libgfortran/config/fpu-aix.h
@@ -1,8 +1,8 @@
 /* AIX FPU-related code.
-   Copyright 2005, 2007, 2009 Free Software Foundation, Inc.
+   Copyright 2005, 2007, 2009, 2011 Free Software Foundation, Inc.
    Contributed by Francois-Xavier Coudert <coudert@clipper.ens.fr>
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -38,44 +38,44 @@ set_fpu (void)
 #ifdef TRP_INVALID
     mode |= TRP_INVALID;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'invalid operation' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'invalid operation' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_DENORMAL)
-    st_printf ("Fortran runtime warning: IEEE 'denormal number' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'denormal number' "
+	        "exception not supported.\n");
 
   if (options.fpe & GFC_FPE_ZERO)
 #ifdef TRP_DIV_BY_ZERO
     mode |= TRP_DIV_BY_ZERO;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'division by zero' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'division by zero' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_OVERFLOW)
 #ifdef TRP_OVERFLOW
     mode |= TRP_OVERFLOW;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'overflow' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'overflow' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_UNDERFLOW)
 #ifdef TRP_UNDERFLOW
     mode |= TRP_UNDERFLOW;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'underflow' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'underflow' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_PRECISION)
 #ifdef TRP_UNDERFLOW
     mode |= TRP_UNDERFLOW;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'loss of precision' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'loss of precision' "
+	        "exception not supported.\n");
 #endif
 
   fp_trap(FP_TRAP_SYNC);
diff --git a/libgfortran/config/fpu-generic.h b/libgfortran/config/fpu-generic.h
index 234e6e2..72de91b 100644
--- a/libgfortran/config/fpu-generic.h
+++ b/libgfortran/config/fpu-generic.h
@@ -1,8 +1,8 @@
 /* Fallback FPU-related code (for systems not otherwise supported).
-   Copyright 2005, 2007, 2009 Free Software Foundation, Inc.
+   Copyright 2005, 2007, 2009, 2011 Free Software Foundation, Inc.
    Contributed by Francois-Xavier Coudert <coudert@clipper.ens.fr>
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -32,21 +32,21 @@ void
 set_fpu (void)
 {
   if (options.fpe & GFC_FPE_INVALID)
-    st_printf ("Fortran runtime warning: IEEE 'invalid operation' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'invalid operation' "
+	        "exception not supported.\n");
   if (options.fpe & GFC_FPE_DENORMAL)
-    st_printf ("Fortran runtime warning: IEEE 'denormal number' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'denormal number' "
+	        "exception not supported.\n");
   if (options.fpe & GFC_FPE_ZERO)
-    st_printf ("Fortran runtime warning: IEEE 'division by zero' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'division by zero' "
+	        "exception not supported.\n");
   if (options.fpe & GFC_FPE_OVERFLOW)
-    st_printf ("Fortran runtime warning: IEEE 'overflow' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'overflow' "
+	        "exception not supported.\n");
   if (options.fpe & GFC_FPE_UNDERFLOW)
-    st_printf ("Fortran runtime warning: IEEE 'underflow' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'underflow' "
+	        "exception not supported.\n");
   if (options.fpe & GFC_FPE_PRECISION)
-    st_printf ("Fortran runtime warning: IEEE 'loss of precision' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'loss of precision' "
+	        "exception not supported.\n");
 }
diff --git a/libgfortran/config/fpu-glibc.h b/libgfortran/config/fpu-glibc.h
index 807f094..669b7ad 100644
--- a/libgfortran/config/fpu-glibc.h
+++ b/libgfortran/config/fpu-glibc.h
@@ -1,8 +1,8 @@
 /* FPU-related code for systems with GNU libc.
-   Copyright 2005, 2007, 2009 Free Software Foundation, Inc.
+   Copyright 2005, 2007, 2009, 2011 Free Software Foundation, Inc.
    Contributed by Francois-Xavier Coudert <coudert@clipper.ens.fr>
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -40,8 +40,8 @@ void set_fpu (void)
 #ifdef FE_INVALID
     feenableexcept (FE_INVALID);
 #else
-    st_printf ("Fortran runtime warning: IEEE 'invalid operation' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'invalid operation' "
+	        "exception not supported.\n");
 #endif
 
 /* glibc does never have a FE_DENORMAL.  */
@@ -49,39 +49,39 @@ void set_fpu (void)
 #ifdef FE_DENORMAL
     feenableexcept (FE_DENORMAL);
 #else
-    st_printf ("Fortran runtime warning: IEEE 'denormal number' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'denormal number' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_ZERO)
 #ifdef FE_DIVBYZERO
     feenableexcept (FE_DIVBYZERO);
 #else
-    st_printf ("Fortran runtime warning: IEEE 'division by zero' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'division by zero' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_OVERFLOW)
 #ifdef FE_OVERFLOW
     feenableexcept (FE_OVERFLOW);
 #else
-    st_printf ("Fortran runtime warning: IEEE 'overflow' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'overflow' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_UNDERFLOW)
 #ifdef FE_UNDERFLOW
     feenableexcept (FE_UNDERFLOW);
 #else
-    st_printf ("Fortran runtime warning: IEEE 'underflow' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'underflow' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_PRECISION)
 #ifdef FE_INEXACT
     feenableexcept (FE_INEXACT);
 #else
-    st_printf ("Fortran runtime warning: IEEE 'loss of precision' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'loss of precision' "
+	        "exception not supported.\n");
 #endif
 }
diff --git a/libgfortran/config/fpu-sysv.h b/libgfortran/config/fpu-sysv.h
index 85ca252..4770089 100644
--- a/libgfortran/config/fpu-sysv.h
+++ b/libgfortran/config/fpu-sysv.h
@@ -1,8 +1,8 @@
 /* SysV FPU-related code (for systems not otherwise supported).
-   Copyright 2005, 2007, 2009 Free Software Foundation, Inc.
+   Copyright 2005, 2007, 2009, 2011 Free Software Foundation, Inc.
    Contributed by Francois-Xavier Coudert <coudert@clipper.ens.fr>
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -34,48 +34,48 @@ set_fpu (void)
 #ifdef FP_X_INV
     cw |= FP_X_INV;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'invalid operation' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'invalid operation' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_DENORMAL)
 #ifdef FP_X_DNML
     cw |= FP_X_DNML;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'denormal number' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'denormal number' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_ZERO)
 #ifdef FP_X_DZ
     cw |= FP_X_DZ;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'division by zero' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'division by zero' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_OVERFLOW)
 #ifdef FP_X_OFL
     cw |= FP_X_OFL;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'overflow' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'overflow' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_UNDERFLOW)
 #ifdef FP_X_UFL
     cw |= FP_X_UFL;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'underflow' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'underflow' "
+	        "exception not supported.\n");
 #endif
 
   if (options.fpe & GFC_FPE_PRECISION)
 #ifdef FP_X_IMP
     cw |= FP_X_IMP;
 #else
-    st_printf ("Fortran runtime warning: IEEE 'loss of precision' "
-	       "exception not supported.\n");
+    estr_write ("Fortran runtime warning: IEEE 'loss of precision' "
+	        "exception not supported.\n");
 #endif
 
   fpsetmask(cw);
diff --git a/libgfortran/io/unix.c b/libgfortran/io/unix.c
index 4e4bc3b..c257766 100644
--- a/libgfortran/io/unix.c
+++ b/libgfortran/io/unix.c
@@ -1353,61 +1353,6 @@ error_stream (void)
 }
 
 
-/* st_vprintf()-- vprintf function for error output.  To avoid buffer
-   overruns, we limit the length of the buffer to ST_VPRINTF_SIZE.  2k
-   is big enough to completely fill a 80x25 terminal, so it shuld be
-   OK.  We use a direct write() because it is simpler and least likely
-   to be clobbered by memory corruption.  Writing an error message
-   longer than that is an error.  */
-
-#define ST_VPRINTF_SIZE 2048
-
-int
-st_vprintf (const char *format, va_list ap)
-{
-  static char buffer[ST_VPRINTF_SIZE];
-  int written;
-  int fd;
-
-  fd = options.use_stderr ? STDERR_FILENO : STDOUT_FILENO;
-#ifdef HAVE_VSNPRINTF
-  written = vsnprintf(buffer, ST_VPRINTF_SIZE, format, ap);
-#else
-  written = vsprintf(buffer, format, ap);
-
-  if (written >= ST_VPRINTF_SIZE-1)
-    {
-      /* The error message was longer than our buffer.  Ouch.  Because
-	 we may have messed up things badly, report the error and
-	 quit.  */
-#define ERROR_MESSAGE "Internal error: buffer overrun in st_vprintf()\n"
-      write (fd, buffer, ST_VPRINTF_SIZE-1);
-      write (fd, ERROR_MESSAGE, strlen(ERROR_MESSAGE));
-      sys_exit(2);
-#undef ERROR_MESSAGE
-
-    }
-#endif
-
-  written = write (fd, buffer, written);
-  return written;
-}
-
-/* st_printf()-- printf() function for error output.  This just calls
-   st_vprintf() to do the actual work.  */
-
-int
-st_printf (const char *format, ...)
-{
-  int written;
-  va_list ap;
-  va_start (ap, format);
-  written = st_vprintf(format, ap);
-  va_end (ap);
-  return written;
-}
-
-
 /* compare_file_filename()-- Given an open stream and a fortran string
  * that is a filename, figure out if the file is the same as the
  * filename. */
diff --git a/libgfortran/libgfortran.h b/libgfortran/libgfortran.h
index 6cccaca..ae86a02 100644
--- a/libgfortran/libgfortran.h
+++ b/libgfortran/libgfortran.h
@@ -508,7 +508,7 @@ typedef struct
   int separator_len;
   const char *separator;
 
-  int use_stderr, all_unbuffered, unbuffered_preconnected, default_recl;
+  int all_unbuffered, unbuffered_preconnected, default_recl;
   int fpe, dump_core, backtrace;
 }
 options_t;
@@ -691,6 +691,16 @@ internal_proto(show_backtrace);
 extern void sys_exit (int) __attribute__ ((noreturn));
 internal_proto(sys_exit);
 
+extern ssize_t estr_write (const char *);
+internal_proto(estr_write);
+
+extern int st_vprintf (const char *, va_list);
+internal_proto(st_vprintf);
+
+extern int st_printf (const char *, ...)
+  __attribute__((format (gfc_printf, 1, 2)));
+internal_proto(st_printf);
+
 extern const char *gfc_xtoa (GFC_UINTEGER_LARGEST, char *, size_t);
 internal_proto(gfc_xtoa);
 
@@ -792,13 +802,6 @@ internal_proto(close_units);
 extern int unit_to_fd (int);
 internal_proto(unit_to_fd);
 
-extern int st_printf (const char *, ...)
-  __attribute__ ((format (gfc_printf, 1, 2)));
-internal_proto(st_printf);
-
-extern int st_vprintf (const char *, va_list);
-internal_proto(st_vprintf);
-
 extern char * filename_from_unit (int);
 internal_proto(filename_from_unit);
 
diff --git a/libgfortran/runtime/backtrace.c b/libgfortran/runtime/backtrace.c
index 5e4f15c..aa77025 100644
--- a/libgfortran/runtime/backtrace.c
+++ b/libgfortran/runtime/backtrace.c
@@ -95,7 +95,11 @@ dump_glibc_backtrace (int depth, char *str[])
   int i;
 
   for (i = 0; i < depth; i++)
-    st_printf ("  + %s\n", str[i]);
+    {
+      estr_write ("  + ");
+      estr_write (str[i]);
+      estr_write ("\n");
+    }
 
   free (str);
 }
@@ -192,7 +196,7 @@ show_backtrace (void)
 
     if (fgets (func, sizeof(func), output))
       {
-	st_printf ("\nBacktrace for this error:\n");
+	estr_write ("\nBacktrace for this error:\n");
 
 	do
 	  {
@@ -222,7 +226,9 @@ show_backtrace (void)
 	    if (func[0] == '?' && func[1] == '?' && file[0] == '?'
 		&& file[1] == '?')
 	      {
-	        st_printf ("  + %s\n", str[i]);
+		estr_write ("  + ");
+		estr_write (str[i]);
+		estr_write ("\n");
 	        continue;
 	      }
 
@@ -239,15 +245,25 @@ show_backtrace (void)
 	      line = -1;
 
 	    if (strcmp (func, "MAIN__") == 0)
-	      st_printf ("  + in the main program\n");
+	      estr_write ("  + in the main program\n");
 	    else
-	      st_printf ("  + function %s (0x%s)\n", func, addr[i]);
+	      {
+		estr_write ("  + function ");
+		estr_write (func);
+		estr_write (" (0x");
+		estr_write (addr[i]);
+		estr_write (")\n");
+	      }
 
 	    if (line <= 0 && strcmp (file, "??") == 0)
 	      continue;
 
 	    if (line <= 0)
-	      st_printf ("    from file %s\n", file);
+	      {
+		estr_write ("    from file ");
+		estr_write (file);
+		estr_write ("\n");
+	      }
 	    else
 	      st_printf ("    at line %d of file %s\n", line, file);
 	  }
@@ -257,8 +273,8 @@ show_backtrace (void)
 	return;
 
 fallback:
-	st_printf ("** Something went wrong while running addr2line. **\n"
-		   "** Falling back  to a simpler  backtrace scheme. **\n");
+	estr_write ("** Something went wrong while running addr2line. **\n"
+		    "** Falling back  to a simpler  backtrace scheme. **\n");
       }
     }
   while (0);
@@ -288,7 +304,7 @@ fallback:
 	char *arg[NUM_ARGS+1];
 	char buf[20];
 
-	st_printf ("\nBacktrace for this error:\n");
+	estr_write ("\nBacktrace for this error:\n");
 	arg[0] = (char *) "pstack";
 	snprintf (buf, sizeof(buf), "%d", (int) getppid ());
 	arg[1] = buf;
@@ -301,7 +317,7 @@ fallback:
 #if GLIBC_BACKTRACE
 	dump_glibc_backtrace (depth, str);
 #else
-	st_printf ("  unable to produce a backtrace, sorry!\n");
+	estr_write ("  unable to produce a backtrace, sorry!\n");
 #endif
 
 	_exit (0);
@@ -316,7 +332,7 @@ fallback:
 
 #if GLIBC_BACKTRACE
   /* Fallback to the glibc backtrace.  */
-  st_printf ("\nBacktrace for this error:\n");
+  estr_write ("\nBacktrace for this error:\n");
   dump_glibc_backtrace (depth, str);
 #endif
 }
diff --git a/libgfortran/runtime/environ.c b/libgfortran/runtime/environ.c
index a6ce645..7695f0d 100644
--- a/libgfortran/runtime/environ.c
+++ b/libgfortran/runtime/environ.c
@@ -71,7 +71,7 @@ print_spaces (int n)
 
   buffer[i] = '\0';
 
-  st_printf (buffer);
+  estr_write (buffer);
 }
 
 
@@ -261,7 +261,10 @@ show_string (variable * v)
   if (p == NULL)
     p = "";
 
-  st_printf ("%s  \"%s\"\n", var_source (v), p);
+  estr_write (var_source (v));
+  estr_write ("  \"");
+  estr_write (p);
+  estr_write ("\"\n");
 }
 
 
@@ -281,10 +284,6 @@ static variable variable_table[] = {
    "Unit number that will be preconnected to standard error\n"
    "(No preconnection if negative)", 0},
 
-  {"GFORTRAN_USE_STDERR", 1, &options.use_stderr, init_boolean,
-   show_boolean,
-   "Sends library output to standard error instead of standard output.", 0},
-
   {"GFORTRAN_TMPDIR", 0, NULL, init_string, show_string,
    "Directory for scratch files.  Overrides the TMP environment variable\n"
    "If TMP is not set " DEFAULT_TEMPDIR " is used.", 0},
@@ -352,32 +351,33 @@ show_variables (void)
   int n;
 
   /* TODO: print version number.  */
-  st_printf ("GNU Fortran 95 runtime library version "
+  estr_write ("GNU Fortran runtime library version "
 	     "UNKNOWN" "\n\n");
 
-  st_printf ("Environment variables:\n");
-  st_printf ("----------------------\n");
+  estr_write ("Environment variables:\n");
+  estr_write ("----------------------\n");
 
   for (v = variable_table; v->name; v++)
     {
-      n = st_printf ("%s", v->name);
+      n = estr_write (v->name);
       print_spaces (25 - n);
 
       if (v->show == show_integer)
-	st_printf ("Integer ");
+	estr_write ("Integer ");
       else if (v->show == show_boolean)
-	st_printf ("Boolean ");
+	estr_write ("Boolean ");
       else
-	st_printf ("String  ");
+	estr_write ("String  ");
 
       v->show (v);
-      st_printf ("%s\n\n", v->desc);
+      estr_write (v->desc);
+      estr_write ("\n\n");
     }
 
   /* System error codes */
 
-  st_printf ("\nRuntime error codes:");
-  st_printf ("\n--------------------\n");
+  estr_write ("\nRuntime error codes:");
+  estr_write ("\n--------------------\n");
 
   for (n = LIBERROR_FIRST + 1; n < LIBERROR_LAST; n++)
     if (n < 0 || n > 9)
@@ -385,10 +385,8 @@ show_variables (void)
     else
       st_printf (" %d  %s\n", n, translate_error (n));
 
-  st_printf ("\nCommand line arguments:\n");
-  st_printf ("  --help               Print this list\n");
-
-  /* st_printf("  --resume <dropfile>  Resume program execution from dropfile\n"); */
+  estr_write ("\nCommand line arguments:\n");
+  estr_write ("  --help               Print this list\n");
 
   sys_exit (0);
 }
diff --git a/libgfortran/runtime/error.c b/libgfortran/runtime/error.c
index 06c144a..e619044 100644
--- a/libgfortran/runtime/error.c
+++ b/libgfortran/runtime/error.c
@@ -81,7 +81,7 @@ sys_exit (int code)
       struct rlimit core_limit;
 
       if (getrlimit (RLIMIT_CORE, &core_limit) == 0 && core_limit.rlim_cur == 0)
-	st_printf ("** Warning: a core dump was requested, but the core size"
+	estr_write ("** Warning: a core dump was requested, but the core size"
 		   "limit\n**          is currently zero.\n\n");
 #endif
       
@@ -89,7 +89,7 @@ sys_exit (int code)
 #if defined(HAVE_KILL) && defined(HAVE_GETPID) && defined(SIGQUIT)
       kill (getpid (), SIGQUIT);
 #else
-      st_printf ("Core dump not possible, sorry.");
+      estr_write ("Core dump not possible, sorry.");
 #endif
     }
 
@@ -112,6 +112,67 @@ sys_exit (int code)
  * Other error returns are reserved for the STOP statement with a numeric code.
  */
 
+
+/* Write a null-terminated C string to standard error. This function
+   is async-signal-safe.  */
+
+ssize_t
+estr_write (const char *str)
+{
+  return write (STDERR_FILENO, str, strlen (str));
+}
+
+
+/* st_vprintf()-- vsnprintf-like function for error output.  We use a
+   stack allocated buffer for formatting; since this function might be
+   called from within a signal handler, printing directly to stderr
+   with vfprintf is not safe since the stderr locking might lead to a
+   deadlock.  */
+
+#define ST_VPRINTF_SIZE 512
+
+int
+st_vprintf (const char *format, va_list ap)
+{
+  int written;
+  char buffer[ST_VPRINTF_SIZE];
+
+#ifdef HAVE_VSNPRINTF
+  written = vsnprintf(buffer, ST_VPRINTF_SIZE, format, ap);
+#else
+  written = vsprintf(buffer, format, ap);
+
+  if (written >= ST_VPRINTF_SIZE - 1)
+    {
+      /* The error message was longer than our buffer.  Ouch.  Because
+	 we may have messed up things badly, report the error and
+	 quit.  */
+#define ERROR_MESSAGE "Internal error: buffer overrun in st_vprintf()\n"
+      write (STDERR_FILENO, buffer, ST_VPRINTF_SIZE - 1);
+      write (STDERR_FILENO, ERROR_MESSAGE, strlen(ERROR_MESSAGE));
+      sys_exit(2);
+#undef ERROR_MESSAGE
+
+    }
+#endif
+
+  written = write (STDERR_FILENO, buffer, written);
+  return written;
+}
+
+
+int
+st_printf (const char * format, ...)
+{
+  int written;
+  va_list ap;
+  va_start (ap, format);
+  written = st_vprintf (format, ap);
+  va_end (ap);
+  return written;
+}
+
+
 /* gfc_xtoa()-- Integer to hexadecimal conversion.  */
 
 const char *
@@ -177,7 +238,7 @@ gf_strerror (int errnum,
 void
 show_locus (st_parameter_common *cmp)
 {
-  static char *filename;
+  char *filename;
 
   if (!options.locus || cmp == NULL || cmp->filename == NULL)
     return;
@@ -185,6 +246,7 @@ show_locus (st_parameter_common *cmp)
   if (cmp->unit > 0)
     {
       filename = filename_from_unit (cmp->unit);
+
       if (filename != NULL)
 	{
 	  st_printf ("At line %d of file %s (unit = %d, file = '%s')\n",
@@ -233,8 +295,11 @@ os_error (const char *message)
 {
   char errmsg[STRERR_MAXSZ];
   recursion_check ();
-  st_printf ("Operating system error: %s\n%s\n", 
-	     gf_strerror (errno, errmsg, STRERR_MAXSZ), message);
+  estr_write ("Operating system error: ");
+  estr_write (gf_strerror (errno, errmsg, STRERR_MAXSZ));
+  estr_write ("\n");
+  estr_write (message);
+  estr_write ("\n");
   sys_exit (1);
 }
 iexport(os_error);
@@ -249,11 +314,11 @@ runtime_error (const char *message, ...)
   va_list ap;
 
   recursion_check ();
-  st_printf ("Fortran runtime error: ");
+  estr_write ("Fortran runtime error: ");
   va_start (ap, message);
   st_vprintf (message, ap);
   va_end (ap);
-  st_printf ("\n");
+  estr_write ("\n");
   sys_exit (2);
 }
 iexport(runtime_error);
@@ -267,12 +332,12 @@ runtime_error_at (const char *where, const char *message, ...)
   va_list ap;
 
   recursion_check ();
-  st_printf ("%s\n", where);
-  st_printf ("Fortran runtime error: ");
+  estr_write (where);
+  estr_write ("\nFortran runtime error: ");
   va_start (ap, message);
   st_vprintf (message, ap);
   va_end (ap);
-  st_printf ("\n");
+  estr_write ("\n");
   sys_exit (2);
 }
 iexport(runtime_error_at);
@@ -283,12 +348,12 @@ runtime_warning_at (const char *where, const char *message, ...)
 {
   va_list ap;
 
-  st_printf ("%s\n", where);
-  st_printf ("Fortran runtime warning: ");
+  estr_write (where);
+  estr_write ("\nFortran runtime warning: ");
   va_start (ap, message);
   st_vprintf (message, ap);
   va_end (ap);
-  st_printf ("\n");
+  estr_write ("\n");
 }
 iexport(runtime_warning_at);
 
@@ -301,7 +366,9 @@ internal_error (st_parameter_common *cmp, const char *message)
 {
   recursion_check ();
   show_locus (cmp);
-  st_printf ("Internal Error: %s\n", message);
+  estr_write ("Internal Error: ");
+  estr_write (message);
+  estr_write ("\n");
 
   /* This function call is here to get the main.o object file included
      when linking statically. This works because error.o is supposed to
@@ -474,7 +541,9 @@ generate_error (st_parameter_common *cmp, int family, const char *message)
 
   recursion_check ();
   show_locus (cmp);
-  st_printf ("Fortran runtime error: %s\n", message);
+  estr_write ("Fortran runtime error: ");
+  estr_write (message);
+  estr_write ("\n");
   sys_exit (2);
 }
 iexport(generate_error);
@@ -489,7 +558,9 @@ generate_warning (st_parameter_common *cmp, const char *message)
     message = " ";
 
   show_locus (cmp);
-  st_printf ("Fortran runtime warning: %s\n", message);
+  estr_write ("Fortran runtime warning: ");
+  estr_write (message);
+  estr_write ("\n");
 }
 
 
@@ -532,13 +603,17 @@ notify_std (st_parameter_common *cmp, int std, const char * message)
     {
       recursion_check ();
       show_locus (cmp);
-      st_printf ("Fortran runtime error: %s\n", message);
+      estr_write ("Fortran runtime error: ");
+      estr_write (message);
+      estr_write ("\n");
       sys_exit (2);
     }
   else
     {
       show_locus (cmp);
-      st_printf ("Fortran runtime warning: %s\n", message);
+      estr_write ("Fortran runtime warning: ");
+      estr_write (message);
+      estr_write ("\n");
     }
   return FAILURE;
 }
diff --git a/libgfortran/runtime/pause.c b/libgfortran/runtime/pause.c
index 61ab4db..0e3c8ae 100644
--- a/libgfortran/runtime/pause.c
+++ b/libgfortran/runtime/pause.c
@@ -1,8 +1,8 @@
-/* Implementation of the STOP statement.
-   Copyright 2002, 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+/* Implementation of the PAUSE statement.
+   Copyright 2002, 2005, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
    Contributed by Paul Brook <paul@nowt.org>
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -25,18 +25,19 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #include "libgfortran.h"
 #include <string.h>
+#include <unistd.h>
 
 static void
 do_pause (void)
 {
   char buff[4];
-  st_printf ("To resume execution, type go.  "
-	     "Other input will terminate the job.\n");
+  estr_write ("To resume execution, type go.  "
+	      "Other input will terminate the job.\n");
 
   fgets(buff, 4, stdin);
   if (strncmp(buff, "go\n", 3) != 0)
     stop_string ('\0', 0);
-  st_printf ("RESUMED\n");
+  estr_write ("RESUMED\n");
 }
 
 /* A numeric PAUSE statement.  */
@@ -59,10 +60,11 @@ export_proto(pause_string);
 void
 pause_string (char *string, GFC_INTEGER_4 len)
 {
-  st_printf ("PAUSE ");
-  while (len--)
-    st_printf ("%c", *(string++));
-  st_printf ("\n");
+  estr_write ("PAUSE ");
+  ssize_t w = write (STDERR_FILENO, string, len);
+  (void) sizeof (w); /* Avoid compiler warning about not using write
+			return val.  */
+  estr_write ("\n");
 
   do_pause ();
 }
diff --git a/libgfortran/runtime/stop.c b/libgfortran/runtime/stop.c
index 29f5031..b6f61ff 100644
--- a/libgfortran/runtime/stop.c
+++ b/libgfortran/runtime/stop.c
@@ -1,8 +1,8 @@
 /* Implementation of the STOP statement.
-   Copyright 2002, 2005, 2007, 2009, 2010 Free Software Foundation, Inc.
+   Copyright 2002, 2005, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
    Contributed by Paul Brook <paul@nowt.org>
 
-This file is part of the GNU Fortran 95 runtime library (libgfortran).
+This file is part of the GNU Fortran runtime library (libgfortran).
 
 Libgfortran is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
@@ -25,6 +25,7 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 
 #include "libgfortran.h"
 #include <string.h>
+#include <unistd.h>
 
 /* A numeric STOP statement.  */
 
@@ -65,10 +66,10 @@ stop_string (const char *string, GFC_INTEGER_4 len)
 {
   if (string)
     {
-      st_printf ("STOP ");
-      while (len--)
-	st_printf ("%c", *(string++));
-      st_printf ("\n");
+      estr_write ("STOP ");
+      ssize_t w = write (STDERR_FILENO, string, len);
+      (void) sizeof (w); /* Avoid compiler warning about not using w.  */
+      estr_write ("\n");
     }
   sys_exit (0);
 }
@@ -86,10 +87,10 @@ export_proto(error_stop_string);
 void
 error_stop_string (const char *string, GFC_INTEGER_4 len)
 {
-  st_printf ("ERROR STOP ");
-  while (len--)
-    st_printf ("%c", *(string++));
-  st_printf ("\n");
+  estr_write ("ERROR STOP ");
+  ssize_t w = write (STDERR_FILENO, string, len);
+  (void) sizeof (w); /* Avoid compiler warning about not using w.  */
+  estr_write ("\n");
 
   sys_exit (1);
 }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch, libfortran] Thread safety and simplification of error printing
  2011-05-08 17:35   ` N.M. Maclaren
  2011-05-08 22:50     ` N.M. Maclaren
@ 2011-05-09  2:31     ` Janne Blomqvist
  2011-05-09  4:00       ` N.M. Maclaren
  1 sibling, 1 reply; 9+ messages in thread
From: Janne Blomqvist @ 2011-05-09  2:31 UTC (permalink / raw)
  To: N.M. Maclaren; +Cc: Fortran List, GCC Patches

On Sun, May 8, 2011 at 16:42, N.M. Maclaren <nmm1@cam.ac.uk> wrote:
> On May 8 2011, Janne Blomqvist wrote:
>>>
>>> the error printing functionality (in io/unix.c) st_printf and
>>> st_vprintf are not thread-safe as they use a static buffer. ...
>>
>> While this patch makes error printing thread-safe, it's no longer
>> async-signal-safe as the stderr lock might lead to a deadlock. So I'm
>> retracting this patch and thinking some more about this problem.
>
> It's theoretically insoluble, given the constraints you are working
> under.  Sorry.  It is possible to do reasonably well, but there will
> always be likely scenarios where all you can do is to say "Aargh!
> I give up."

Well, I realize perfection is impossible, so I'm settling for merely
improving the status quo!

> Both I and the VMS people adopted the ratchet design.  You have N
> levels of error recovery, each level allocates all of the resources
> it needs before startup, and any exception during level K increases
> the level to K+1 and calls the level K+1 error handler.  When you
> have an exception at level N, you just die.

To some extent we have a crude version of this, in that when we're
entering many of the fatal error handling functions we do a recursion
check and if that fails, die. Also, in a recent patch of mine
(http://gcc.gnu.org/ml/gcc-patches/2011-05/msg00584.html ) the fatal
signal handler function has been reworked to hopefully deal better
with other signal(s) being delivered before it's done; that code is
modeled after an example in the glibc manual, and I'm a bit unsure if
the recursion check thingy really works or we just end up in an
infinite recursion (that is, do we need to re-set to the default
handler before re-raising? I have a vague memory that the signal
handler for SIGXXX must finish before starting the handler for another
SIGXXX pending signal, which would make the current version safe).

> That imposes the constraint that all diagnostics have a fixed upper
> bound on the resources they need (not just buffer space, but that's
> the main one).  It's a real bummer when the system has some critical
> resources that you can't reserve, so you have to treat an allocation
> failure as an exception, but buffer space is not one such.
>
> That also tackles the thread problem, not very satisfactorily.  If a
> resource needs to be locked, you can try to get it for a bit, and
> then raise a higher exception if you can't.  And, typically, one or
> more of the highest levels are for closing down the process, and
> simply suspend any subsequent threads that call them (i.e. just leave
> them waiting for a lock they won't get).

I think in our case the situation is a bit easier in that we're not
trying to recover from a serious failure, merely print some diagnostic
information without getting stuck in a deadlock.


-- 
Janne Blomqvist

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch, libfortran] Thread safety and simplification of error printing
  2011-05-09  2:31     ` Janne Blomqvist
@ 2011-05-09  4:00       ` N.M. Maclaren
  0 siblings, 0 replies; 9+ messages in thread
From: N.M. Maclaren @ 2011-05-09  4:00 UTC (permalink / raw)
  To: Fortran List, GCC Patches

On May 8 2011, Janne Blomqvist wrote:
>>
>> It's theoretically insoluble, given the constraints you are working
>> under.  Sorry.  It is possible to do reasonably well, but there will
>> always be likely scenarios where all you can do is to say "Aargh!
>> I give up."
>
>Well, I realize perfection is impossible, so I'm settling for merely
>improving the status quo!

Very reasonable, given the problem.  Where the effort increases rapidly
with the reliability, not being too fancy is a sound strategy.

>I think in our case the situation is a bit easier in that we're not
>trying to recover from a serious failure, merely print some diagnostic
>information without getting stuck in a deadlock.

I wasn't trying to do much more!  I was mainly trying to maximise the
chances of files being closed cleanly and appropriate diagnostics
being produced.  Once there was a serious error, I didn't allow the
program to continue.  But I did allow user-written exception handlers
(which could only return - NOT jump out).  Even that was hard enough :-(

Regards,
Nick Maclaren.



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch, libfortran] Thread safety and simplification of error printing
  2011-05-08 23:01   ` Janne Blomqvist
@ 2011-05-13 11:00     ` Janne Blomqvist
  2011-05-14 10:26     ` Steve Kargl
  1 sibling, 0 replies; 9+ messages in thread
From: Janne Blomqvist @ 2011-05-13 11:00 UTC (permalink / raw)
  To: Fortran List, GCC Patches

PING

On Sun, May 8, 2011 at 19:53, Janne Blomqvist <blomqvist.janne@gmail.com> wrote:
> On Sun, May 8, 2011 at 14:40, Janne Blomqvist <blomqvist.janne@gmail.com> wrote:
>> On Sat, May 7, 2011 at 19:35, Janne Blomqvist <blomqvist.janne@gmail.com> wrote:
>>> Hi,
>>>
>>> the error printing functionality (in io/unix.c) st_printf and
>>> st_vprintf are not thread-safe as they use a static buffer. However,
>>> since these routines are used when something has gone wrong, we
>>> shouldn't use malloc() to allocate thread-specific buffers or anything
>>> fancy like that. The way the buffer is used is that it is filled with
>>> vs(n)printf() and then the buffer is written using write(); the
>>> simplest way to fix this is to just print directly using vfprintf(),
>>> which is what this patch does. Also, the patch uses
>>> flockfile/funlockfile in a few places to increase the likelihood of
>>> multiple related messages to end up together on the output.
>>>
>>> As stderr is unbuffered and stdout is buffered, this patch also gets
>>> rid of the functionality to choose where to send the error output, as
>>> we don't want to deal with buffering in the error handling path. IMHO
>>> this is no loss, as all targets/shells/batch schedulers/ etc. where
>>> this might be relevant, offer functionality to redirect stderr to
>>> wherever stdout goes. So there is no need for a gfortran-specific
>>> mechanism for this.
>>
>> While this patch makes error printing thread-safe, it's no longer
>> async-signal-safe as the stderr lock might lead to a deadlock. So I'm
>> retracting this patch and thinking some more about this problem.
>
> So here is an updated patch. Compared to the status quo the main
> differences are that it uses a stack allocated buffer to do the
> formatting instead of a static one. This prevents conflicts from
> multiple threads and in contrast to the approach in the previous patch
> should also be async-signal-safe (or at least, less likely to go
> belly-up when called from within a signal handler than using
> vfprintf() directly). It also introduces a new function to write a
> string to standard error without any formatting (estr_write), which is
> simpler and less likely to cause problems than the general purpose
> vs(n)printf() used before. This can be used in lieu of st_printf in
> the majority of cases where no formatting is done.
>
> Regtested on x86_64-unknown-linux-gnu, Ok for trunk?
>
> frontend ChangeLog:
>
> 2011-05-08  Janne Blomqvist  <jb@gcc.gnu.org>
>
>        * gfortran.texi: Remove GFORTRAN_USE_STDERR documentation.
>
>
> library ChangeLog:
>
> 2011-05-08  Janne Blomqvist  <jb@gcc.gnu.org>
>
>        * io/unix.c (st_vprintf,st_printf): Move to runtime/error.c.
>        * libgfortran.h (struct options_t): Remove use_stderr field.
>        (st_vprintf,st_printf): Move prototypes.
>        (estr_write): New prototype.
>        * runtime/error.c (sys_exit): Use estr_write instead of st_printf.
>        (estr_write): New function.
>        (st_vprintf): Move from io/unix.c, use stack allocated buffer,
>        always output to stderr.
>        (st_printf): Move from io/unix.c.
>        (show_locus): Use a local variable instead of static.
>        (os_error): Use estr_write instead of st_printf.
>        (runtime_error): Likewise.
>        (runtime_error_at): Likewise.
>        (runtime_warning_at): Likewise.
>        (internal_error): Likewise.
>        (generate_error): Likewise.
>        (generate_warning): Likewise.
>        (notify_std): Likewise.
>        * runtime/pause.c (do_pause): Likewise.
>        (pause_string): Likewise.
>        * runtime/stop.c (stop_string): Likewise.
>        (error_stop_string): Likewise.
>        * config/fpu_aix.h (set_fpu): Likewise.
>        * config/fpu_generic.h (set_fpu): Likewise.
>        * config/fpu_glibc.h (set_fpu): Likewise.
>        * config/fpu-sysv.h (set_fpu): Likewise.
>        * runtime/backtrace.c (dump_glibc_backtrace): Likewise.
>        (show_backtrace): Likewise.
>        * runtime/environ.c (print_spaces): Likewise.
>        (show_string): Likewise.
>        (show_variables): Likewise.
>        (variable_table[]): Remove GFORTRAN_USE_STDERR entry.
>
>
> --
> Janne Blomqvist
>



-- 
Janne Blomqvist

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [Patch, libfortran] Thread safety and simplification of error printing
  2011-05-08 23:01   ` Janne Blomqvist
  2011-05-13 11:00     ` Janne Blomqvist
@ 2011-05-14 10:26     ` Steve Kargl
  1 sibling, 0 replies; 9+ messages in thread
From: Steve Kargl @ 2011-05-14 10:26 UTC (permalink / raw)
  To: Janne Blomqvist; +Cc: Fortran List, GCC Patches

On Sun, May 08, 2011 at 07:53:43PM +0300, Janne Blomqvist wrote:
> 
> 2011-05-08  Janne Blomqvist  <jb@gcc.gnu.org>
> 
> 	* gfortran.texi: Remove GFORTRAN_USE_STDERR documentation.
> 
> 
> library ChangeLog:
> 
> 2011-05-08  Janne Blomqvist  <jb@gcc.gnu.org>
> 
> 	* io/unix.c (st_vprintf,st_printf): Move to runtime/error.c.
> 	* libgfortran.h (struct options_t): Remove use_stderr field.
> 	(st_vprintf,st_printf): Move prototypes.
> 	(estr_write): New prototype.
> 	* runtime/error.c (sys_exit): Use estr_write instead of st_printf.
> 	(estr_write): New function.
> 	(st_vprintf): Move from io/unix.c, use stack allocated buffer,
> 	always output to stderr.
> 	(st_printf): Move from io/unix.c.
> 	(show_locus): Use a local variable instead of static.
> 	(os_error): Use estr_write instead of st_printf.
> 	(runtime_error): Likewise.
> 	(runtime_error_at): Likewise.
> 	(runtime_warning_at): Likewise.
> 	(internal_error): Likewise.
> 	(generate_error): Likewise.
> 	(generate_warning): Likewise.
> 	(notify_std): Likewise.
> 	* runtime/pause.c (do_pause): Likewise.
> 	(pause_string): Likewise.
> 	* runtime/stop.c (stop_string): Likewise.
> 	(error_stop_string): Likewise.
> 	* config/fpu_aix.h (set_fpu): Likewise.
> 	* config/fpu_generic.h (set_fpu): Likewise.
> 	* config/fpu_glibc.h (set_fpu): Likewise.
> 	* config/fpu-sysv.h (set_fpu): Likewise.
> 	* runtime/backtrace.c (dump_glibc_backtrace): Likewise.
> 	(show_backtrace): Likewise.
> 	* runtime/environ.c (print_spaces): Likewise.
> 	(show_string): Likewise.
> 	(show_variables): Likewise.
> 	(variable_table[]): Remove GFORTRAN_USE_STDERR entry.
> 

OK.

-- 
Steve

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2011-05-14  0:53 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-05-07 17:07 [Patch, libfortran] Thread safety and simplification of error printing Janne Blomqvist
2011-05-08 13:44 ` Janne Blomqvist
2011-05-08 17:35   ` N.M. Maclaren
2011-05-08 22:50     ` N.M. Maclaren
2011-05-09  2:31     ` Janne Blomqvist
2011-05-09  4:00       ` N.M. Maclaren
2011-05-08 23:01   ` Janne Blomqvist
2011-05-13 11:00     ` Janne Blomqvist
2011-05-14 10:26     ` Steve Kargl

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).