interrupted syscalls run asyncs before throwing syserror
authorAndy Wingo <wingo@pobox.com>
Sun, 10 Oct 2010 09:24:29 +0000 (11:24 +0200)
committerAndy Wingo <wingo@pobox.com>
Sun, 10 Oct 2010 09:25:50 +0000 (11:25 +0200)
* libguile/error.c (scm_syserror, scm_syserror_msg): Run pending pending
  asyncs before throwing the error, as one of the asyncs might be a
  signal handler. But there is unfortunately a race here, as noted in a
  comment.

libguile/error.c

index 4b6bab8..b4ed7d0 100644 (file)
@@ -147,6 +147,33 @@ void
 scm_syserror (const char *subr)
 {
   SCM err = scm_from_int (SCM_I_ERRNO ());
+
+  /* It could be that we're getting here because the syscall was
+     interrupted by a signal.  In that case a signal handler might have
+     been queued to run.  The signal handler probably throws an
+     exception.
+
+     If we don't try to run the signal handler now, it will run later,
+     which would result in two exceptions being thrown: this syserror,
+     and then at some later time the exception thrown by the async
+     signal handler.
+
+     The problem is that we don't know if handling the signal caused an
+     async to be queued.  By this time scmsigs.c:take_signal will have
+     written a byte on the fd, but we don't know if the signal-handling
+     thread has read it off and queued an async.
+
+     Ideally we need some API like scm_i_ensure_signals_delivered() to
+     catch up signal delivery.  Barring that, we just cross our digits
+     and pray; it could be that we handle the signal in time, and just
+     throw once, or it could be that we miss the deadline and throw
+     twice.
+  */
+#ifdef EINTR
+  if (scm_to_int (err) == EINTR)
+    SCM_ASYNC_TICK;
+#endif
+
   scm_error (scm_system_error_key,
             subr,
             "~A",
@@ -157,6 +184,11 @@ scm_syserror (const char *subr)
 void
 scm_syserror_msg (const char *subr, const char *message, SCM args, int eno)
 {
+  /* See above note about the EINTR signal handling race. */
+#ifdef EINTR
+  if (eno == EINTR)
+    SCM_ASYNC_TICK;
+#endif
   scm_error (scm_system_error_key,
             subr,
             message,