handle_signal_return: when a waiting read/write syscall is interrupted
by a signal which has been set to non-SARESTART, clean up the waiting_fds
table correctly.

xemacs (cvs, --pdump only) now works.  Yay!


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@157 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/vg_scheduler.c b/vg_scheduler.c
index 9786e5b..e9313f6 100644
--- a/vg_scheduler.c
+++ b/vg_scheduler.c
@@ -693,6 +693,40 @@
 
 /* vthread tid is returning from a signal handler; modify its
    stack/regs accordingly. */
+
+/* [Helper fn for handle_signal_return] tid, assumed to be in WaitFD
+   for read or write, has been interrupted by a signal.  Find and
+   clear the relevant vg_waiting_fd[] entry.  Most of the code in this
+   procedure is total paranoia, if you look closely. */
+static
+void cleanup_waiting_fd_table ( ThreadId tid )
+{
+   Int  i, waiters;
+
+   vg_assert(is_valid_tid(tid));
+   vg_assert(vg_threads[tid].status == VgTs_WaitFD);
+   vg_assert(vg_threads[tid].m_eax == __NR_read 
+             || vg_threads[tid].m_eax == __NR_write);
+
+   /* Excessively paranoidly ... find the fd this op was waiting
+      for, and mark it as not being waited on. */
+   waiters = 0;
+   for (i = 0; i < VG_N_WAITING_FDS; i++) {
+      if (vg_waiting_fds[i].tid == tid) {
+         waiters++;
+         vg_assert(vg_waiting_fds[i].syscall_no == vg_threads[tid].m_eax);
+      }
+   }
+   vg_assert(waiters == 1);
+   for (i = 0; i < VG_N_WAITING_FDS; i++)
+      if (vg_waiting_fds[i].tid == tid)
+         break;
+   vg_assert(i < VG_N_WAITING_FDS);
+   vg_assert(vg_waiting_fds[i].fd != -1);
+   vg_waiting_fds[i].fd = -1; /* not in use */
+}
+
+
 static
 void handle_signal_return ( ThreadId tid )
 {
@@ -707,12 +741,14 @@
       /* Easy; we don't have to do anything. */
       return;
 
-   if (vg_threads[tid].status == VgTs_WaitFD) {
-      vg_assert(vg_threads[tid].m_eax == __NR_read 
-                || vg_threads[tid].m_eax == __NR_write);
+   if (vg_threads[tid].status == VgTs_WaitFD
+       && (vg_threads[tid].m_eax == __NR_read 
+           || vg_threads[tid].m_eax == __NR_write)) {
       /* read() or write() interrupted.  Force a return with EINTR. */
+      cleanup_waiting_fd_table(tid);
       vg_threads[tid].m_eax = -VKI_EINTR;
       vg_threads[tid].status = VgTs_Runnable;
+
       if (VG_(clo_trace_sched)) {
          VG_(sprintf)(msg_buf, 
             "read() / write() interrupted by signal; return EINTR" );
@@ -721,14 +757,18 @@
       return;
    }
 
-   if (vg_threads[tid].status == VgTs_WaitFD) {
-      vg_assert(vg_threads[tid].m_eax == __NR_nanosleep);
+   if (vg_threads[tid].status == VgTs_WaitFD
+       && vg_threads[tid].m_eax == __NR_nanosleep) {
       /* We interrupted a nanosleep().  The right thing to do is to
          write the unused time to nanosleep's second param and return
          EINTR, but I'm too lazy for that. */
       return;
    }
 
+   if (vg_threads[tid].status == VgTs_WaitFD) {
+      VG_(panic)("handle_signal_return: unknown interrupted syscall");
+   }
+
    /* All other cases?  Just return. */
 }