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.
-/* 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) \
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;
/* ======================= Phase 0 of 3 =======================
Be paranoid. Always a good idea. */
+ stage1:
scheduler_sanity();
/* ======================= Phase 1 of 3 =======================
}
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;
}
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);
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 );
--- /dev/null
+
+#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;
+}
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.
-/* 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) \
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;
/* ======================= Phase 0 of 3 =======================
Be paranoid. Always a good idea. */
+ stage1:
scheduler_sanity();
/* ======================= Phase 1 of 3 =======================
}
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;
}
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);
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 );