]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Get the core thread modelling code compiling again - uncomment the
authorTom Hughes <tom@compton.nu>
Tue, 17 Jan 2006 15:09:07 +0000 (15:09 +0000)
committerTom Hughes <tom@compton.nu>
Tue, 17 Jan 2006 15:09:07 +0000 (15:09 +0000)
code, fix the includes and switch to OSets instead of SkipLists.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5542

coregrind/m_threadmodel.c
coregrind/pub_core_threadmodel.h

index 9787ac9e4a5a1bb6cd40619456374802fc9266fa..6e3c64f3a03918261d2df32975ed8a41db18324b 100644 (file)
  */
 
 #include "pub_core_basics.h"
+#include "pub_core_errormgr.h"
+#include "pub_core_execontext.h"
+#include "pub_core_libcassert.h"
+#include "pub_core_libcbase.h"
+#include "pub_core_libcprint.h"
+#include "pub_core_mallocfree.h"
+#include "pub_core_oset.h"
+#include "pub_core_threadmodel.h"
+#include "pub_core_threadstate.h"
+#include "pub_core_tooliface.h"
 
-//:: struct thread;
-//:: struct mutex;
-//:: struct condvar;
-//:: 
-//:: static const Bool debug_thread = False;
-//:: static const Bool debug_mutex = False;
-//:: 
-//:: /* --------------------------------------------------
-//::    Thread lifetime
-//::    
-//::    Threads are all expressed in terms of internal ThreadIds.  The
-//::    thread library interface needs to map from the library's identifers
-//::    to ThreadIds.
-//::    -------------------------------------------------- */
-//:: 
-//:: /* Per-thread state.  We maintain our own here rather than hanging it
-//::    off ThreadState, so that we have the option of not having a 1:1
-//::    relationship between modelled threads and Valgrind threads. */
-//:: struct thread
-//:: {
-//::    ThreadId       tid;
-//::    ThreadId       creator;
-//:: 
-//::    Bool           detached;       /* thread is detached */
-//:: 
-//::    enum thread_state {
-//::       TS_Alive,                   /* alive */
-//::       TS_Zombie,          /* waiting to be joined on (detached is False) */
-//::       TS_Dead,                    /* all dead */
-//::       
-//::       TS_Running,         /* running */
-//::       TS_MutexBlocked,            /* blocked on mutex */
-//::       TS_CVBlocked,               /* blocked on condvar */
-//::       TS_JoinBlocked,             /* blocked in join */
-//::    }              state;
-//:: 
-//::    struct mutex           *mx_blocked; /* mutex we're blocked on (state==TS_MutexBlocked) */
-//::    struct condvar *cv_blocked; /* condvar we're blocked on (state==TS_CVBlocked) */
-//::    struct thread  *th_blocked; /* thread we're blocked on (state==TS_JoinBlocked) */
-//:: 
-//::    ExeContext     *ec_created;    /* where created */
-//::    ExeContext     *ec_blocked;    /* where blocked/unblocked */
-//:: };
-//:: 
-//:: enum thread_error
-//:: {
-//::    THE_NotExist,          /* thread doesn't exist */
-//::    THE_NotAlive,          /* thread isn't alive (use after death) */
-//::    THE_Rebirth,                   /* thread already alive */
-//::    THE_Blocked,                   /* thread not supposed to be blocked */
-//::    THE_NotBlocked,                /* thread supposed to be blocked */
-//::    THE_Detached,          /* thread is detached */
-//:: };
-//:: 
-//:: struct thread_error_data
-//:: {
-//::    enum thread_error err;
-//::    struct thread     *th;
-//::    const Char        *action;
-//:: };
-//:: 
-//:: static const Char *pp_threadstate(const struct thread *th)
-//:: {
-//::    if (th == NULL)
-//::       return "non-existent";
-//:: 
-//::    switch(th->state) {
-//::    case TS_Alive: return "alive";
-//::    case TS_Zombie:        return "zombie";
-//::    case TS_Dead:  return "dead";
-//::    case TS_Running:       return "running";
-//::    case TS_MutexBlocked:return "mutex-blocked";
-//::    case TS_CVBlocked:     return "cv-blocked";
-//::    case TS_JoinBlocked:   return "join-blocked";
-//::    default:               return "???";
-//::    }
-//:: }
-//:: 
-//:: static void thread_validate(struct thread *th)
-//:: {
-//::    switch(th->state) {
-//::    case TS_Alive:
-//::    case TS_Running:
-//::    case TS_Dead:
-//::    case TS_Zombie:
-//::       vg_assert(th->mx_blocked == NULL);
-//::       vg_assert(th->cv_blocked == NULL);
-//::       vg_assert(th->th_blocked == NULL);
-//::       break;
-//:: 
-//::    case TS_MutexBlocked:
-//::       vg_assert(th->mx_blocked != NULL);
-//::       vg_assert(th->cv_blocked == NULL);
-//::       vg_assert(th->th_blocked == NULL);
-//::       break;
-//:: 
-//::    case TS_CVBlocked:
-//::       vg_assert(th->mx_blocked == NULL);
-//::       vg_assert(th->cv_blocked != NULL);
-//::       vg_assert(th->th_blocked == NULL);
-//::       break;
-//:: 
-//::    case TS_JoinBlocked:
-//::       vg_assert(th->mx_blocked == NULL);
-//::       vg_assert(th->cv_blocked == NULL);
-//::       vg_assert(th->th_blocked != NULL);
-//::       break;
-//::    }
-//:: }
-//:: 
-//:: static void thread_setstate(struct thread *th, enum thread_state state)
-//:: {
-//::    ExeContext *ec;
-//:: 
-//::    if (th->state == state)
-//::       return;
-//:: 
-//::    ec = VG_(record_ExeContext)(th->tid);
-//:: 
-//::    switch(state) {
-//::    case TS_Alive:
-//::    case TS_Dead:
-//::       th->ec_created = ec;
-//::       break;
-//:: 
-//::    case TS_Running:
-//::    case TS_MutexBlocked:
-//::    case TS_CVBlocked:
-//::    case TS_JoinBlocked:
-//::    case TS_Zombie:
-//::       th->ec_blocked = ec;
-//::    }
-//:: 
-//::    th->state = state;
-//::    if (debug_thread)
-//::       VG_(printf)("setting thread(%d) -> %s\n", th->tid, pp_threadstate(th));
-//::    thread_validate(th);
-//:: }
-//:: 
-//:: static void do_thread_run(struct thread *th)
-//:: {
-//::    th->mx_blocked = NULL;
-//::    th->cv_blocked = NULL;
-//::    th->th_blocked = NULL;
-//::    thread_setstate(th, TS_Running);
-//:: }
-//:: 
-//:: static void do_thread_block_mutex(struct thread *th, struct mutex *mx)
-//:: {
-//::    th->mx_blocked = mx;
-//::    th->cv_blocked = NULL;
-//::    th->th_blocked = NULL;
-//::    thread_setstate(th, TS_MutexBlocked);
-//:: }
-//:: 
-//:: static void do_thread_block_condvar(struct thread *th, struct condvar *cv)
-//:: {
-//::    th->mx_blocked = NULL;
-//::    th->cv_blocked = cv;
-//::    th->th_blocked = NULL;
-//::    thread_setstate(th, TS_CVBlocked);
-//:: }
-//:: 
-//:: static void do_thread_block_join(struct thread *th, struct thread *joinee)
-//:: {
-//::    th->mx_blocked = NULL;
-//::    th->cv_blocked = NULL;
-//::    th->th_blocked = joinee;
-//::    thread_setstate(th, TS_JoinBlocked);
-//:: }
-//:: 
-//:: static void do_thread_block_zombie(struct thread *th)
-//:: {
-//::    th->mx_blocked = NULL;
-//::    th->cv_blocked = NULL;
-//::    th->th_blocked = NULL;
-//::    thread_setstate(th, TS_Zombie);
-//:: }
-//:: 
-//:: static void do_thread_dead(struct thread *th)
-//:: {
-//::    th->mx_blocked = NULL;
-//::    th->cv_blocked = NULL;
-//::    th->th_blocked = NULL;
-//::    thread_setstate(th, TS_Dead);
-//:: }
-//:: 
-//:: static SkipList sk_threads = VG_SKIPLIST_INIT(struct thread, tid, VG_(cmp_UInt), NULL, VG_AR_CORE);
-//:: 
-//:: static struct thread *thread_get(ThreadId tid)
-//:: {
-//::    return VG_(SkipList_Find_Exact)(&sk_threads, &tid);
-//:: }
-//:: 
-//:: static void thread_report(ThreadId tid, enum thread_error err, const Char *action)
-//:: {
-//::    Char *errstr = "?";
-//::    struct thread *th = thread_get(tid);
-//::    struct thread_error_data errdata;
-//:: 
-//::    switch(err) {
-//::    case THE_NotExist:     errstr = "non existent"; break;
-//::    case THE_NotAlive:     errstr = "not alive"; break;
-//::    case THE_Rebirth:      errstr = "re-born"; break;
-//::    case THE_Blocked:      errstr = "blocked"; break;
-//::    case THE_NotBlocked:   errstr = "not blocked"; break;
-//::    case THE_Detached:     errstr = "detached"; break;
-//::    }
-//:: 
-//::    errdata.err = err;
-//::    errdata.th = th;
-//::    errdata.action = action;
-//::    
-//::    VG_(maybe_record_error)(VG_(get_running_tid)(), ThreadErr, 0, errstr, &errdata);
-//:: }
-//:: 
-//:: static void pp_thread_error(Error *err)
-//:: {
-//::    struct thread_error_data *errdata = VG_(get_error_extra)(err);
-//::    struct thread *th = errdata->th;
-//::    Char *errstr = VG_(get_error_string)(err);
-//::    
-//::    VG_(message)(Vg_UserMsg, "Found %s thread in state %s while %s",
-//::                 errstr, pp_threadstate(th), errdata->action);
-//::    VG_(pp_ExeContext)(VG_(get_error_where)(err));
-//:: 
-//::    if (th) {
-//::       VG_(message)(Vg_UserMsg, " Thread %d was %s",
-//::              th->tid, th->state == TS_Dead ? "destroyed" : "created");
-//::       VG_(pp_ExeContext)(th->ec_created);
-//::    }
-//:: }
-//:: 
-//:: /* Thread creation */
-//:: void VG_(tm_thread_create)(ThreadId creator, ThreadId tid, Bool detached)
-//:: {
-//::    struct thread *th = thread_get(tid);
-//:: 
-//::    if (debug_thread)
-//::       VG_(printf)("thread %d creates %d %s\n", creator, tid, detached ? "detached" : "");
-//::    if (th != NULL) {
-//::       if (th->state != TS_Dead)
-//::    thread_report(tid, THE_Rebirth, "creating");
-//::    } else {
-//::       th = VG_(SkipNode_Alloc)(&sk_threads);
-//::       th->tid = tid;
-//::       VG_(SkipList_Insert)(&sk_threads, th);
-//::    }
-//:: 
-//::    th->creator = creator;
-//::    th->detached = detached;
-//::    th->mx_blocked = NULL;
-//::    th->cv_blocked = NULL;
-//::    th->th_blocked = NULL;
-//:: 
-//::    thread_setstate(th, TS_Alive);
-//::    do_thread_run(th);
-//:: }
-//:: 
-//:: Bool VG_(tm_thread_exists)(ThreadId tid)
-//:: {
-//::    struct thread *th = thread_get(tid);
-//:: 
-//::    return th && th->state != TS_Dead;
-//:: }
-//:: 
-//:: /* A thread is terminating itself
-//::     - fails if tid has already terminated
-//::     - if detached, tid becomes invalid for all further operations
-//::     - if not detached, the thread remains in a Zombie state until
-//::       someone joins on it
-//::  */
-//:: void VG_(tm_thread_exit)(ThreadId tid)
-//:: {
-//::    struct thread *th = thread_get(tid);
-//:: 
-//::    if (th == NULL)
-//::       thread_report(tid, THE_NotExist, "exiting");
-//::    else {
-//::       struct thread *joiner;
-//:: 
-//::       switch(th->state) {
-//::       case TS_Dead:
-//::       case TS_Zombie:     /* already exited once */
-//::    thread_report(tid, THE_NotAlive, "exiting");
-//::    break;
-//:: 
-//::       case TS_MutexBlocked:
-//::       case TS_CVBlocked:
-//::       case TS_JoinBlocked:
-//::       thread_report(tid, THE_Blocked, "exiting");
-//::       break;
-//:: 
-//::       case TS_Alive:
-//::       case TS_Running:
-//::    /* OK */
-//::    break;
-//::       }
-//:: 
-//::       /* ugly - walk all threads to find people joining with us */
-//::       /* In pthreads its an error to have multiple joiners, but that
-//::    seems a bit specific to implement here; there should a way
-//::    for the thread library binding to handle this. */
-//::       for(joiner = VG_(SkipNode_First)(&sk_threads);
-//::     joiner != NULL;
-//::     joiner = VG_(SkipNode_Next)(&sk_threads, joiner)) {
-//::    if (joiner->state == TS_JoinBlocked && joiner->th_blocked == th) {
-//::       /* found someone - wake them up */
-//::       do_thread_run(joiner);
-//:: 
-//::       /* we're dead */
-//::       do_thread_dead(th);
-//::    }
-//::       }
-//:: 
-//::       if (th->state != TS_Dead)
-//::    do_thread_block_zombie(th);
-//::    }
-//:: }
-//:: 
-//:: void VG_(tm_thread_detach)(ThreadId tid)
-//:: {
-//::    struct thread *th = thread_get(tid);
-//:: 
-//::    if (th == NULL)
-//::       thread_report(tid, THE_NotExist, "detaching");
-//::    else {
-//::       if (th->detached)
-//::    thread_report(tid, THE_Detached, "detaching");
-//::       else {
-//::    /* XXX look for waiters */
-//::    th->detached = True;
-//::       }
-//::    }
-//:: }
-//:: 
-//:: /* One thread blocks until another has terminated
-//::     - fails if joinee is detached
-//::     - fails if joinee doesn't exist
-//::     - once the join completes, joinee is dead
-//::  */
-//:: void VG_(tm_thread_join)(ThreadId joinerid, ThreadId joineeid)
-//:: {
-//::    struct thread *joiner = thread_get(joinerid);
-//::    struct thread *joinee = thread_get(joineeid);
-//:: 
-//::    /* First, check the joinee thread's state */
-//::    if (joinee == NULL)
-//::       thread_report(joineeid, THE_NotExist, "joining as joinee");
-//::    else {
-//::       switch(joinee->state) {
-//::       case TS_Alive:              /* really shouldn't see them in this state... */
-//::       case TS_Running:
-//::       case TS_Zombie:
-//::       case TS_MutexBlocked:
-//::       case TS_CVBlocked:
-//::       case TS_JoinBlocked:
-//::    /* OK */
-//::    break;
-//:: 
-//::       case TS_Dead:
-//::    thread_report(joineeid, THE_NotAlive, "joining as joinee");
-//::    break;
-//::       }
-//::    }
-//:: 
-//::    /* now the joiner... */
-//::    if (joiner == NULL)
-//::       thread_report(joineeid, THE_NotExist, "joining as joiner");
-//::    else {
-//::       switch(joiner->state) {
-//::       case TS_Alive:              /* ? */
-//::       case TS_Running:            /* OK */
-//::    break;
-//:: 
-//::       case TS_Zombie:             /* back from the dead */
-//::       case TS_Dead:
-//::    thread_report(joineeid, THE_NotAlive, "joining as joiner");
-//::    break;
-//:: 
-//::       case TS_MutexBlocked:
-//::       case TS_CVBlocked:
-//::       case TS_JoinBlocked:
-//::    thread_report(joineeid, THE_Blocked, "joining as joiner");
-//::    break;
-//::       }
-//:: 
-//::       if (joinee->detached)
-//::    thread_report(joineeid, THE_Detached, "joining as joiner");
-//::       else {
-//::    /* block if the joinee hasn't exited yet */
-//::    if (joinee) {
-//::       switch(joinee->state) {
-//::       case TS_Dead:
-//::          break;
-//:: 
-//::       default:
-//::          if (joinee->state == TS_Zombie)
-//::             do_thread_dead(joinee);
-//::          else
-//::             do_thread_block_join(joiner, joinee);
-//::       }
-//::    }
-//::       }
-//::    }
-//:: }
-//:: 
-//:: /* Context switch to a new thread */
-//:: void VG_(tm_thread_switchto)(ThreadId tid)
-//:: {
-//::    VG_TRACK( thread_run, tid );
-//:: }
-//:: 
-//:: static void thread_block_mutex(ThreadId tid, struct mutex *mx)
-//:: {
-//::    struct thread *th = thread_get(tid);
-//:: 
-//::    if (th == NULL) {
-//::       /* should an unknown thread doing something make it spring to life? */
-//::       thread_report(tid, THE_NotExist, "blocking on mutex");
-//::       return;
-//::    }
-//::    switch(th->state) {
-//::    case TS_Dead:
-//::    case TS_Zombie:
-//::       thread_report(th->tid, THE_NotAlive, "blocking on mutex");
-//::       break;
-//:: 
-//::    case TS_MutexBlocked:
-//::    case TS_CVBlocked:
-//::    case TS_JoinBlocked:
-//::       thread_report(th->tid, THE_Blocked, "blocking on mutex");
-//::       break;
-//:: 
-//::    case TS_Alive:
-//::    case TS_Running:               /* OK */
-//::       break;
-//::    }
-//:: 
-//::    do_thread_block_mutex(th, mx);
-//:: }
-//:: 
-//:: static void thread_unblock_mutex(ThreadId tid, struct mutex *mx, const Char *action)
-//:: {
-//::    struct thread *th = thread_get(tid);
-//:: 
-//::    if (th == NULL) {
-//::       /* should an unknown thread doing something make it spring to life? */
-//::       thread_report(tid, THE_NotExist, "giving up on mutex");
-//::       return;
-//::    }
-//:: 
-//::    switch(th->state) {
-//::    case TS_MutexBlocked:  /* OK */
-//::       break;
-//:: 
-//::    case TS_Alive:
-//::    case TS_Running:
-//::       thread_report(tid, THE_NotBlocked, action);
-//::       break;
-//:: 
-//::    case TS_CVBlocked:
-//::    case TS_JoinBlocked:
-//::       thread_report(tid, THE_Blocked, action);
-//::       break;
-//:: 
-//::    case TS_Dead:
-//::    case TS_Zombie:
-//::       thread_report(tid, THE_NotAlive, action);
-//::       break;
-//::    }
-//:: 
-//::    do_thread_run(th);
-//:: }
-//:: 
-//:: /* --------------------------------------------------
-//::    Mutexes
-//:: 
-//::    This models simple, non-recursive mutexes.
-//::    -------------------------------------------------- */
-//:: 
-//:: struct mutex
-//:: {
-//::    Addr           mutex;          /* address of mutex */
-//::    ThreadId       owner;          /* owner if state == MX_Locked */
-//::    enum mutex_state {
-//::       MX_Init,
-//::       MX_Free,
-//::       MX_Locked,
-//::       MX_Unlocking,               /* half-unlocked */
-//::       MX_Dead
-//::    }              state;          /* mutex state */
-//:: 
-//::    ExeContext     *ec_create;     /* where created/destroyed */
-//::    ExeContext     *ec_locked;     /* where last locked/unlocked */
-//:: };
-//:: 
-//:: enum mutex_error
-//:: {
-//::    MXE_NotExist,          /* never existed */
-//::    MXE_NotInit,                   /* not initialized (use after destroy) */
-//::    MXE_ReInit,                    /* already initialized */
-//::    MXE_NotLocked,         /* not locked */
-//::    MXE_Locked,                    /* is locked */
-//::    MXE_Deadlock,          /* deadlock detected */
-//::    MXE_NotOwner,          /* non-owner trying to change lock */
-//:: };
-//:: 
-//:: struct mutex_error_data
-//:: {
-//::    enum mutex_error err;
-//::    struct mutex     *mx;
-//::    const Char       *action;
-//:: };
-//:: 
-//:: static struct mutex *mutex_get(Addr mutexp);
-//:: 
-//:: static const Char *pp_mutexstate(const struct mutex *mx)
-//:: {
-//::    static Char buf[20];
-//:: 
-//::    switch(mx->state) {
-//::    case MX_Init: return "Init";
-//::    case MX_Free: return "Free";
-//::    case MX_Dead: return "Dead";
-//:: 
-//::    case MX_Locked:
-//::       VG_(sprintf)(buf, "Locked by tid %d", mx->owner);
-//::       break;
-//:: 
-//::    case MX_Unlocking:
-//::       VG_(sprintf)(buf, "Being unlocked by tid %d", mx->owner);
-//::       break;
-//:: 
-//::    default:
-//::       VG_(sprintf)(buf, "?? %d", mx->state);
-//::       break;
-//::    }
-//:: 
-//::    return buf;
-//:: }
-//:: 
-//:: static void mutex_setstate(ThreadId tid, struct mutex *mx, enum mutex_state st)
-//:: {
-//::    ExeContext *ec = VG_(record_ExeContext)(tid);
-//:: 
-//::    switch(st) {
-//::    case MX_Init:
-//::    case MX_Dead:
-//::       mx->ec_create = ec;
-//::       break;
-//:: 
-//::    case MX_Unlocking:
-//::    case MX_Locked:
-//::    case MX_Free:
-//::       mx->ec_locked = ec;
-//::       break;
-//::    }
-//:: 
-//::    mx->state = st;
-//::    if (debug_mutex)
-//::       VG_(printf)("setting mutex(%p) -> %s\n", mx->mutex, pp_mutexstate(mx));
-//:: }
-//:: 
-//:: static void mutex_report(ThreadId tid, Addr mutexp, enum mutex_error err, const Char *action)
-//:: {
-//::    Char *errstr="?";
-//::    struct mutex *mx = mutex_get(mutexp);
-//::    struct mutex_error_data errdata;
-//:: 
-//::    switch(err) {
-//::    case MXE_NotExist:             errstr="non-existent"; break;
-//::    case MXE_NotInit:              errstr="uninitialized"; break;
-//::    case MXE_ReInit:               errstr="already initialized"; break;
-//::    case MXE_NotLocked:            errstr="not locked"; break;
-//::    case MXE_Locked:               errstr="locked"; break;
-//::    case MXE_NotOwner:             errstr="unowned"; break;
-//::    case MXE_Deadlock:             errstr="deadlock on"; break;
-//::    }
-//:: 
-//::    errdata.err = err;
-//::    errdata.mx = mx;
-//::    errdata.action = action;
-//::    
-//::    VG_(maybe_record_error)(tid, MutexErr, 0, errstr, &errdata);
-//:: }
-//:: 
-//:: static void pp_mutex_error(Error *err)
-//:: {
-//::    struct mutex_error_data *errdata = VG_(get_error_extra)(err);
-//::    struct mutex *mx = errdata->mx;
-//::    Char *errstr = VG_(get_error_string)(err);
-//::    
-//::    VG_(message)(Vg_UserMsg, "Found %s mutex %p while %s",
-//::                 errstr, mx ? mx->mutex : 0, errdata->action);
-//::    VG_(pp_ExeContext)(VG_(get_error_where)(err));
-//:: 
-//::    switch (mx->state) {
-//::       case MX_Init:
-//::       case MX_Dead:
-//::          break;
-//::       case MX_Locked:
-//::          VG_(message)(Vg_UserMsg, " Mutex was locked by thread %d", mx->owner);
-//::          VG_(pp_ExeContext)(mx->ec_locked);
-//::          break;
-//::       case MX_Unlocking:
-//::          VG_(message)(Vg_UserMsg, " Mutex being unlocked");
-//::          VG_(pp_ExeContext)(mx->ec_locked);
-//::          break;
-//::       case MX_Free:
-//::          VG_(message)(Vg_UserMsg, " Mutex was unlocked");
-//::          VG_(pp_ExeContext)(mx->ec_locked);
-//::          break;
-//::    }
-//:: 
-//::    VG_(message)(Vg_UserMsg, " Mutex was %s",
-//::                 mx->state == MX_Dead ? "destroyed" : "created");
-//::    VG_(pp_ExeContext)(mx->ec_create);
-//:: }
-//:: 
-//:: static SkipList sk_mutex = VG_SKIPLIST_INIT(struct mutex, mutex, VG_(cmp_Addr), NULL, VG_AR_CORE);
-//:: 
-//:: static struct mutex *mutex_get(Addr mutexp)
-//:: {
-//::    return VG_(SkipList_Find_Exact)(&sk_mutex, &mutexp);
-//:: }
-//:: 
-//:: static Bool mx_is_initialized(Addr mutexp)
-//:: {
-//::    const struct mutex *mx = mutex_get(mutexp);
-//:: 
-//::    return mx && mx->state != MX_Dead;
-//:: }
-//:: 
-//:: static struct mutex *mutex_check_initialized(ThreadId tid, Addr mutexp, const Char *action)
-//:: {
-//::    struct mutex *mx;
-//:: 
-//::    vg_assert(tid != VG_INVALID_THREADID);
-//:: 
-//::    if (!mx_is_initialized(mutexp)) {
-//::       mutex_report(tid, mutexp, MXE_NotInit, action);
-//::       VG_(tm_mutex_init)(tid, mutexp);
-//::    }
-//:: 
-//::    mx = mutex_get(mutexp);
-//::    vg_assert(mx != NULL);
-//:: 
-//::    return mx;
-//:: }
-//:: 
-//:: #if 0
-//:: static Bool mx_is_locked(Addr mutexp)
-//:: {
-//::    const struct mutex *mx = mutex_get(mutexp);
-//:: 
-//::    return mx && (mx->state == MX_Locked);
-//:: }
-//:: #endif
-//:: 
-//:: /* Mutex at mutexp is initialized.  This must be done before any
-//::    further mutex operations are OK. Fails if:
-//::     - mutexp already exists (and is locked?)
-//:: */
-//:: void VG_(tm_mutex_init)(ThreadId tid, Addr mutexp)
-//:: {
-//::    struct mutex *mx = mutex_get(mutexp);
-//:: 
-//::    if (mx == NULL) {
-//::       mx = VG_(SkipNode_Alloc)(&sk_mutex);
-//::       mx->mutex = mutexp;
-//::       VG_(SkipList_Insert)(&sk_mutex, mx);
-//::    } else if (mx->state != MX_Dead)
-//::       mutex_report(tid, mutexp, MXE_ReInit, "initializing");
-//:: 
-//::    mx->owner = VG_INVALID_THREADID;
-//:: 
-//::    mutex_setstate(tid, mx, MX_Init);
-//::    mutex_setstate(tid, mx, MX_Free);
-//:: }
-//:: 
-//:: Bool VG_(tm_mutex_exists)(Addr mutexp)
-//:: {
-//::    return mx_is_initialized(mutexp);
-//:: }
-//:: 
-//:: /* Mutex is being destroyed.  Fails if:
-//::     - mutex was not initialized
-//::     - mutex is locked (?)
-//::  */
-//:: void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp)
-//:: {
-//::    struct mutex *mx = mutex_get(mutexp);
-//:: 
-//::    if (mx == NULL)
-//::       mutex_report(tid, mutexp, MXE_NotExist, "destroying");
-//::    else {
-//::       switch(mx->state) {
-//::       case MX_Dead:
-//::    mutex_report(tid, mutexp, MXE_NotInit, "destroying");
-//::    break;
-//:: 
-//::       case MX_Locked:
-//::       case MX_Unlocking:
-//::    mutex_report(tid, mutexp, MXE_Locked, "destroying");
-//::    VG_(tm_mutex_unlock)(tid, mutexp);
-//::    break;
-//:: 
-//::       case MX_Init:
-//::       case MX_Free:
-//::    /* OK */
-//::    break;
-//::       }
-//::       mutex_setstate(tid, mx, MX_Dead);
-//::    }
-//:: }
-//:: 
-//:: /* A thread attempts to lock a mutex.  If "blocking" then the thread
-//::    is put into a blocked state until the lock is acquired.  Fails if:
-//::     - tid is invalid
-//::     - mutex has not been initialized
-//::     - thread is blocked on another object (?)
-//::     - blocking on this mutex could cause a deadlock
-//::    (Lock rank detection?)
-//::  */
-//:: void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp)
-//:: {
-//::    struct mutex *mx;
-//:: 
-//::    mx = mutex_check_initialized(tid, mutexp, "trylocking");
-//:: 
-//::    thread_block_mutex(tid, mx);
-//:: 
-//::    if (mx->state == MX_Locked && mx->owner == tid) /* deadlock */
-//::       mutex_report(tid, mutexp, MXE_Deadlock, "trylocking");
-//:: 
-//::    VG_TRACK( pre_mutex_lock, tid, (void *)mutexp );
-//:: }
-//:: 
-//:: /* Give up waiting for a mutex.  Fails if:
-//::     - thread is not currently blocked on the mutex
-//::  */
-//:: void VG_(tm_mutex_giveup)(ThreadId tid, Addr mutexp)
-//:: {
-//::    struct mutex *mx;
-//:: 
-//::    mx = mutex_check_initialized(tid, mutexp, "giving up");
-//:: 
-//::    thread_unblock_mutex(tid, mx, "giving up on mutex");
-//:: }
-//:: 
-//:: /* A thread acquires a mutex.  Fails if:
-//::     - thread is not blocked waiting for the mutex
-//::     - mutex is held by another thread
-//::  */
-//:: void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp)
-//:: {
-//::    struct mutex *mx;
-//:: 
-//::    mx = mutex_check_initialized(tid, mutexp, "acquiring");   
-//::    
-//::    switch(mx->state) {
-//::    case MX_Unlocking:             /* ownership transfer or relock */
-//::       VG_TRACK( post_mutex_unlock, mx->owner, (void *)mutexp );
-//::       if (mx->owner != tid)
-//::    thread_unblock_mutex(tid, mx, "acquiring mutex");
-//::       break;
-//:: 
-//::    case MX_Free:
-//::       thread_unblock_mutex(tid, mx, "acquiring mutex");
-//::       break;
-//:: 
-//::    case MX_Locked:
-//::       if (debug_mutex)
-//::    VG_(printf)("mutex=%p mx->state=%s\n", mutexp, pp_mutexstate(mx));
-//::       VG_TRACK( post_mutex_unlock, mx->owner, (void *)mutexp );
-//::       mutex_report(tid, mutexp, MXE_Locked, "acquiring");
-//::       thread_unblock_mutex(tid, mx, "acquiring mutex");
-//::       break;
-//:: 
-//::    case MX_Init:
-//::    case MX_Dead:
-//::       vg_assert(0);
-//::    } 
-//::      
-//::    mx->owner = tid;
-//::    mutex_setstate(tid, mx, MX_Locked);
-//:: 
-//::    VG_TRACK( post_mutex_lock, tid, (void *)mutexp );
-//:: }
-//:: 
-//:: /* Try unlocking a lock.  This will move it into a state where it can
-//::    either be unlocked, or change ownership to another thread.  If
-//::    unlock fails, it will remain locked. */
-//:: void VG_(tm_mutex_tryunlock)(ThreadId tid, Addr mutexp)
-//:: {
-//::    struct thread *th;
-//::    struct mutex *mx;
-//:: 
-//::    mx = mutex_check_initialized(tid, mutexp, "try-unlocking");
-//:: 
-//::    th = thread_get(tid);
-//:: 
-//::    if (th == NULL)
-//::       thread_report(tid, THE_NotExist, "try-unlocking mutex");
-//::    else {
-//::       switch(th->state) {
-//::       case TS_Alive:
-//::       case TS_Running:            /* OK */
-//::    break;
-//:: 
-//::       case TS_Dead:
-//::       case TS_Zombie:
-//::    thread_report(tid, THE_NotAlive, "try-unlocking mutex");
-//::    break;
-//:: 
-//::       case TS_JoinBlocked:
-//::       case TS_CVBlocked:
-//::       case TS_MutexBlocked:
-//::    thread_report(tid, THE_Blocked, "try-unlocking mutex");
-//::    do_thread_run(th);
-//::    break;
-//::       }
-//::    }
-//:: 
-//::    switch(mx->state) {
-//::    case MX_Locked:
-//::       if (mx->owner != tid)
-//::    mutex_report(tid, mutexp, MXE_NotOwner, "try-unlocking");
-//::       break;
-//:: 
-//::    case MX_Free:
-//::       mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking");
-//::       break;
-//:: 
-//::    case MX_Unlocking:
-//::       mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking");
-//::       break;
-//:: 
-//::    case MX_Init:
-//::    case MX_Dead:
-//::       vg_assert(0);
-//::    }
-//:: 
-//::    mutex_setstate(tid, mx, MX_Unlocking);
-//:: }
-//:: 
-//:: /* Finish unlocking a Mutex.  The mutex can validly be in one of three
-//::    states:
-//::    - Unlocking
-//::    - Locked, owned by someone else (someone else got it in the meantime)
-//::    - Free (someone else completed a lock-unlock cycle)
-//::  */
-//:: void VG_(tm_mutex_unlock)(ThreadId tid, Addr mutexp)
-//:: {
-//::    struct mutex *mx;
-//::    struct thread *th;
-//:: 
-//::    mx = mutex_check_initialized(tid, mutexp, "unlocking mutex");
-//:: 
-//::    th = thread_get(tid);
-//:: 
-//::    if (th == NULL)
-//::       thread_report(tid, THE_NotExist, "unlocking mutex");
-//::    else {
-//::       switch(th->state) {
-//::       case TS_Alive:
-//::       case TS_Running:            /* OK */
-//::    break;
-//:: 
-//::       case TS_Dead:
-//::       case TS_Zombie:
-//::    thread_report(tid, THE_NotAlive, "unlocking mutex");
-//::    break;
-//:: 
-//::       case TS_JoinBlocked:
-//::       case TS_CVBlocked:
-//::       case TS_MutexBlocked:
-//::    thread_report(tid, THE_Blocked, "unlocking mutex");
-//::    do_thread_run(th);
-//::    break;
-//::       }
-//::    }
-//:: 
-//::    switch(mx->state) {
-//::    case MX_Locked:
-//::       /* Someone else might have taken ownership in the meantime */
-//::       if (mx->owner == tid)
-//::    mutex_report(tid, mutexp, MXE_Locked, "unlocking");
-//::       break;
-//:: 
-//::    case MX_Free:
-//::       /* OK - nothing to do */
-//::       break;
-//:: 
-//::    case MX_Unlocking:
-//::       /* OK - we need to complete the unlock */
-//::       VG_TRACK( post_mutex_unlock, tid, (void *)mutexp );
-//::       mutex_setstate(tid, mx, MX_Free);
-//::       break;
-//:: 
-//::    case MX_Init:
-//::    case MX_Dead:
-//::       vg_assert(0);
-//::    }
-//:: }
-//:: 
-//:: /* --------------------------------------------------
-//::    Condition variables
-//::    -------------------------------------------------- */
-//:: 
-//:: struct condvar_waiter
-//:: {
-//::    ThreadId               waiter;
-//::    
-//::    struct condvar *condvar;
-//::    struct mutex           *mutex;
-//:: 
-//::    struct condvar_waiter  *next;
-//:: };
-//:: 
-//:: struct condvar 
-//:: {
-//::    Addr           condvar;
-//:: 
-//::    enum condvar_state {
-//::       CV_Dead,
-//::       CV_Alive,
-//::    }              state;
-//:: 
-//::    struct condvar_waiter  *waiters;       // XXX skiplist?
-//::    
-//::    ExeContext     *ec_created;    // where created
-//::    ExeContext     *ec_signalled;  // where last signalled
-//:: };
-//:: 
-//:: enum condvar_err {
-//::    CVE_NotExist,
-//::    CVE_NotInit,
-//::    CVE_ReInit,
-//::    CVE_Busy,
-//::    CVE_Blocked,
-//:: };
-//:: 
-//:: static SkipList sk_condvar = VG_SKIPLIST_INIT(struct condvar, condvar, VG_(cmp_Addr),
-//::                                      NULL, VG_AR_CORE);
-//:: 
-//:: static struct condvar *condvar_get(Addr condp)
-//:: {
-//::    return VG_(SkipList_Find_Exact)(&sk_condvar, &condp);
-//:: }
-//:: 
-//:: static Bool condvar_is_initialized(Addr condp)
-//:: {
-//::    const struct condvar *cv = condvar_get(condp);
-//:: 
-//::    return cv && cv->state != CV_Dead;
-//:: }
-//:: 
-//:: static void condvar_report(ThreadId tid, Addr condp, enum condvar_err err, const Char *action)
-//:: {
-//:: }
-//:: 
-//:: static struct condvar *condvar_check_initialized(ThreadId tid, Addr condp, const Char *action)
-//:: {
-//::    struct condvar *cv;
-//::    vg_assert(tid != VG_INVALID_THREADID);
-//::    
-//::    if (!condvar_is_initialized(condp)) {
-//::       condvar_report(tid, condp, CVE_NotInit, action);
-//::       VG_(tm_cond_init)(tid, condp);
-//::    }
-//:: 
-//::    cv = condvar_get(condp);
-//::    vg_assert(cv != NULL);
-//:: 
-//::    return cv;
-//:: }
-//:: 
-//:: /* Initialize a condition variable.  Fails if:
-//::     - condp has already been initialized
-//::  */
-//:: void VG_(tm_cond_init)(ThreadId tid, Addr condp)
-//:: {
-//::    struct condvar *cv = condvar_get(condp);
-//:: 
-//::    if (cv == NULL) {
-//::       cv = VG_(SkipNode_Alloc)(&sk_condvar);
-//::       cv->condvar = condp;
-//::       cv->waiters = NULL;
-//::       VG_(SkipList_Insert)(&sk_condvar, cv);
-//::    } else if (cv->state != CV_Dead) {
-//::       condvar_report(tid, condp, CVE_ReInit, "initializing");
-//::       /* ? what about existing waiters? */
-//::    }
-//:: 
-//::    cv->state = CV_Alive;
-//:: }
-//:: 
-//:: /* Destroy a condition variable.  Fails if:
-//::     - condp has not been initialized
-//::     - condp is currently being waited on
-//::  */
-//:: void VG_(tm_cond_destroy)(ThreadId tid, Addr condp)
-//:: {
-//::    struct condvar *cv = condvar_get(condp);
-//:: 
-//::    if (cv == NULL)
-//::       condvar_report(tid, condp, CVE_NotExist, "destroying");
-//::    else {
-//::       if (cv->state != CV_Alive)
-//::    condvar_report(tid, condp, CVE_NotInit, "destroying");
-//::       if (cv->waiters != NULL)
-//::    condvar_report(tid, condp, CVE_Busy, "destroying");
-//::       cv->state = CV_Dead;
-//::    }
-//:: }
-//:: 
-//:: static struct condvar_waiter *get_waiter(const struct condvar *cv, ThreadId tid)
-//:: {
-//::    struct condvar_waiter *w;
-//:: 
-//::    for(w = cv->waiters; w; w = w->next)
-//::       if (w->waiter == tid)
-//::    return w;
-//::    return NULL;
-//:: }
-//:: 
-//:: /* Wait for a condition, putting thread into blocked state.  Fails if:
-//::     - condp has not been initialized
-//::     - thread doesn't hold mutexp
-//::     - thread is blocked on some other object
-//::     - thread is already blocked on mutex
-//::  */
-//:: void VG_(tm_cond_wait)(ThreadId tid, Addr condp, Addr mutexp)
-//:: {
-//::    struct thread *th = thread_get(tid);
-//::    struct mutex *mx;
-//::    struct condvar *cv;
-//::    struct condvar_waiter *waiter;
-//:: 
-//::    /* Condvar must exist */
-//::    cv = condvar_check_initialized(tid, condp, "waiting");
-//:: 
-//::    /* Mutex must exist */
-//::    mx = mutex_check_initialized(tid, mutexp, "waiting on condvar");
-//:: 
-//::    /* Thread must own mutex */
-//::    if (mx->state != MX_Locked) {
-//::       mutex_report(tid, mutexp, MXE_NotLocked, "waiting on condvar");
-//::       VG_(tm_mutex_trylock)(tid, mutexp);
-//::       VG_(tm_mutex_acquire)(tid, mutexp);
-//::    } else if (mx->owner != tid) {
-//::       mutex_report(tid, mutexp, MXE_NotOwner, "waiting on condvar");
-//::       mx->owner = tid;
-//::    }
-//:: 
-//::    /* Thread must not be already waiting for condvar */
-//::    waiter = get_waiter(cv, tid);
-//::    if (waiter != NULL)
-//::       condvar_report(tid, condp, CVE_Blocked, "waiting");
-//::    else {
-//::       waiter = VG_(arena_malloc)(VG_AR_CORE, sizeof(*waiter));
-//::       waiter->condvar = cv;
-//::       waiter->mutex = mx;
-//::       waiter->next = cv->waiters;
-//::       cv->waiters = waiter;
-//::    }
-//:: 
-//::    /* Thread is now blocking on condvar */
-//::    do_thread_block_condvar(th, cv);
-//:: 
-//::    /* (half) release mutex */
-//::    VG_(tm_mutex_tryunlock)(tid, mutexp);
-//:: }
-//:: 
-//:: /* Wake from a condition, either because we've been signalled, or
-//::    because of timeout.  Fails if:
-//::      - thread is not waiting on condp
-//::  */
-//:: void VG_(tm_cond_wakeup)(ThreadId tid, Addr condp, Addr mutexp)
-//:: {
-//:: }
-//:: 
-//:: /* Signal a condition variable.  Fails if:
-//::     - condp has not been initialized
-//::  */
-//:: void VG_(tm_cond_signal)(ThreadId tid, Addr condp)
-//:: {
-//:: }
-//:: 
-//:: /* --------------------------------------------------
-//::    Error handling
-//::    -------------------------------------------------- */
-//:: 
-//:: UInt VG_(tm_error_update_extra)(Error *err)
-//:: {
-//::    switch (VG_(get_error_kind)(err)) {
-//::       case ThreadErr: {
-//::          struct thread_error_data *errdata = VG_(get_error_extra)(err);
-//::          struct thread *new_th = VG_(arena_malloc)(VG_AR_CORE, sizeof(struct thread));
-//:: 
-//::          VG_(memcpy)(new_th, errdata->th, sizeof(struct thread));
-//:: 
-//::          errdata->th = new_th;
-//:: 
-//::          return sizeof(struct thread_error_data);
-//::       }
-//:: 
-//::       case MutexErr: {
-//::          struct mutex_error_data *errdata = VG_(get_error_extra)(err);
-//::          struct mutex *new_mx = VG_(arena_malloc)(VG_AR_CORE, sizeof(struct mutex));
-//:: 
-//::          VG_(memcpy)(new_mx, errdata->mx, sizeof(struct mutex));
-//:: 
-//::          errdata->mx = new_mx;
-//:: 
-//::          return sizeof(struct mutex_error_data);
-//::       }
-//:: 
-//::       default:
-//::          return 0;
-//::    }
-//:: }
-//:: 
-//:: Bool VG_(tm_error_equal)(VgRes res, Error *e1, Error *e2)
-//:: {
-//::    /* Guaranteed by calling function */
-//::    vg_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
-//::    
-//::    switch (VG_(get_error_kind)(e1)) {
-//::       case ThreadErr: {
-//::          struct thread_error_data *errdata1 = VG_(get_error_extra)(e1);
-//::          struct thread_error_data *errdata2 = VG_(get_error_extra)(e2);
-//:: 
-//::          return errdata1->err == errdata2->err;
-//::       }
-//:: 
-//::       case MutexErr: {
-//::          struct mutex_error_data *errdata1 = VG_(get_error_extra)(e1);
-//::          struct mutex_error_data *errdata2 = VG_(get_error_extra)(e2);
-//:: 
-//::          return errdata1->err == errdata2->err;
-//::       }
-//:: 
-//::       default: 
-//::          VG_(printf)("Error:\n  unknown error code %d\n",
-//::                      VG_(get_error_kind)(e1));
-//::          VG_(core_panic)("unknown error code in VG_(tm_error_equal)");
-//::    }
-//:: }
-//:: 
-//:: void VG_(tm_error_print)(Error *err)
-//:: {
-//::    switch (VG_(get_error_kind)(err)) {
-//::       case ThreadErr:
-//::          pp_thread_error(err);
-//::          break;
-//::       case MutexErr:
-//::          pp_mutex_error(err);
-//::          break;
-//::    }
-//:: }
-//:: 
-//:: /* --------------------------------------------------
-//::    Initialisation
-//::    -------------------------------------------------- */
-//:: 
-//:: void VG_(tm_init)()
-//:: {
-//::    VG_(needs_core_errors)();
-//:: }
+struct thread;
+struct mutex;
+struct condvar;
+
+static const Bool debug_thread = False;
+static const Bool debug_mutex = False;
+
+/* --------------------------------------------------
+   Thread lifetime
+   
+   Threads are all expressed in terms of internal ThreadIds.  The
+   thread library interface needs to map from the library's identifers
+   to ThreadIds.
+   -------------------------------------------------- */
+
+/* Per-thread state.  We maintain our own here rather than hanging it
+   off ThreadState, so that we have the option of not having a 1:1
+   relationship between modelled threads and Valgrind threads. */
+struct thread
+{
+   ThreadId    tid;
+   ThreadId    creator;
+
+   Bool                detached;       /* thread is detached */
+
+   enum thread_state {
+      TS_Alive,                        /* alive */
+      TS_Zombie,               /* waiting to be joined on (detached is False) */
+      TS_Dead,                 /* all dead */
+      
+      TS_Running,              /* running */
+      TS_MutexBlocked,         /* blocked on mutex */
+      TS_CVBlocked,            /* blocked on condvar */
+      TS_JoinBlocked,          /* blocked in join */
+   }           state;
+
+   struct mutex                *mx_blocked; /* mutex we're blocked on (state==TS_MutexBlocked) */
+   struct condvar      *cv_blocked; /* condvar we're blocked on (state==TS_CVBlocked) */
+   struct thread       *th_blocked; /* thread we're blocked on (state==TS_JoinBlocked) */
+
+   ExeContext  *ec_created;    /* where created */
+   ExeContext  *ec_blocked;    /* where blocked/unblocked */
+};
+
+enum thread_error
+{
+   THE_NotExist,               /* thread doesn't exist */
+   THE_NotAlive,               /* thread isn't alive (use after death) */
+   THE_Rebirth,                        /* thread already alive */
+   THE_Blocked,                        /* thread not supposed to be blocked */
+   THE_NotBlocked,             /* thread supposed to be blocked */
+   THE_Detached,               /* thread is detached */
+};
+
+struct thread_error_data
+{
+   enum thread_error err;
+   struct thread     *th;
+   const Char        *action;
+};
+
+static const Char *pp_threadstate(const struct thread *th)
+{
+   if (th == NULL)
+      return "non-existent";
+
+   switch(th->state) {
+   case TS_Alive:      return "alive";
+   case TS_Zombie:     return "zombie";
+   case TS_Dead:       return "dead";
+   case TS_Running:    return "running";
+   case TS_MutexBlocked:return "mutex-blocked";
+   case TS_CVBlocked:  return "cv-blocked";
+   case TS_JoinBlocked:        return "join-blocked";
+   default:            return "???";
+   }
+}
+
+static void thread_validate(struct thread *th)
+{
+   switch(th->state) {
+   case TS_Alive:
+   case TS_Running:
+   case TS_Dead:
+   case TS_Zombie:
+      vg_assert(th->mx_blocked == NULL);
+      vg_assert(th->cv_blocked == NULL);
+      vg_assert(th->th_blocked == NULL);
+      break;
+
+   case TS_MutexBlocked:
+      vg_assert(th->mx_blocked != NULL);
+      vg_assert(th->cv_blocked == NULL);
+      vg_assert(th->th_blocked == NULL);
+      break;
+
+   case TS_CVBlocked:
+      vg_assert(th->mx_blocked == NULL);
+      vg_assert(th->cv_blocked != NULL);
+      vg_assert(th->th_blocked == NULL);
+      break;
+
+   case TS_JoinBlocked:
+      vg_assert(th->mx_blocked == NULL);
+      vg_assert(th->cv_blocked == NULL);
+      vg_assert(th->th_blocked != NULL);
+      break;
+   }
+}
+
+static void thread_setstate(struct thread *th, enum thread_state state)
+{
+   ExeContext *ec;
+
+   if (th->state == state)
+      return;
+
+   ec = VG_(record_ExeContext)(th->tid);
+
+   switch(state) {
+   case TS_Alive:
+   case TS_Dead:
+      th->ec_created = ec;
+      break;
+
+   case TS_Running:
+   case TS_MutexBlocked:
+   case TS_CVBlocked:
+   case TS_JoinBlocked:
+   case TS_Zombie:
+      th->ec_blocked = ec;
+   }
+
+   th->state = state;
+   if (debug_thread)
+      VG_(printf)("setting thread(%d) -> %s\n", th->tid, pp_threadstate(th));
+   thread_validate(th);
+}
+
+static void do_thread_run(struct thread *th)
+{
+   th->mx_blocked = NULL;
+   th->cv_blocked = NULL;
+   th->th_blocked = NULL;
+   thread_setstate(th, TS_Running);
+}
+
+static void do_thread_block_mutex(struct thread *th, struct mutex *mx)
+{
+   th->mx_blocked = mx;
+   th->cv_blocked = NULL;
+   th->th_blocked = NULL;
+   thread_setstate(th, TS_MutexBlocked);
+}
+
+static void do_thread_block_condvar(struct thread *th, struct condvar *cv)
+{
+   th->mx_blocked = NULL;
+   th->cv_blocked = cv;
+   th->th_blocked = NULL;
+   thread_setstate(th, TS_CVBlocked);
+}
+
+static void do_thread_block_join(struct thread *th, struct thread *joinee)
+{
+   th->mx_blocked = NULL;
+   th->cv_blocked = NULL;
+   th->th_blocked = joinee;
+   thread_setstate(th, TS_JoinBlocked);
+}
+
+static void do_thread_block_zombie(struct thread *th)
+{
+   th->mx_blocked = NULL;
+   th->cv_blocked = NULL;
+   th->th_blocked = NULL;
+   thread_setstate(th, TS_Zombie);
+}
+
+static void do_thread_dead(struct thread *th)
+{
+   th->mx_blocked = NULL;
+   th->cv_blocked = NULL;
+   th->th_blocked = NULL;
+   thread_setstate(th, TS_Dead);
+}
+
+static OSet *threadSet = NULL;
+
+static struct thread *thread_get(ThreadId tid)
+{
+   return VG_(OSet_Lookup)(threadSet, &tid);
+}
+
+static void thread_report(ThreadId tid, enum thread_error err, const Char *action)
+{
+   Char *errstr = "?";
+   struct thread *th = thread_get(tid);
+   struct thread_error_data errdata;
+
+   switch(err) {
+   case THE_NotExist:  errstr = "non existent"; break;
+   case THE_NotAlive:  errstr = "not alive"; break;
+   case THE_Rebirth:   errstr = "re-born"; break;
+   case THE_Blocked:   errstr = "blocked"; break;
+   case THE_NotBlocked:        errstr = "not blocked"; break;
+   case THE_Detached:  errstr = "detached"; break;
+   }
+
+   errdata.err = err;
+   errdata.th = th;
+   errdata.action = action;
+   
+   VG_(maybe_record_error)(VG_(get_running_tid)(), ThreadErr, 0, errstr, &errdata);
+}
+
+static void pp_thread_error(Error *err)
+{
+   struct thread_error_data *errdata = VG_(get_error_extra)(err);
+   struct thread *th = errdata->th;
+   Char *errstr = VG_(get_error_string)(err);
+   
+   VG_(message)(Vg_UserMsg, "Found %s thread in state %s while %s",
+                errstr, pp_threadstate(th), errdata->action);
+   VG_(pp_ExeContext)(VG_(get_error_where)(err));
+
+   if (th) {
+      VG_(message)(Vg_UserMsg, " Thread %d was %s",
+                  th->tid, th->state == TS_Dead ? "destroyed" : "created");
+      VG_(pp_ExeContext)(th->ec_created);
+   }
+}
+
+/* Thread creation */
+void VG_(tm_thread_create)(ThreadId creator, ThreadId tid, Bool detached)
+{
+   struct thread *th = thread_get(tid);
+
+   if (debug_thread)
+      VG_(printf)("thread %d creates %d %s\n", creator, tid, detached ? "detached" : "");
+   if (th != NULL) {
+      if (th->state != TS_Dead)
+        thread_report(tid, THE_Rebirth, "creating");
+   } else {
+      th = VG_(OSet_AllocNode)(threadSet, sizeof(struct thread));
+      th->tid = tid;
+      VG_(OSet_Insert)(threadSet, th);
+   }
+
+   th->creator = creator;
+   th->detached = detached;
+   th->mx_blocked = NULL;
+   th->cv_blocked = NULL;
+   th->th_blocked = NULL;
+
+   thread_setstate(th, TS_Alive);
+   do_thread_run(th);
+}
+
+Bool VG_(tm_thread_exists)(ThreadId tid)
+{
+   struct thread *th = thread_get(tid);
+
+   return th && th->state != TS_Dead;
+}
+
+/* A thread is terminating itself
+    - fails if tid has already terminated
+    - if detached, tid becomes invalid for all further operations
+    - if not detached, the thread remains in a Zombie state until
+      someone joins on it
+ */
+void VG_(tm_thread_exit)(ThreadId tid)
+{
+   struct thread *th = thread_get(tid);
+
+   if (th == NULL)
+      thread_report(tid, THE_NotExist, "exiting");
+   else {
+      struct thread *joiner;
+
+      switch(th->state) {
+      case TS_Dead:
+      case TS_Zombie:  /* already exited once */
+        thread_report(tid, THE_NotAlive, "exiting");
+        break;
+
+      case TS_MutexBlocked:
+      case TS_CVBlocked:
+      case TS_JoinBlocked:
+           thread_report(tid, THE_Blocked, "exiting");
+           break;
+
+      case TS_Alive:
+      case TS_Running:
+        /* OK */
+        break;
+      }
+
+      /* ugly - walk all threads to find people joining with us */
+      /* In pthreads its an error to have multiple joiners, but that
+        seems a bit specific to implement here; there should a way
+        for the thread library binding to handle this. */
+      VG_(OSet_ResetIter)(threadSet);
+      while ((joiner = VG_(OSet_Next)(threadSet)) != NULL) {
+        if (joiner->state == TS_JoinBlocked && joiner->th_blocked == th) {
+           /* found someone - wake them up */
+           do_thread_run(joiner);
+
+           /* we're dead */
+           do_thread_dead(th);
+        }
+      }
+
+      if (th->state != TS_Dead)
+        do_thread_block_zombie(th);
+   }
+}
+
+void VG_(tm_thread_detach)(ThreadId tid)
+{
+   struct thread *th = thread_get(tid);
+
+   if (th == NULL)
+      thread_report(tid, THE_NotExist, "detaching");
+   else {
+      if (th->detached)
+        thread_report(tid, THE_Detached, "detaching");
+      else {
+        /* XXX look for waiters */
+        th->detached = True;
+      }
+   }
+}
+
+/* One thread blocks until another has terminated
+    - fails if joinee is detached
+    - fails if joinee doesn't exist
+    - once the join completes, joinee is dead
+ */
+void VG_(tm_thread_join)(ThreadId joinerid, ThreadId joineeid)
+{
+   struct thread *joiner = thread_get(joinerid);
+   struct thread *joinee = thread_get(joineeid);
+
+   /* First, check the joinee thread's state */
+   if (joinee == NULL)
+      thread_report(joineeid, THE_NotExist, "joining as joinee");
+   else {
+      switch(joinee->state) {
+      case TS_Alive:           /* really shouldn't see them in this state... */
+      case TS_Running:
+      case TS_Zombie:
+      case TS_MutexBlocked:
+      case TS_CVBlocked:
+      case TS_JoinBlocked:
+        /* OK */
+        break;
+
+      case TS_Dead:
+        thread_report(joineeid, THE_NotAlive, "joining as joinee");
+        break;
+      }
+   }
+
+   /* now the joiner... */
+   if (joiner == NULL)
+      thread_report(joineeid, THE_NotExist, "joining as joiner");
+   else {
+      switch(joiner->state) {
+      case TS_Alive:           /* ? */
+      case TS_Running:         /* OK */
+        break;
+
+      case TS_Zombie:          /* back from the dead */
+      case TS_Dead:
+        thread_report(joineeid, THE_NotAlive, "joining as joiner");
+        break;
+
+      case TS_MutexBlocked:
+      case TS_CVBlocked:
+      case TS_JoinBlocked:
+        thread_report(joineeid, THE_Blocked, "joining as joiner");
+        break;
+      }
+
+      if (joinee->detached)
+        thread_report(joineeid, THE_Detached, "joining as joiner");
+      else {
+        /* block if the joinee hasn't exited yet */
+        if (joinee) {
+           switch(joinee->state) {
+           case TS_Dead:
+              break;
+
+           default:
+              if (joinee->state == TS_Zombie)
+                 do_thread_dead(joinee);
+              else
+                 do_thread_block_join(joiner, joinee);
+           }
+        }
+      }
+   }
+}
+
+/* Context switch to a new thread */
+void VG_(tm_thread_switchto)(ThreadId tid)
+{
+   VG_TRACK( thread_run, tid );
+}
+
+static void thread_block_mutex(ThreadId tid, struct mutex *mx)
+{
+   struct thread *th = thread_get(tid);
+
+   if (th == NULL) {
+      /* should an unknown thread doing something make it spring to life? */
+      thread_report(tid, THE_NotExist, "blocking on mutex");
+      return;
+   }
+   switch(th->state) {
+   case TS_Dead:
+   case TS_Zombie:
+      thread_report(th->tid, THE_NotAlive, "blocking on mutex");
+      break;
+
+   case TS_MutexBlocked:
+   case TS_CVBlocked:
+   case TS_JoinBlocked:
+      thread_report(th->tid, THE_Blocked, "blocking on mutex");
+      break;
+
+   case TS_Alive:
+   case TS_Running:            /* OK */
+      break;
+   }
+
+   do_thread_block_mutex(th, mx);
+}
+
+static void thread_unblock_mutex(ThreadId tid, struct mutex *mx, const Char *action)
+{
+   struct thread *th = thread_get(tid);
+
+   if (th == NULL) {
+      /* should an unknown thread doing something make it spring to life? */
+      thread_report(tid, THE_NotExist, "giving up on mutex");
+      return;
+   }
+
+   switch(th->state) {
+   case TS_MutexBlocked:       /* OK */
+      break;
+
+   case TS_Alive:
+   case TS_Running:
+      thread_report(tid, THE_NotBlocked, action);
+      break;
+
+   case TS_CVBlocked:
+   case TS_JoinBlocked:
+      thread_report(tid, THE_Blocked, action);
+      break;
+
+   case TS_Dead:
+   case TS_Zombie:
+      thread_report(tid, THE_NotAlive, action);
+      break;
+   }
+
+   do_thread_run(th);
+}
+
+/* --------------------------------------------------
+   Mutexes
+
+   This models simple, non-recursive mutexes.
+   -------------------------------------------------- */
+
+struct mutex
+{
+   Addr                mutex;          /* address of mutex */
+   ThreadId    owner;          /* owner if state == MX_Locked */
+   enum mutex_state {
+      MX_Init,
+      MX_Free,
+      MX_Locked,
+      MX_Unlocking,            /* half-unlocked */
+      MX_Dead
+   }           state;          /* mutex state */
+
+   ExeContext  *ec_create;     /* where created/destroyed */
+   ExeContext  *ec_locked;     /* where last locked/unlocked */
+};
+
+enum mutex_error
+{
+   MXE_NotExist,               /* never existed */
+   MXE_NotInit,                        /* not initialized (use after destroy) */
+   MXE_ReInit,                 /* already initialized */
+   MXE_NotLocked,              /* not locked */
+   MXE_Locked,                 /* is locked */
+   MXE_Deadlock,               /* deadlock detected */
+   MXE_NotOwner,               /* non-owner trying to change lock */
+};
+
+struct mutex_error_data
+{
+   enum mutex_error err;
+   struct mutex     *mx;
+   const Char       *action;
+};
+
+static struct mutex *mutex_get(Addr mutexp);
+
+static const Char *pp_mutexstate(const struct mutex *mx)
+{
+   static Char buf[20];
+
+   switch(mx->state) {
+   case MX_Init: return "Init";
+   case MX_Free: return "Free";
+   case MX_Dead: return "Dead";
+
+   case MX_Locked:
+      VG_(sprintf)(buf, "Locked by tid %d", mx->owner);
+      break;
+
+   case MX_Unlocking:
+      VG_(sprintf)(buf, "Being unlocked by tid %d", mx->owner);
+      break;
+
+   default:
+      VG_(sprintf)(buf, "?? %d", mx->state);
+      break;
+   }
+
+   return buf;
+}
+
+static void mutex_setstate(ThreadId tid, struct mutex *mx, enum mutex_state st)
+{
+   ExeContext *ec = VG_(record_ExeContext)(tid);
+
+   switch(st) {
+   case MX_Init:
+   case MX_Dead:
+      mx->ec_create = ec;
+      break;
+
+   case MX_Unlocking:
+   case MX_Locked:
+   case MX_Free:
+      mx->ec_locked = ec;
+      break;
+   }
+
+   mx->state = st;
+   if (debug_mutex)
+      VG_(printf)("setting mutex(%p) -> %s\n", mx->mutex, pp_mutexstate(mx));
+}
+
+static void mutex_report(ThreadId tid, Addr mutexp, enum mutex_error err, const Char *action)
+{
+   Char *errstr="?";
+   struct mutex *mx = mutex_get(mutexp);
+   struct mutex_error_data errdata;
+
+   switch(err) {
+   case MXE_NotExist:          errstr="non-existent"; break;
+   case MXE_NotInit:           errstr="uninitialized"; break;
+   case MXE_ReInit:            errstr="already initialized"; break;
+   case MXE_NotLocked:         errstr="not locked"; break;
+   case MXE_Locked:            errstr="locked"; break;
+   case MXE_NotOwner:          errstr="unowned"; break;
+   case MXE_Deadlock:          errstr="deadlock on"; break;
+   }
+
+   errdata.err = err;
+   errdata.mx = mx;
+   errdata.action = action;
+   
+   VG_(maybe_record_error)(tid, MutexErr, 0, errstr, &errdata);
+}
+
+static void pp_mutex_error(Error *err)
+{
+   struct mutex_error_data *errdata = VG_(get_error_extra)(err);
+   struct mutex *mx = errdata->mx;
+   Char *errstr = VG_(get_error_string)(err);
+   
+   VG_(message)(Vg_UserMsg, "Found %s mutex %p while %s",
+                errstr, mx ? mx->mutex : 0, errdata->action);
+   VG_(pp_ExeContext)(VG_(get_error_where)(err));
+
+   switch (mx->state) {
+      case MX_Init:
+      case MX_Dead:
+         break;
+      case MX_Locked:
+         VG_(message)(Vg_UserMsg, " Mutex was locked by thread %d", mx->owner);
+         VG_(pp_ExeContext)(mx->ec_locked);
+         break;
+      case MX_Unlocking:
+         VG_(message)(Vg_UserMsg, " Mutex being unlocked");
+         VG_(pp_ExeContext)(mx->ec_locked);
+         break;
+      case MX_Free:
+         VG_(message)(Vg_UserMsg, " Mutex was unlocked");
+         VG_(pp_ExeContext)(mx->ec_locked);
+         break;
+   }
+
+   VG_(message)(Vg_UserMsg, " Mutex was %s",
+                mx->state == MX_Dead ? "destroyed" : "created");
+   VG_(pp_ExeContext)(mx->ec_create);
+}
+
+static OSet *mutexSet = NULL;
+
+static struct mutex *mutex_get(Addr mutexp)
+{
+   return VG_(OSet_Lookup)(mutexSet, &mutexp);
+}
+
+static Bool mx_is_initialized(Addr mutexp)
+{
+   const struct mutex *mx = mutex_get(mutexp);
+
+   return mx && mx->state != MX_Dead;
+}
+
+static struct mutex *mutex_check_initialized(ThreadId tid, Addr mutexp, const Char *action)
+{
+   struct mutex *mx;
+
+   vg_assert(tid != VG_INVALID_THREADID);
+
+   if (!mx_is_initialized(mutexp)) {
+      mutex_report(tid, mutexp, MXE_NotInit, action);
+      VG_(tm_mutex_init)(tid, mutexp);
+   }
+
+   mx = mutex_get(mutexp);
+   vg_assert(mx != NULL);
+
+   return mx;
+}
+
+#if 0
+static Bool mx_is_locked(Addr mutexp)
+{
+   const struct mutex *mx = mutex_get(mutexp);
+
+   return mx && (mx->state == MX_Locked);
+}
+#endif
+
+/* Mutex at mutexp is initialized.  This must be done before any
+   further mutex operations are OK. Fails if:
+    - mutexp already exists (and is locked?)
+*/
+void VG_(tm_mutex_init)(ThreadId tid, Addr mutexp)
+{
+   struct mutex *mx = mutex_get(mutexp);
+
+   if (mx == NULL) {
+      mx = VG_(OSet_AllocNode)(mutexSet, sizeof(struct mutex));
+      mx->mutex = mutexp;
+      VG_(OSet_Insert)(mutexSet, mx);
+   } else if (mx->state != MX_Dead)
+      mutex_report(tid, mutexp, MXE_ReInit, "initializing");
+
+   mx->owner = VG_INVALID_THREADID;
+
+   mutex_setstate(tid, mx, MX_Init);
+   mutex_setstate(tid, mx, MX_Free);
+}
+
+Bool VG_(tm_mutex_exists)(Addr mutexp)
+{
+   return mx_is_initialized(mutexp);
+}
+
+/* Mutex is being destroyed.  Fails if:
+    - mutex was not initialized
+    - mutex is locked (?)
+ */
+void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp)
+{
+   struct mutex *mx = mutex_get(mutexp);
+
+   if (mx == NULL)
+      mutex_report(tid, mutexp, MXE_NotExist, "destroying");
+   else {
+      switch(mx->state) {
+      case MX_Dead:
+        mutex_report(tid, mutexp, MXE_NotInit, "destroying");
+        break;
+
+      case MX_Locked:
+      case MX_Unlocking:
+        mutex_report(tid, mutexp, MXE_Locked, "destroying");
+        VG_(tm_mutex_unlock)(tid, mutexp);
+        break;
+
+      case MX_Init:
+      case MX_Free:
+        /* OK */
+        break;
+      }
+      mutex_setstate(tid, mx, MX_Dead);
+   }
+}
+
+/* A thread attempts to lock a mutex.  If "blocking" then the thread
+   is put into a blocked state until the lock is acquired.  Fails if:
+    - tid is invalid
+    - mutex has not been initialized
+    - thread is blocked on another object (?)
+    - blocking on this mutex could cause a deadlock
+   (Lock rank detection?)
+ */
+void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp)
+{
+   struct mutex *mx;
+
+   mx = mutex_check_initialized(tid, mutexp, "trylocking");
+
+   thread_block_mutex(tid, mx);
+
+   if (mx->state == MX_Locked && mx->owner == tid) /* deadlock */
+      mutex_report(tid, mutexp, MXE_Deadlock, "trylocking");
+
+   VG_TRACK( pre_mutex_lock, tid, (void *)mutexp );
+}
+
+/* Give up waiting for a mutex.  Fails if:
+    - thread is not currently blocked on the mutex
+ */
+void VG_(tm_mutex_giveup)(ThreadId tid, Addr mutexp)
+{
+   struct mutex *mx;
+
+   mx = mutex_check_initialized(tid, mutexp, "giving up");
+
+   thread_unblock_mutex(tid, mx, "giving up on mutex");
+}
+
+/* A thread acquires a mutex.  Fails if:
+    - thread is not blocked waiting for the mutex
+    - mutex is held by another thread
+ */
+void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp)
+{
+   struct mutex *mx;
+
+   mx = mutex_check_initialized(tid, mutexp, "acquiring");   
+   
+   switch(mx->state) {
+   case MX_Unlocking:          /* ownership transfer or relock */
+      VG_TRACK( post_mutex_unlock, mx->owner, (void *)mutexp );
+      if (mx->owner != tid)
+        thread_unblock_mutex(tid, mx, "acquiring mutex");
+      break;
+
+   case MX_Free:
+      thread_unblock_mutex(tid, mx, "acquiring mutex");
+      break;
+
+   case MX_Locked:
+      if (debug_mutex)
+        VG_(printf)("mutex=%p mx->state=%s\n", mutexp, pp_mutexstate(mx));
+      VG_TRACK( post_mutex_unlock, mx->owner, (void *)mutexp );
+      mutex_report(tid, mutexp, MXE_Locked, "acquiring");
+      thread_unblock_mutex(tid, mx, "acquiring mutex");
+      break;
+
+   case MX_Init:
+   case MX_Dead:
+      vg_assert(0);
+   } 
+     
+   mx->owner = tid;
+   mutex_setstate(tid, mx, MX_Locked);
+
+   VG_TRACK( post_mutex_lock, tid, (void *)mutexp );
+}
+
+/* Try unlocking a lock.  This will move it into a state where it can
+   either be unlocked, or change ownership to another thread.  If
+   unlock fails, it will remain locked. */
+void VG_(tm_mutex_tryunlock)(ThreadId tid, Addr mutexp)
+{
+   struct thread *th;
+   struct mutex *mx;
+
+   mx = mutex_check_initialized(tid, mutexp, "try-unlocking");
+
+   th = thread_get(tid);
+
+   if (th == NULL)
+      thread_report(tid, THE_NotExist, "try-unlocking mutex");
+   else {
+      switch(th->state) {
+      case TS_Alive:
+      case TS_Running:         /* OK */
+        break;
+
+      case TS_Dead:
+      case TS_Zombie:
+        thread_report(tid, THE_NotAlive, "try-unlocking mutex");
+        break;
+
+      case TS_JoinBlocked:
+      case TS_CVBlocked:
+      case TS_MutexBlocked:
+        thread_report(tid, THE_Blocked, "try-unlocking mutex");
+        do_thread_run(th);
+        break;
+      }
+   }
+
+   switch(mx->state) {
+   case MX_Locked:
+      if (mx->owner != tid)
+        mutex_report(tid, mutexp, MXE_NotOwner, "try-unlocking");
+      break;
+
+   case MX_Free:
+      mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking");
+      break;
+
+   case MX_Unlocking:
+      mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking");
+      break;
+
+   case MX_Init:
+   case MX_Dead:
+      vg_assert(0);
+   }
+
+   mutex_setstate(tid, mx, MX_Unlocking);
+}
+
+/* Finish unlocking a Mutex.  The mutex can validly be in one of three
+   states:
+   - Unlocking
+   - Locked, owned by someone else (someone else got it in the meantime)
+   - Free (someone else completed a lock-unlock cycle)
+ */
+void VG_(tm_mutex_unlock)(ThreadId tid, Addr mutexp)
+{
+   struct mutex *mx;
+   struct thread *th;
+
+   mx = mutex_check_initialized(tid, mutexp, "unlocking mutex");
+
+   th = thread_get(tid);
+
+   if (th == NULL)
+      thread_report(tid, THE_NotExist, "unlocking mutex");
+   else {
+      switch(th->state) {
+      case TS_Alive:
+      case TS_Running:         /* OK */
+        break;
+
+      case TS_Dead:
+      case TS_Zombie:
+        thread_report(tid, THE_NotAlive, "unlocking mutex");
+        break;
+
+      case TS_JoinBlocked:
+      case TS_CVBlocked:
+      case TS_MutexBlocked:
+        thread_report(tid, THE_Blocked, "unlocking mutex");
+        do_thread_run(th);
+        break;
+      }
+   }
+
+   switch(mx->state) {
+   case MX_Locked:
+      /* Someone else might have taken ownership in the meantime */
+      if (mx->owner == tid)
+        mutex_report(tid, mutexp, MXE_Locked, "unlocking");
+      break;
+
+   case MX_Free:
+      /* OK - nothing to do */
+      break;
+
+   case MX_Unlocking:
+      /* OK - we need to complete the unlock */
+      VG_TRACK( post_mutex_unlock, tid, (void *)mutexp );
+      mutex_setstate(tid, mx, MX_Free);
+      break;
+
+   case MX_Init:
+   case MX_Dead:
+      vg_assert(0);
+   }
+}
+
+/* --------------------------------------------------
+   Condition variables
+   -------------------------------------------------- */
+
+struct condvar_waiter
+{
+   ThreadId            waiter;
+   
+   struct condvar      *condvar;
+   struct mutex                *mutex;
+
+   struct condvar_waiter       *next;
+};
+
+struct condvar 
+{
+   Addr                condvar;
+
+   enum condvar_state {
+      CV_Dead,
+      CV_Alive,
+   }           state;
+
+   struct condvar_waiter       *waiters;       // XXX skiplist?
+
+   
+   ExeContext  *ec_created;    // where created
+   ExeContext  *ec_signalled;  // where last signalled
+};
+
+enum condvar_err {
+   CVE_NotExist,
+   CVE_NotInit,
+   CVE_ReInit,
+   CVE_Busy,
+   CVE_Blocked,
+};
+
+static OSet *condvarSet = NULL;
+
+static struct condvar *condvar_get(Addr condp)
+{
+   return VG_(OSet_Lookup)(condvarSet, &condp);
+}
+
+static Bool condvar_is_initialized(Addr condp)
+{
+   const struct condvar *cv = condvar_get(condp);
+
+   return cv && cv->state != CV_Dead;
+}
+
+static void condvar_report(ThreadId tid, Addr condp, enum condvar_err err, const Char *action)
+{
+}
+
+static struct condvar *condvar_check_initialized(ThreadId tid, Addr condp, const Char *action)
+{
+   struct condvar *cv;
+   vg_assert(tid != VG_INVALID_THREADID);
+   
+   if (!condvar_is_initialized(condp)) {
+      condvar_report(tid, condp, CVE_NotInit, action);
+      VG_(tm_cond_init)(tid, condp);
+   }
+
+   cv = condvar_get(condp);
+   vg_assert(cv != NULL);
+
+   return cv;
+}
+
+/* Initialize a condition variable.  Fails if:
+    - condp has already been initialized
+ */
+void VG_(tm_cond_init)(ThreadId tid, Addr condp)
+{
+   struct condvar *cv = condvar_get(condp);
+
+   if (cv == NULL) {
+      cv = VG_(OSet_AllocNode)(condvarSet, sizeof(struct condvar));
+      cv->condvar = condp;
+      cv->waiters = NULL;
+      VG_(OSet_Insert)(condvarSet, cv);
+   } else if (cv->state != CV_Dead) {
+      condvar_report(tid, condp, CVE_ReInit, "initializing");
+      /* ? what about existing waiters? */
+   }
+
+   cv->state = CV_Alive;
+}
+
+/* Destroy a condition variable.  Fails if:
+    - condp has not been initialized
+    - condp is currently being waited on
+ */
+void VG_(tm_cond_destroy)(ThreadId tid, Addr condp)
+{
+   struct condvar *cv = condvar_get(condp);
+
+   if (cv == NULL)
+      condvar_report(tid, condp, CVE_NotExist, "destroying");
+   else {
+      if (cv->state != CV_Alive)
+        condvar_report(tid, condp, CVE_NotInit, "destroying");
+      if (cv->waiters != NULL)
+        condvar_report(tid, condp, CVE_Busy, "destroying");
+      cv->state = CV_Dead;
+   }
+}
+
+static struct condvar_waiter *get_waiter(const struct condvar *cv, ThreadId tid)
+{
+   struct condvar_waiter *w;
+
+   for(w = cv->waiters; w; w = w->next)
+      if (w->waiter == tid)
+        return w;
+   return NULL;
+}
+
+/* Wait for a condition, putting thread into blocked state.  Fails if:
+    - condp has not been initialized
+    - thread doesn't hold mutexp
+    - thread is blocked on some other object
+    - thread is already blocked on mutex
+ */
+void VG_(tm_cond_wait)(ThreadId tid, Addr condp, Addr mutexp)
+{
+   struct thread *th = thread_get(tid);
+   struct mutex *mx;
+   struct condvar *cv;
+   struct condvar_waiter *waiter;
+
+   /* Condvar must exist */
+   cv = condvar_check_initialized(tid, condp, "waiting");
+
+   /* Mutex must exist */
+   mx = mutex_check_initialized(tid, mutexp, "waiting on condvar");
+
+   /* Thread must own mutex */
+   if (mx->state != MX_Locked) {
+      mutex_report(tid, mutexp, MXE_NotLocked, "waiting on condvar");
+      VG_(tm_mutex_trylock)(tid, mutexp);
+      VG_(tm_mutex_acquire)(tid, mutexp);
+   } else if (mx->owner != tid) {
+      mutex_report(tid, mutexp, MXE_NotOwner, "waiting on condvar");
+      mx->owner = tid;
+   }
+
+   /* Thread must not be already waiting for condvar */
+   waiter = get_waiter(cv, tid);
+   if (waiter != NULL)
+      condvar_report(tid, condp, CVE_Blocked, "waiting");
+   else {
+      waiter = VG_(arena_malloc)(VG_AR_CORE, sizeof(*waiter));
+      waiter->condvar = cv;
+      waiter->mutex = mx;
+      waiter->next = cv->waiters;
+      cv->waiters = waiter;
+   }
+
+   /* Thread is now blocking on condvar */
+   do_thread_block_condvar(th, cv);
+
+   /* (half) release mutex */
+   VG_(tm_mutex_tryunlock)(tid, mutexp);
+}
+
+/* Wake from a condition, either because we've been signalled, or
+   because of timeout.  Fails if:
+     - thread is not waiting on condp
+ */
+void VG_(tm_cond_wakeup)(ThreadId tid, Addr condp, Addr mutexp)
+{
+}
+
+/* Signal a condition variable.  Fails if:
+    - condp has not been initialized
+ */
+void VG_(tm_cond_signal)(ThreadId tid, Addr condp)
+{
+}
+
+/* --------------------------------------------------
+   Error handling
+   -------------------------------------------------- */
+
+UInt VG_(tm_error_update_extra)(Error *err)
+{
+   switch (VG_(get_error_kind)(err)) {
+      case ThreadErr: {
+         struct thread_error_data *errdata = VG_(get_error_extra)(err);
+         struct thread *new_th = VG_(arena_malloc)(VG_AR_CORE, sizeof(struct thread));
+
+         VG_(memcpy)(new_th, errdata->th, sizeof(struct thread));
+
+         errdata->th = new_th;
+
+         return sizeof(struct thread_error_data);
+      }
+
+      case MutexErr: {
+         struct mutex_error_data *errdata = VG_(get_error_extra)(err);
+         struct mutex *new_mx = VG_(arena_malloc)(VG_AR_CORE, sizeof(struct mutex));
+
+         VG_(memcpy)(new_mx, errdata->mx, sizeof(struct mutex));
+
+         errdata->mx = new_mx;
+
+         return sizeof(struct mutex_error_data);
+      }
+
+      default:
+         return 0;
+   }
+}
+
+Bool VG_(tm_error_equal)(VgRes res, Error *e1, Error *e2)
+{
+   /* Guaranteed by calling function */
+   vg_assert(VG_(get_error_kind)(e1) == VG_(get_error_kind)(e2));
+   
+   switch (VG_(get_error_kind)(e1)) {
+      case ThreadErr: {
+         struct thread_error_data *errdata1 = VG_(get_error_extra)(e1);
+         struct thread_error_data *errdata2 = VG_(get_error_extra)(e2);
+
+         return errdata1->err == errdata2->err;
+      }
+
+      case MutexErr: {
+         struct mutex_error_data *errdata1 = VG_(get_error_extra)(e1);
+         struct mutex_error_data *errdata2 = VG_(get_error_extra)(e2);
+
+         return errdata1->err == errdata2->err;
+      }
+
+      default: 
+         VG_(printf)("Error:\n  unknown error code %d\n",
+                     VG_(get_error_kind)(e1));
+         VG_(core_panic)("unknown error code in VG_(tm_error_equal)");
+   }
+}
+
+void VG_(tm_error_print)(Error *err)
+{
+   switch (VG_(get_error_kind)(err)) {
+      case ThreadErr:
+         pp_thread_error(err);
+         break;
+      case MutexErr:
+         pp_mutex_error(err);
+         break;
+   }
+}
+
+/* --------------------------------------------------
+   Initialisation
+   -------------------------------------------------- */
+
+static Word tm_compare_tid(ThreadId *tid1, ThreadId *tid2)
+{
+   if (*tid1 < *tid2) return -1;
+   if (*tid1 > *tid2) return 1;
+   return 0;
+}
+
+static Word tm_compare_addr(Addr *addr1, Addr *addr2)
+{
+   if (*addr1 < *addr2) return -1;
+   if (*addr1 > *addr2) return 1;
+   return 0;
+}
+
+static void* oset_malloc(SizeT szB)
+{
+   return VG_(arena_malloc)(VG_AR_CORE, szB);
+}
+
+static void oset_free(void * p)
+{
+   VG_(arena_free)(VG_AR_CORE, p);
+}
+
+void VG_(tm_init)()
+{
+   VG_(needs_core_errors)();
+
+   threadSet = VG_(OSet_Create)(offsetof(struct thread, tid),
+                                (OSetCmp_t)tm_compare_tid,
+                                oset_malloc, oset_free);
+   mutexSet = VG_(OSet_Create)(offsetof(struct mutex, mutex),
+                               (OSetCmp_t)tm_compare_addr,
+                               oset_malloc, oset_free);
+   condvarSet = VG_(OSet_Create)(offsetof(struct condvar, condvar),
+                                 (OSetCmp_t)tm_compare_addr,
+                                 oset_malloc, oset_free);
+}
 
 /*--------------------------------------------------------------------*/
 /*--- end                                                          ---*/
index 292ea89584a627678b111a1f9e2c01d63a03a61b..f003a6c947f68a1744894e068ce423a31f9ee433 100644 (file)
 // PURPOSE: This module does thread modelling stuff...
 //--------------------------------------------------------------------
 
-//extern void VG_(tm_thread_create)  (ThreadId creator, ThreadId tid, Bool detached);
-//extern void VG_(tm_thread_exit)    (ThreadId tid);
-//extern Bool VG_(tm_thread_exists)  (ThreadId tid);
-//extern void VG_(tm_thread_detach)  (ThreadId tid);
-//extern void VG_(tm_thread_join)    (ThreadId joiner, ThreadId joinee);
-//extern void VG_(tm_thread_switchto)(ThreadId tid);
-//
-//extern void VG_(tm_mutex_init)   (ThreadId tid, Addr mutexp);
-//extern void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp);
-//extern void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp);
-//extern void VG_(tm_mutex_giveup) (ThreadId tid, Addr mutexp);
-//extern void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp);
-//extern void VG_(tm_mutex_tryunlock)(ThreadId tid, Addr mutexp);
-//extern void VG_(tm_mutex_unlock) (ThreadId tid, Addr mutexp);
-//extern Bool VG_(tm_mutex_exists) (Addr mutexp);
-//
-//extern UInt VG_(tm_error_update_extra) (Error *err);
-//extern Bool VG_(tm_error_equal) (VgRes res, Error *e1, Error *e2);
-//extern void VG_(tm_error_print) (Error *err);
-//
-//extern void VG_(tm_init) ();
-//
-//extern void VG_(tm_cond_init)    (ThreadId tid, Addr condp);
-//extern void VG_(tm_cond_destroy) (ThreadId tid, Addr condp);
-//extern void VG_(tm_cond_wait)    (ThreadId tid, Addr condp, Addr mutexp);
-//extern void VG_(tm_cond_wakeup)  (ThreadId tid, Addr condp, Addr mutexp);
-//extern void VG_(tm_cond_signal)  (ThreadId tid, Addr condp);
+extern void VG_(tm_thread_create)  (ThreadId creator, ThreadId tid, Bool detached);
+extern void VG_(tm_thread_exit)    (ThreadId tid);
+extern Bool VG_(tm_thread_exists)  (ThreadId tid);
+extern void VG_(tm_thread_detach)  (ThreadId tid);
+extern void VG_(tm_thread_join)    (ThreadId joiner, ThreadId joinee);
+extern void VG_(tm_thread_switchto)(ThreadId tid);
+
+extern void VG_(tm_mutex_init)   (ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_giveup) (ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_tryunlock)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_unlock) (ThreadId tid, Addr mutexp);
+extern Bool VG_(tm_mutex_exists) (Addr mutexp);
+
+extern UInt VG_(tm_error_update_extra) (Error *err);
+extern Bool VG_(tm_error_equal) (VgRes res, Error *e1, Error *e2);
+extern void VG_(tm_error_print) (Error *err);
+
+extern void VG_(tm_init) (void);
+
+extern void VG_(tm_cond_init)    (ThreadId tid, Addr condp);
+extern void VG_(tm_cond_destroy) (ThreadId tid, Addr condp);
+extern void VG_(tm_cond_wait)    (ThreadId tid, Addr condp, Addr mutexp);
+extern void VG_(tm_cond_wakeup)  (ThreadId tid, Addr condp, Addr mutexp);
+extern void VG_(tm_cond_signal)  (ThreadId tid, Addr condp);
 
 #endif   // __PUB_CORE_THREADMODEL_H