Add susphello test, which has turned up a number of interesting bugs


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@2333 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/none/tests/Makefile.am b/none/tests/Makefile.am
index a9a7840..e8715e0 100644
--- a/none/tests/Makefile.am
+++ b/none/tests/Makefile.am
@@ -45,6 +45,7 @@
 	resolv.stderr.exp resolv.stdout.exp resolv.vgtest \
 	seg_override.stderr.exp \
 	seg_override.stdout.exp seg_override.vgtest \
+	susphello.stdout.exp susphello.stderr.exp susphello.vgtest \
 	sha1_test.stderr.exp sha1_test.vgtest \
 	shortpush.stderr.exp shortpush.vgtest \
 	shorts.stderr.exp shorts.vgtest \
@@ -61,7 +62,7 @@
 	fucomip $(INSN_TESTS) \
 	int munmap_exe map_unmap mremap rcl_assert \
 	rcrl readline1 resolv seg_override sha1_test shortpush shorts smc1 \
-	pth_blockedsig pushpopseg \
+	susphello pth_blockedsig pushpopseg \
 	syscall-restart1 syscall-restart2 system \
 	coolo_sigaction gxx304 yield
 
@@ -111,6 +112,8 @@
 sha1_test_SOURCES 	= sha1_test.c
 shortpush_SOURCES 	= shortpush.c
 shorts_SOURCES 		= shorts.c
+susphello_SOURCES	= susphello.c
+susphello_LDADD		= -lpthread
 syscall_restart1_SOURCES = syscall-restart1.c
 syscall_restart2_SOURCES = syscall-restart2.c
 system_SOURCES 		= system.c
diff --git a/none/tests/susphello.c b/none/tests/susphello.c
new file mode 100644
index 0000000..179dd94
--- /dev/null
+++ b/none/tests/susphello.c
@@ -0,0 +1,305 @@
+/* JSGF: no idea what this is actually doing, but it really gives the
+   signals/sigaltstack/threads machinery a working out */
+/**
+ * Compile with: 
+ * gcc -g -Wall -lpthread -o susphello susphello.c
+ *
+ * Author Magnus Ihse, ihse at bea.com 
+ */
+
+#include <signal.h>
+
+
+#include <errno.h>
+#include <stddef.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <dlfcn.h>
+
+
+#include <pthread.h>
+#include <unistd.h>
+#include <string.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#define THREAD_COUNT 10
+#define ITER_COUNT 200
+
+static volatile int finishedArray[THREAD_COUNT];
+static int pKey;
+
+static sigset_t srSigset;
+
+pthread_t main_thread;
+
+int srSignal = SIGUSR1;
+
+
+void
+ptiSrSigHandler(int sig, siginfo_t *sip, void *arg)
+{
+   //ucontext_t *ucontext = (ucontext_t *)arg;
+   
+   int mypos = (int) pthread_getspecific(pKey);
+   
+   
+//   int threadPos = (int)pthread_getspecific(srThreadKey);
+
+//   thread->os_context = (OSContextP)&(ucontext->uc_mcontext);
+
+   // Notify suspender that we have been suspended
+   if (pthread_kill(main_thread, srSignal) == -1) {
+      perror("pthread_kill");
+      exit(1);
+   }
+   
+   finishedArray[mypos]++;
+
+//   printf("this is thread %d: i'm now suspended!\n", mypos);
+
+   // Wait until we are resumed
+   while (sigwaitinfo(&srSigset, NULL) == -1) {
+      // Interrupted by SIGSTOP in gdb
+      if(errno != EINTR) {
+      	perror("sigwaitinfo");
+        exit(1);
+      }
+   }
+
+//   printf("this is thread %d: i'm now resumed!\n", mypos);
+
+   //thread->os_context = NULL; // just for the sake of it...
+
+   // Notify resumer that we have been resumed
+   if (pthread_kill(main_thread, srSignal) == -1) {
+      perror("pthread_kill");
+      exit(1);
+   }
+//   printf("this is thread %d: and I've told Master!!\n", mypos);
+
+}
+
+
+void
+suspendOrResume(pthread_t thread, int i)
+{
+   sigset_t oss;
+
+   // Mask out suspend/resume signal until we explicitly wait for it
+   sigprocmask(SIG_BLOCK, &srSigset, &oss);
+
+   // Send signal to suspend or resume the thread
+   if (pthread_kill(thread, srSignal) == -1) {
+      perror("pthread_kill");
+      exit(1);
+   }
+
+//   printf("sent signal to %d...", i);
+   // Wait for notification from thread being suspended/resumed
+   while (sigwaitinfo(&srSigset, NULL) == -1) {
+      // Interrupted by SIGSTOP in gdb
+      if(errno != EINTR) {
+      	perror("sigwaitinfo");
+        exit(1);
+      }
+   }
+
+   // Restore original signal mask
+   sigprocmask(SIG_SETMASK, &oss, NULL);
+
+//   printf("... okay, %d suspended\n", i);
+}
+
+
+
+void
+initSignalling(void)
+{
+   struct sigaction sa;
+
+   // Signal mask for suspend/resume
+   sigemptyset(&srSigset);
+   sigaddset(&srSigset, srSignal);
+
+   // Set up signal handler for suspend/resume
+   sa.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
+   sa.sa_sigaction = ptiSrSigHandler;
+   sigfillset(&sa.sa_mask);
+   sigdelset(&sa.sa_mask, (__SIGRTMIN+1));
+   if (sigaction(srSignal, &sa, 0) == -1) {
+      perror("sigaction");
+      exit(1);
+   }
+
+   // Unblock suspend signal
+   sigprocmask(SIG_UNBLOCK, &srSigset, 0);
+
+   main_thread = pthread_self();
+}
+
+
+void* setup_altstack(void) {
+   stack_t ss;
+
+       ss.ss_sp = malloc(20*1024);
+       if (ss.ss_sp == 0) {
+	   return NULL;
+       }
+       ss.ss_size = 20*1024;
+       ss.ss_flags = 0;
+       
+       if (sigaltstack(&ss, NULL) == -1) {
+	   perror("sigaltstack");
+           return NULL;
+       }
+       return ss.ss_sp;
+}
+
+void takedown_altstack(void* stack) {
+   struct sigaltstack ss;
+   int result;
+   
+   ss.ss_flags = SS_DISABLE;
+   ss.ss_sp = (void*)47;  // This value should be ignored when ss_flags is SS_DISABLE
+   ss.ss_size = 29;       // This value should be ignored when ss_flags is SS_DISABLE
+   
+   {
+       result = sigaltstack(&ss, NULL);
+       free(stack);
+   }
+}
+
+void *threadfunc(void *arg) {
+   int mypos = (int)arg;
+   int i;
+   long square = 1;
+   void* altstack = setup_altstack();
+   
+   pthread_setspecific(pKey, arg);
+   for (i=0; i < 1000; i++) {
+      square = i*i + square*mypos;
+   }
+
+// wait for signal   
+      while (finishedArray[mypos] == 0) {
+   struct timespec req, rem;
+
+   req.tv_sec = 0;
+   req.tv_nsec = 5 * 1000 * 1000;
+
+   nanosleep(&req, &rem);
+         
+   };
+   
+   finishedArray[mypos]++;
+   
+   takedown_altstack(altstack);
+   
+   return NULL;
+}
+
+
+int main(int argc, char ** argv) {
+  pthread_t threads[THREAD_COUNT];
+  pthread_attr_t attr;
+  int result;
+  int i;
+  int iteration;
+  int finished;
+
+  initSignalling();
+  
+  pthread_attr_init(&attr);
+  pthread_attr_setstacksize(&attr, 128*1024);
+
+  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+  
+  pthread_key_create(&pKey, NULL);
+
+  for (iteration = 0; iteration < ITER_COUNT; iteration++) {
+#if 0
+     if ((iteration % 100) == 0) {
+	printf("\nStarting run series %i: ", iteration);
+     }
+
+     if ((iteration % 10) == 0) {
+	printf(".");
+        fflush(stdout);
+     }
+#endif
+
+      // Clear array
+      for (i = 0; i< THREAD_COUNT; i++) {
+	 finishedArray[i] = 0;
+      }
+
+      // Start threads
+      for (i = 0; i< THREAD_COUNT; i++) {
+	 result = pthread_create(&threads[i], &attr, threadfunc, (void*)i);
+	 if (result != 0) {
+	    perror("pthread_create");
+	    exit(1);
+	 }
+    }
+    
+//    printf("all threads started\n");
+    // suspend threads
+    for (i = 0; i< THREAD_COUNT; i++) {
+    	suspendOrResume(threads[i], i);
+    }
+    
+//    printf("now all threads are suspended\n");
+    
+    // resume threads
+    for (i = 0; i< THREAD_COUNT; i++) {
+    	suspendOrResume(threads[i], i);
+    }
+    
+
+      // Join threads
+/*      
+      printf("about to join...");
+      for (i = 0; i< THREAD_COUNT; i++) {
+	 result = pthread_join(threads[i], NULL);
+	 if (result != 0) {
+	    perror("pthread_join");
+	    exit(1);
+	 }
+      }
+      
+      printf("...joined");
+*/      
+//      printf("Spin waiting for results\n");
+      finished = 1;
+      do {
+   struct timespec req, rem;
+
+   req.tv_sec = 0;
+   req.tv_nsec = 5 * 1000 * 1000;
+   finished = 1;
+
+   nanosleep(&req, &rem);
+         
+//         sleep(1);
+         for (i = 0; i< THREAD_COUNT; i++) {
+            if (finishedArray[i] < 2) {
+               finished = 0;
+//               printf("no result at: %d, value: %d\n", i, finishedArray[i]);
+               break;
+            }
+	 }
+//         sleep(1);
+      } while (!finished);
+
+  }
+
+  printf("PASSED\n");
+  return 0;
+}
diff --git a/none/tests/susphello.stderr.exp b/none/tests/susphello.stderr.exp
new file mode 100644
index 0000000..139597f
--- /dev/null
+++ b/none/tests/susphello.stderr.exp
@@ -0,0 +1,2 @@
+
+
diff --git a/none/tests/susphello.stdout.exp b/none/tests/susphello.stdout.exp
new file mode 100644
index 0000000..53cdf1e
--- /dev/null
+++ b/none/tests/susphello.stdout.exp
@@ -0,0 +1 @@
+PASSED
diff --git a/none/tests/susphello.vgtest b/none/tests/susphello.vgtest
new file mode 100644
index 0000000..0a163ac
--- /dev/null
+++ b/none/tests/susphello.vgtest
@@ -0,0 +1 @@
+prog: susphello