]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Fast-track pthread_mutex_{lock,unlock} in the scheduler. This reduces
authorJulian Seward <jseward@acm.org>
Wed, 24 Apr 2002 01:57:27 +0000 (01:57 +0000)
committerJulian Seward <jseward@acm.org>
Wed, 24 Apr 2002 01:57:27 +0000 (01:57 +0000)
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

coregrind/vg_scheduler.c
tests/pth_mutexspeed.c [new file with mode: 0644]
vg_scheduler.c

index 32201b93839a26886f4989d35e2a0c5cf82f0795..a8d575e8798d7b0f6c250edd6cad58ad72874747 100644 (file)
@@ -143,6 +143,11 @@ static void do_nontrivial_clientreq ( ThreadId tid );
 
 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 @@ Bool fd_is_blockful ( Int fd )
 
 
 
-/* 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:
+
+   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.
 
-   If the request is non-trivial, return False; a more capable but
-   slower mechanism will deal with it.  
+   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 @@ Bool maybe_do_trivial_clientreq ( ThreadId tid )
       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 @@ VgSchedReturnCode VG_(scheduler) ( void )
 
       /* ======================= Phase 0 of 3 =======================
         Be paranoid.  Always a good idea. */
+     stage1:
       scheduler_sanity();
 
       /* ======================= Phase 1 of 3 =======================
@@ -1242,18 +1260,20 @@ VgSchedReturnCode VG_(scheduler) ( void )
          }
 
          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 @@ void release_one_thread_waiting_on_mutex ( pthread_mutex_t* mutex,
 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 @@ void do_pthread_mutex_lock( ThreadId tid,
 
 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 );
diff --git a/tests/pth_mutexspeed.c b/tests/pth_mutexspeed.c
new file mode 100644 (file)
index 0000000..ad263e1
--- /dev/null
@@ -0,0 +1,19 @@
+
+#include <stdio.h>
+#include <assert.h>
+#include <pthread.h>
+
+int main ( void )
+{
+   const int n = 100000;
+   int i, r;
+   pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;
+   printf("begin %d lock--unlocks\n", n);
+   for (i = 0; i < n; i++) {
+      r =  pthread_mutex_lock(&mx);
+      r |= pthread_mutex_unlock(&mx);
+      assert(r == 0);
+   }
+   printf("done  %d lock--unlocks\n", n);
+   return 0;
+}
index 32201b93839a26886f4989d35e2a0c5cf82f0795..a8d575e8798d7b0f6c250edd6cad58ad72874747 100644 (file)
@@ -143,6 +143,11 @@ static void do_nontrivial_clientreq ( ThreadId tid );
 
 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 @@ Bool fd_is_blockful ( Int fd )
 
 
 
-/* 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:
+
+   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.
 
-   If the request is non-trivial, return False; a more capable but
-   slower mechanism will deal with it.  
+   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 @@ Bool maybe_do_trivial_clientreq ( ThreadId tid )
       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 @@ VgSchedReturnCode VG_(scheduler) ( void )
 
       /* ======================= Phase 0 of 3 =======================
         Be paranoid.  Always a good idea. */
+     stage1:
       scheduler_sanity();
 
       /* ======================= Phase 1 of 3 =======================
@@ -1242,18 +1260,20 @@ VgSchedReturnCode VG_(scheduler) ( void )
          }
 
          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 @@ void release_one_thread_waiting_on_mutex ( pthread_mutex_t* mutex,
 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 @@ void do_pthread_mutex_lock( ThreadId tid,
 
 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 );