Patchwork simulate-thread changes to avoid infinite loops

login
register
mail settings
Submitter Aldy Hernandez
Date Oct. 10, 2011, 3:54 p.m.
Message ID <4E931534.90400@redhat.com>
Download mbox | patch
Permalink /patch/118768/
State New
Headers show

Comments

Aldy Hernandez - Oct. 10, 2011, 3:54 p.m.
Hi folks.

This was a change Andrew had made to the simulate-thread framework to 
avoid infinite loops in hostile threads.  I forgot to merge this in my 
original submission, and am doing so now.

The original patch is here:

http://permalink.gmane.org/gmane.comp.gcc.patches/247678

My patch is a mere sed run on Andrew's patch, plus a few fixed typos in 
the comments.  I also kept the original simulate_thread_fini name as 
opposed to the __gdb_memmodel_fini name Andrew has (to keep names 
homogeneous throughout).

Tested on x86-64 Linux by running "make check 
RUNTESTFLAGS=simulate-thread.exp" on the build tree I already had for 
the original submission.

OK for trunk?
* gcc.dg/simulate-thread/simulate-thread.gdb: Call
	wrappers for *other_threads() and *final_verify().
	* gcc.dg/simulate-thread/simulate-thread.h
	(simulate_thread_wrapper_other_threads): New.
	(simulate_thread_wrapper_final_verify): New.
Jeff Law - Oct. 10, 2011, 4:20 p.m.
On 10/10/11 09:54, Aldy Hernandez wrote:
> Hi folks.
>
> This was a change Andrew had made to the simulate-thread framework to
> avoid infinite loops in hostile threads. I forgot to merge this in my
> original submission, and am doing so now.
>
> The original patch is here:
>
> http://permalink.gmane.org/gmane.comp.gcc.patches/247678
>
> My patch is a mere sed run on Andrew's patch, plus a few fixed typos in
> the comments. I also kept the original simulate_thread_fini name as
> opposed to the __gdb_memmodel_fini name Andrew has (to keep names
> homogeneous throughout).
>
> Tested on x86-64 Linux by running "make check
> RUNTESTFLAGS=simulate-thread.exp" on the build tree I already had for
> the original submission.
>
> OK for trunk?
OK.
jeff

Patch

Index: gcc.dg/simulate-thread/simulate-thread.gdb
===================================================================
--- gcc.dg/simulate-thread/simulate-thread.gdb	(revision 179751)
+++ gcc.dg/simulate-thread/simulate-thread.gdb	(working copy)
@@ -5,13 +5,13 @@  run
 
 set $ret = 0
 while (simulate_thread_fini != 1) && (! $ret)
-  call simulate_thread_other_threads()
+  call simulate_thread_wrapper_other_threads()
   stepi
   set $ret |= simulate_thread_step_verify()
 end
 
 if (! $ret)
-  set $ret |= simulate_thread_final_verify()
+  set $ret |= simulate_thread_wrapper_final_verify()
 end
 continue
 quit $ret
Index: gcc.dg/simulate-thread/simulate-thread.h
===================================================================
--- gcc.dg/simulate-thread/simulate-thread.h	(revision 179751)
+++ gcc.dg/simulate-thread/simulate-thread.h	(working copy)
@@ -5,3 +5,107 @@  simulate_thread_done ()
 {
   simulate_thread_fini = 1;
 }
+
+/* A hostile thread is one which changes a memory location so quickly
+   that another thread may never see the same value again.  This is
+   simulated when simulate_thread_other_thread() is defined to modify
+   a memory location every cycle.
+
+   A process implementing a dependency on this value can run into
+   difficulties with such a hostile thread.  For instance,
+   implementing an add with a compare_and_swap loop goes something
+   like:
+
+     expected = *mem;
+   loop:
+     new = expected += value;
+     if (!succeed (expected = compare_and_swap (mem, expected, new)))
+       goto loop;
+
+   If the content of 'mem' are changed every cycle by
+   simulate_thread_other_thread () this will become an infinite loop
+   since the value *mem will never be 'expected' by the time the
+   compare_and_swap is executed.
+
+   HOSTILE_THREAD_THRESHOLD defines the number of intructions which a
+   program will execute before triggering the hostile thread
+   pause. The pause will last for HOSTILE_THREAD_PAUSE instructions,
+   and then the counter will reset and begin again.  During the pause
+   period, simulate_thread_other_thread will not be called.
+
+   This provides a chance for forward progress to be made and the
+   infinite loop to be avoided.
+
+   If the testcase defines HOSTILE_PAUSE_ERROR, then it will be
+   considered an RUNTIME FAILURE if the hostile pause is triggered.
+   This will allow to test for guaranteed forward progress routines.
+
+   If the default values for HOSTILE_THREAD_THRESHOLD or
+   HOSTILE_THREAD_PAUSE are insufficient, then the testcase may
+   override these by defining the values before including this file.
+
+   Most testcase are intended to run for very short periods of time,
+   so these defaults are considered to be high enough to not trigger
+   on a typical case, but not drag the test time out too much if a
+   hostile condition is interferring.  */
+
+  
+/* Define the threshold to start pausing the hostile thread.  */
+#if !defined (HOSTILE_THREAD_THRESHOLD)
+#define HOSTILE_THREAD_THRESHOLD 	500
+#endif
+
+/* Define the length of pause in cycles for the hostile thread to pause to
+   allow forward progress to be made.  */
+#if !defined (HOSTILE_THREAD_PAUSE)
+#define HOSTILE_THREAD_PAUSE	20
+#endif
+
+void simulate_thread_other_threads (void);
+int simulate_thread_final_verify (void);
+
+static int simulate_thread_hostile_pause = 0;
+
+/* This function wraps simulate_thread_other_threads an monitors for
+   an infinite loop.  If the threshold value HOSTILE_THREAD_THRESHOLD
+   is reached, the other_thread process is paused for
+   HOSTILE_THREAD_PAUSE cycles before resuming, and the counters start
+   again.  */
+void
+simulate_thread_wrapper_other_threads()
+{
+  static int count = 0;
+  static int pause = 0;
+
+  if (++count >= HOSTILE_THREAD_THRESHOLD)
+    {
+      if (!simulate_thread_hostile_pause)
+        simulate_thread_hostile_pause = 1;
+
+      /* Count cycles before calling the hostile thread again.  */
+      if (pause++ < HOSTILE_THREAD_PAUSE)
+	return;
+
+      /* Reset the pause counter, as well as the thread counter.  */
+      pause = 0;
+      count = 0;
+    }
+  simulate_thread_other_threads ();
+}
+
+
+/* If the test case defines HOSTILE_PAUSE_ERROR, then the test case
+   will fail execution if it had a hostile pause.  */
+int
+simulate_thread_wrapper_final_verify ()
+{
+  int ret = simulate_thread_final_verify ();
+#if defined (HOSTILE_PAUSE_ERROR)
+  if (simulate_thread_hostile_pause)
+    {
+      printf ("FAIL: Forward progress made only by pausing hostile thread\n");
+      ret = ret | 1;    /* 0 indicates proper comnpletion.  */
+    }
+#endif
+  return ret;
+}