Fast-track pthread_mutex_{lock,unlock} in the scheduler.  This reduces
their cost by about a factor of 20, which fixes the performance probs
observed with Opera.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@121 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/vg_scheduler.c b/vg_scheduler.c
index 32201b9..a8d575e 100644
--- a/vg_scheduler.c
+++ b/vg_scheduler.c
@@ -143,6 +143,11 @@
 
 static void scheduler_sanity ( void );
 
+static void do_pthread_mutex_unlock ( ThreadId, 
+                                      void* /* pthread_cond_t* */ );
+static void do_pthread_mutex_lock ( ThreadId, Bool, 
+                                    void* /* pthread_cond_t* */ );
+
 
 /* ---------------------------------------------------------------------
    Helper functions for the scheduler.
@@ -590,14 +595,16 @@
 
 
 
-/* Do a purely thread-local request for tid, and put the result in its
-   %EDX, without changing its scheduling state in any way, nor that of
-   any other threads.  Return True if so.
+/* Possibly do a for tid.  Return values are:
 
-   If the request is non-trivial, return False; a more capable but
-   slower mechanism will deal with it.  
+   True = request done.  Thread may or may not be still runnable;
+   caller must check.  If it is still runnable, the result will be in
+   the thread's %EDX as expected.
+
+   False = request not done.  A more capable but slower mechanism will
+   deal with it.  
 */
-static 
+static
 Bool maybe_do_trivial_clientreq ( ThreadId tid )
 {
 #  define SIMPLE_RETURN(vvv)                      \
@@ -655,6 +662,16 @@
       case VG_USERREQ__READ_MILLISECOND_TIMER:
          SIMPLE_RETURN(VG_(read_millisecond_timer)());
 
+      case VG_USERREQ__PTHREAD_MUTEX_UNLOCK:
+         do_pthread_mutex_unlock( tid, (void *)(arg[1]) );
+         return True;
+
+      /* This may make thread tid non-runnable, but the scheduler
+         checks for that on return from this function. */
+      case VG_USERREQ__PTHREAD_MUTEX_LOCK:
+         do_pthread_mutex_lock( tid, False, (void *)(arg[1]) );
+         return True;
+
       default:
          /* Too hard; wimp out. */
          return False;
@@ -1092,6 +1109,7 @@
 
       /* ======================= Phase 0 of 3 =======================
 	 Be paranoid.  Always a good idea. */
+     stage1:
       scheduler_sanity();
 
       /* ======================= Phase 1 of 3 =======================
@@ -1242,18 +1260,20 @@
          }
 
          if (trc == VG_TRC_EBP_JMP_CLIENTREQ) {
-            Bool is_triv = maybe_do_trivial_clientreq(tid);
-            if (is_triv) {
-               /* NOTE: a trivial request is something like a call to
-                  malloc() or free().  It DOES NOT change the
-                  Runnability of this thread nor the status of any
-                  other thread; it is purely thread-local. */
-               continue; /* with this thread */
+            Bool done = maybe_do_trivial_clientreq(tid);
+            if (done) {
+               /* The request is done.  We try and continue with the
+                  same thread if still runnable.  If not, go back to
+                  Stage 1 to select a new thread to run. */
+               if (vg_threads[tid].status == VgTs_Runnable)
+                  continue; /* with this thread */
+               else
+                  goto stage1;
 	    }
 	 }
 
-	 /* It's a non-trivial event.  Give up running this thread and
-            handle things the expensive way. */
+	 /* It's an event we can't quickly deal with.  Give up running
+            this thread and handle things the expensive way. */
 	 break;
       }
 
@@ -1753,13 +1773,15 @@
 static
 void do_pthread_mutex_lock( ThreadId tid, 
                             Bool is_trylock, 
-                            pthread_mutex_t *mutex )
+                            void* /* pthread_mutex_t* */ mutexV )
 {
    Char  msg_buf[100];
    Char* caller
       = is_trylock ? "pthread_mutex_lock   "
                    : "pthread_mutex_trylock";
 
+   pthread_mutex_t* mutex = (pthread_mutex_t*)mutexV;
+
    if (VG_(clo_trace_pthread_level) >= 2) {
       VG_(sprintf)(msg_buf, "%s    mx %p ...", caller, mutex );
       print_pthread_event(tid, msg_buf);
@@ -1847,9 +1869,10 @@
 
 static
 void do_pthread_mutex_unlock ( ThreadId tid,
-                               pthread_mutex_t *mutex )
+                               void* /* pthread_mutex_t* */ mutexV )
 {
    Char msg_buf[100];
+   pthread_mutex_t* mutex = (pthread_mutex_t*)mutexV;
 
    if (VG_(clo_trace_pthread_level) >= 2) {
       VG_(sprintf)(msg_buf, "pthread_mutex_unlock     mx %p ...", mutex );