|
From: <sv...@va...> - 2006-01-17 15:09:17
|
Author: tom
Date: 2006-01-17 15:09:07 +0000 (Tue, 17 Jan 2006)
New Revision: 5542
Log:
Get the core thread modelling code compiling again - uncomment the
code, fix the includes and switch to OSets instead of SkipLists.
Modified:
trunk/coregrind/m_threadmodel.c
trunk/coregrind/pub_core_threadmodel.h
Modified: trunk/coregrind/m_threadmodel.c
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/coregrind/m_threadmodel.c 2006-01-17 02:56:33 UTC (rev 5541)
+++ trunk/coregrind/m_threadmodel.c 2006-01-17 15:09:07 UTC (rev 5542)
@@ -61,1180 +61,1223 @@
*/
=20
#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"
=20
-//:: struct thread;
-//:: struct mutex;
-//:: struct condvar;
-//::=20
-//:: static const Bool debug_thread =3D False;
-//:: static const Bool debug_mutex =3D False;
-//::=20
-//:: /* --------------------------------------------------
-//:: Thread lifetime
-//:: =20
-//:: Threads are all expressed in terms of internal ThreadIds. The
-//:: thread library interface needs to map from the library's identif=
ers
-//:: to ThreadIds.
-//:: -------------------------------------------------- */
-//::=20
-//:: /* 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;
-//::=20
-//:: Bool detached; /* thread is detached */
-//::=20
-//:: enum thread_state {
-//:: TS_Alive, /* alive */
-//:: TS_Zombie, /* waiting to be joined on (detached is False) */
-//:: TS_Dead, /* all dead */
-//:: =20
-//:: TS_Running, /* running */
-//:: TS_MutexBlocked, /* blocked on mutex */
-//:: TS_CVBlocked, /* blocked on condvar */
-//:: TS_JoinBlocked, /* blocked in join */
-//:: } state;
-//::=20
-//:: struct mutex *mx_blocked; /* mutex we're blocked on (state=3D=3D=
TS_MutexBlocked) */
-//:: struct condvar *cv_blocked; /* condvar we're blocked on (state=3D=
=3DTS_CVBlocked) */
-//:: struct thread *th_blocked; /* thread we're blocked on (state=3D=3D=
TS_JoinBlocked) */
-//::=20
-//:: ExeContext *ec_created; /* where created */
-//:: ExeContext *ec_blocked; /* where blocked/unblocked */
-//:: };
-//::=20
-//:: 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 */
-//:: };
-//::=20
-//:: struct thread_error_data
-//:: {
-//:: enum thread_error err;
-//:: struct thread *th;
-//:: const Char *action;
-//:: };
-//::=20
-//:: static const Char *pp_threadstate(const struct thread *th)
-//:: {
-//:: if (th =3D=3D NULL)
-//:: return "non-existent";
-//::=20
-//:: 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 "???";
-//:: }
-//:: }
-//::=20
-//:: 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 =3D=3D NULL);
-//:: vg_assert(th->cv_blocked =3D=3D NULL);
-//:: vg_assert(th->th_blocked =3D=3D NULL);
-//:: break;
-//::=20
-//:: case TS_MutexBlocked:
-//:: vg_assert(th->mx_blocked !=3D NULL);
-//:: vg_assert(th->cv_blocked =3D=3D NULL);
-//:: vg_assert(th->th_blocked =3D=3D NULL);
-//:: break;
-//::=20
-//:: case TS_CVBlocked:
-//:: vg_assert(th->mx_blocked =3D=3D NULL);
-//:: vg_assert(th->cv_blocked !=3D NULL);
-//:: vg_assert(th->th_blocked =3D=3D NULL);
-//:: break;
-//::=20
-//:: case TS_JoinBlocked:
-//:: vg_assert(th->mx_blocked =3D=3D NULL);
-//:: vg_assert(th->cv_blocked =3D=3D NULL);
-//:: vg_assert(th->th_blocked !=3D NULL);
-//:: break;
-//:: }
-//:: }
-//::=20
-//:: static void thread_setstate(struct thread *th, enum thread_state st=
ate)
-//:: {
-//:: ExeContext *ec;
-//::=20
-//:: if (th->state =3D=3D state)
-//:: return;
-//::=20
-//:: ec =3D VG_(record_ExeContext)(th->tid);
-//::=20
-//:: switch(state) {
-//:: case TS_Alive:
-//:: case TS_Dead:
-//:: th->ec_created =3D ec;
-//:: break;
-//::=20
-//:: case TS_Running:
-//:: case TS_MutexBlocked:
-//:: case TS_CVBlocked:
-//:: case TS_JoinBlocked:
-//:: case TS_Zombie:
-//:: th->ec_blocked =3D ec;
-//:: }
-//::=20
-//:: th->state =3D state;
-//:: if (debug_thread)
-//:: VG_(printf)("setting thread(%d) -> %s\n", th->tid, pp_threads=
tate(th));
-//:: thread_validate(th);
-//:: }
-//::=20
-//:: static void do_thread_run(struct thread *th)
-//:: {
-//:: th->mx_blocked =3D NULL;
-//:: th->cv_blocked =3D NULL;
-//:: th->th_blocked =3D NULL;
-//:: thread_setstate(th, TS_Running);
-//:: }
-//::=20
-//:: static void do_thread_block_mutex(struct thread *th, struct mutex *=
mx)
-//:: {
-//:: th->mx_blocked =3D mx;
-//:: th->cv_blocked =3D NULL;
-//:: th->th_blocked =3D NULL;
-//:: thread_setstate(th, TS_MutexBlocked);
-//:: }
-//::=20
-//:: static void do_thread_block_condvar(struct thread *th, struct condv=
ar *cv)
-//:: {
-//:: th->mx_blocked =3D NULL;
-//:: th->cv_blocked =3D cv;
-//:: th->th_blocked =3D NULL;
-//:: thread_setstate(th, TS_CVBlocked);
-//:: }
-//::=20
-//:: static void do_thread_block_join(struct thread *th, struct thread *=
joinee)
-//:: {
-//:: th->mx_blocked =3D NULL;
-//:: th->cv_blocked =3D NULL;
-//:: th->th_blocked =3D joinee;
-//:: thread_setstate(th, TS_JoinBlocked);
-//:: }
-//::=20
-//:: static void do_thread_block_zombie(struct thread *th)
-//:: {
-//:: th->mx_blocked =3D NULL;
-//:: th->cv_blocked =3D NULL;
-//:: th->th_blocked =3D NULL;
-//:: thread_setstate(th, TS_Zombie);
-//:: }
-//::=20
-//:: static void do_thread_dead(struct thread *th)
-//:: {
-//:: th->mx_blocked =3D NULL;
-//:: th->cv_blocked =3D NULL;
-//:: th->th_blocked =3D NULL;
-//:: thread_setstate(th, TS_Dead);
-//:: }
-//::=20
-//:: static SkipList sk_threads =3D VG_SKIPLIST_INIT(struct thread, tid,=
VG_(cmp_UInt), NULL, VG_AR_CORE);
-//::=20
-//:: static struct thread *thread_get(ThreadId tid)
-//:: {
-//:: return VG_(SkipList_Find_Exact)(&sk_threads, &tid);
-//:: }
-//::=20
-//:: static void thread_report(ThreadId tid, enum thread_error err, cons=
t Char *action)
-//:: {
-//:: Char *errstr =3D "?";
-//:: struct thread *th =3D thread_get(tid);
-//:: struct thread_error_data errdata;
-//::=20
-//:: switch(err) {
-//:: case THE_NotExist: errstr =3D "non existent"; break;
-//:: case THE_NotAlive: errstr =3D "not alive"; break;
-//:: case THE_Rebirth: errstr =3D "re-born"; break;
-//:: case THE_Blocked: errstr =3D "blocked"; break;
-//:: case THE_NotBlocked: errstr =3D "not blocked"; break;
-//:: case THE_Detached: errstr =3D "detached"; break;
-//:: }
-//::=20
-//:: errdata.err =3D err;
-//:: errdata.th =3D th;
-//:: errdata.action =3D action;
-//:: =20
-//:: VG_(maybe_record_error)(VG_(get_running_tid)(), ThreadErr, 0, er=
rstr, &errdata);
-//:: }
-//::=20
-//:: static void pp_thread_error(Error *err)
-//:: {
-//:: struct thread_error_data *errdata =3D VG_(get_error_extra)(err);
-//:: struct thread *th =3D errdata->th;
-//:: Char *errstr =3D VG_(get_error_string)(err);
-//:: =20
-//:: 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));
-//::=20
-//:: if (th) {
-//:: VG_(message)(Vg_UserMsg, " Thread %d was %s",
-//:: th->tid, th->state =3D=3D TS_Dead ? "destroyed" : "created");
-//:: VG_(pp_ExeContext)(th->ec_created);
-//:: }
-//:: }
-//::=20
-//:: /* Thread creation */
-//:: void VG_(tm_thread_create)(ThreadId creator, ThreadId tid, Bool det=
ached)
-//:: {
-//:: struct thread *th =3D thread_get(tid);
-//::=20
-//:: if (debug_thread)
-//:: VG_(printf)("thread %d creates %d %s\n", creator, tid, detach=
ed ? "detached" : "");
-//:: if (th !=3D NULL) {
-//:: if (th->state !=3D TS_Dead)
-//:: thread_report(tid, THE_Rebirth, "creating");
-//:: } else {
-//:: th =3D VG_(SkipNode_Alloc)(&sk_threads);
-//:: th->tid =3D tid;
-//:: VG_(SkipList_Insert)(&sk_threads, th);
-//:: }
-//::=20
-//:: th->creator =3D creator;
-//:: th->detached =3D detached;
-//:: th->mx_blocked =3D NULL;
-//:: th->cv_blocked =3D NULL;
-//:: th->th_blocked =3D NULL;
-//::=20
-//:: thread_setstate(th, TS_Alive);
-//:: do_thread_run(th);
-//:: }
-//::=20
-//:: Bool VG_(tm_thread_exists)(ThreadId tid)
-//:: {
-//:: struct thread *th =3D thread_get(tid);
-//::=20
-//:: return th && th->state !=3D TS_Dead;
-//:: }
-//::=20
-//:: /* 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 =3D thread_get(tid);
-//::=20
-//:: if (th =3D=3D NULL)
-//:: thread_report(tid, THE_NotExist, "exiting");
-//:: else {
-//:: struct thread *joiner;
-//::=20
-//:: switch(th->state) {
-//:: case TS_Dead:
-//:: case TS_Zombie: /* already exited once */
-//:: thread_report(tid, THE_NotAlive, "exiting");
-//:: break;
-//::=20
-//:: case TS_MutexBlocked:
-//:: case TS_CVBlocked:
-//:: case TS_JoinBlocked:
-//:: thread_report(tid, THE_Blocked, "exiting");
-//:: break;
-//::=20
-//:: case TS_Alive:
-//:: case TS_Running:
-//:: /* OK */
-//:: break;
-//:: }
-//::=20
-//:: /* ugly - walk all threads to find people joining with us */
-//:: /* In pthreads its an error to have multiple joiners, but tha=
t
-//:: seems a bit specific to implement here; there should a way
-//:: for the thread library binding to handle this. */
-//:: for(joiner =3D VG_(SkipNode_First)(&sk_threads);
-//:: joiner !=3D NULL;
-//:: joiner =3D VG_(SkipNode_Next)(&sk_threads, joiner)) {
-//:: if (joiner->state =3D=3D TS_JoinBlocked && joiner->th_blocked =3D=
=3D th) {
-//:: /* found someone - wake them up */
-//:: do_thread_run(joiner);
-//::=20
-//:: /* we're dead */
-//:: do_thread_dead(th);
-//:: }
-//:: }
-//::=20
-//:: if (th->state !=3D TS_Dead)
-//:: do_thread_block_zombie(th);
-//:: }
-//:: }
-//::=20
-//:: void VG_(tm_thread_detach)(ThreadId tid)
-//:: {
-//:: struct thread *th =3D thread_get(tid);
-//::=20
-//:: if (th =3D=3D NULL)
-//:: thread_report(tid, THE_NotExist, "detaching");
-//:: else {
-//:: if (th->detached)
-//:: thread_report(tid, THE_Detached, "detaching");
-//:: else {
-//:: /* XXX look for waiters */
-//:: th->detached =3D True;
-//:: }
-//:: }
-//:: }
-//::=20
-//:: /* 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 =3D thread_get(joinerid);
-//:: struct thread *joinee =3D thread_get(joineeid);
-//::=20
-//:: /* First, check the joinee thread's state */
-//:: if (joinee =3D=3D 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;
-//::=20
-//:: case TS_Dead:
-//:: thread_report(joineeid, THE_NotAlive, "joining as joinee");
-//:: break;
-//:: }
-//:: }
-//::=20
-//:: /* now the joiner... */
-//:: if (joiner =3D=3D NULL)
-//:: thread_report(joineeid, THE_NotExist, "joining as joiner");
-//:: else {
-//:: switch(joiner->state) {
-//:: case TS_Alive: /* ? */
-//:: case TS_Running: /* OK */
-//:: break;
-//::=20
-//:: case TS_Zombie: /* back from the dead */
-//:: case TS_Dead:
-//:: thread_report(joineeid, THE_NotAlive, "joining as joiner");
-//:: break;
-//::=20
-//:: case TS_MutexBlocked:
-//:: case TS_CVBlocked:
-//:: case TS_JoinBlocked:
-//:: thread_report(joineeid, THE_Blocked, "joining as joiner");
-//:: break;
-//:: }
-//::=20
-//:: 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;
-//::=20
-//:: default:
-//:: if (joinee->state =3D=3D TS_Zombie)
-//:: do_thread_dead(joinee);
-//:: else
-//:: do_thread_block_join(joiner, joinee);
-//:: }
-//:: }
-//:: }
-//:: }
-//:: }
-//::=20
-//:: /* Context switch to a new thread */
-//:: void VG_(tm_thread_switchto)(ThreadId tid)
-//:: {
-//:: VG_TRACK( thread_run, tid );
-//:: }
-//::=20
-//:: static void thread_block_mutex(ThreadId tid, struct mutex *mx)
-//:: {
-//:: struct thread *th =3D thread_get(tid);
-//::=20
-//:: if (th =3D=3D 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;
-//::=20
-//:: case TS_MutexBlocked:
-//:: case TS_CVBlocked:
-//:: case TS_JoinBlocked:
-//:: thread_report(th->tid, THE_Blocked, "blocking on mutex");
-//:: break;
-//::=20
-//:: case TS_Alive:
-//:: case TS_Running: /* OK */
-//:: break;
-//:: }
-//::=20
-//:: do_thread_block_mutex(th, mx);
-//:: }
-//::=20
-//:: static void thread_unblock_mutex(ThreadId tid, struct mutex *mx, co=
nst Char *action)
-//:: {
-//:: struct thread *th =3D thread_get(tid);
-//::=20
-//:: if (th =3D=3D NULL) {
-//:: /* should an unknown thread doing something make it spring to=
life? */
-//:: thread_report(tid, THE_NotExist, "giving up on mutex");
-//:: return;
-//:: }
-//::=20
-//:: switch(th->state) {
-//:: case TS_MutexBlocked: /* OK */
-//:: break;
-//::=20
-//:: case TS_Alive:
-//:: case TS_Running:
-//:: thread_report(tid, THE_NotBlocked, action);
-//:: break;
-//::=20
-//:: case TS_CVBlocked:
-//:: case TS_JoinBlocked:
-//:: thread_report(tid, THE_Blocked, action);
-//:: break;
-//::=20
-//:: case TS_Dead:
-//:: case TS_Zombie:
-//:: thread_report(tid, THE_NotAlive, action);
-//:: break;
-//:: }
-//::=20
-//:: do_thread_run(th);
-//:: }
-//::=20
-//:: /* --------------------------------------------------
-//:: Mutexes
-//::=20
-//:: This models simple, non-recursive mutexes.
-//:: -------------------------------------------------- */
-//::=20
-//:: struct mutex
-//:: {
-//:: Addr mutex; /* address of mutex */
-//:: ThreadId owner; /* owner if state =3D=3D MX_Locked */
-//:: enum mutex_state {
-//:: MX_Init,
-//:: MX_Free,
-//:: MX_Locked,
-//:: MX_Unlocking, /* half-unlocked */
-//:: MX_Dead
-//:: } state; /* mutex state */
-//::=20
-//:: ExeContext *ec_create; /* where created/destroyed */
-//:: ExeContext *ec_locked; /* where last locked/unlocked */
-//:: };
-//::=20
-//:: 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 */
-//:: };
-//::=20
-//:: struct mutex_error_data
-//:: {
-//:: enum mutex_error err;
-//:: struct mutex *mx;
-//:: const Char *action;
-//:: };
-//::=20
-//:: static struct mutex *mutex_get(Addr mutexp);
-//::=20
-//:: static const Char *pp_mutexstate(const struct mutex *mx)
-//:: {
-//:: static Char buf[20];
-//::=20
-//:: switch(mx->state) {
-//:: case MX_Init: return "Init";
-//:: case MX_Free: return "Free";
-//:: case MX_Dead: return "Dead";
-//::=20
-//:: case MX_Locked:
-//:: VG_(sprintf)(buf, "Locked by tid %d", mx->owner);
-//:: break;
-//::=20
-//:: case MX_Unlocking:
-//:: VG_(sprintf)(buf, "Being unlocked by tid %d", mx->owner);
-//:: break;
-//::=20
-//:: default:
-//:: VG_(sprintf)(buf, "?? %d", mx->state);
-//:: break;
-//:: }
-//::=20
-//:: return buf;
-//:: }
-//::=20
-//:: static void mutex_setstate(ThreadId tid, struct mutex *mx, enum mut=
ex_state st)
-//:: {
-//:: ExeContext *ec =3D VG_(record_ExeContext)(tid);
-//::=20
-//:: switch(st) {
-//:: case MX_Init:
-//:: case MX_Dead:
-//:: mx->ec_create =3D ec;
-//:: break;
-//::=20
-//:: case MX_Unlocking:
-//:: case MX_Locked:
-//:: case MX_Free:
-//:: mx->ec_locked =3D ec;
-//:: break;
-//:: }
-//::=20
-//:: mx->state =3D st;
-//:: if (debug_mutex)
-//:: VG_(printf)("setting mutex(%p) -> %s\n", mx->mutex, pp_mutexs=
tate(mx));
-//:: }
-//::=20
-//:: static void mutex_report(ThreadId tid, Addr mutexp, enum mutex_erro=
r err, const Char *action)
-//:: {
-//:: Char *errstr=3D"?";
-//:: struct mutex *mx =3D mutex_get(mutexp);
-//:: struct mutex_error_data errdata;
-//::=20
-//:: switch(err) {
-//:: case MXE_NotExist: errstr=3D"non-existent"; break;
-//:: case MXE_NotInit: errstr=3D"uninitialized"; break;
-//:: case MXE_ReInit: errstr=3D"already initialized"; break;
-//:: case MXE_NotLocked: errstr=3D"not locked"; break;
-//:: case MXE_Locked: errstr=3D"locked"; break;
-//:: case MXE_NotOwner: errstr=3D"unowned"; break;
-//:: case MXE_Deadlock: errstr=3D"deadlock on"; break;
-//:: }
-//::=20
-//:: errdata.err =3D err;
-//:: errdata.mx =3D mx;
-//:: errdata.action =3D action;
-//:: =20
-//:: VG_(maybe_record_error)(tid, MutexErr, 0, errstr, &errdata);
-//:: }
-//::=20
-//:: static void pp_mutex_error(Error *err)
-//:: {
-//:: struct mutex_error_data *errdata =3D VG_(get_error_extra)(err);
-//:: struct mutex *mx =3D errdata->mx;
-//:: Char *errstr =3D VG_(get_error_string)(err);
-//:: =20
-//:: 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));
-//::=20
-//:: 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;
-//:: }
-//::=20
-//:: VG_(message)(Vg_UserMsg, " Mutex was %s",
-//:: mx->state =3D=3D MX_Dead ? "destroyed" : "created")=
;
-//:: VG_(pp_ExeContext)(mx->ec_create);
-//:: }
-//::=20
-//:: static SkipList sk_mutex =3D VG_SKIPLIST_INIT(struct mutex, mutex, =
VG_(cmp_Addr), NULL, VG_AR_CORE);
-//::=20
-//:: static struct mutex *mutex_get(Addr mutexp)
-//:: {
-//:: return VG_(SkipList_Find_Exact)(&sk_mutex, &mutexp);
-//:: }
-//::=20
-//:: static Bool mx_is_initialized(Addr mutexp)
-//:: {
-//:: const struct mutex *mx =3D mutex_get(mutexp);
-//::=20
-//:: return mx && mx->state !=3D MX_Dead;
-//:: }
-//::=20
-//:: static struct mutex *mutex_check_initialized(ThreadId tid, Addr mut=
exp, const Char *action)
-//:: {
-//:: struct mutex *mx;
-//::=20
-//:: vg_assert(tid !=3D VG_INVALID_THREADID);
-//::=20
-//:: if (!mx_is_initialized(mutexp)) {
-//:: mutex_report(tid, mutexp, MXE_NotInit, action);
-//:: VG_(tm_mutex_init)(tid, mutexp);
-//:: }
-//::=20
-//:: mx =3D mutex_get(mutexp);
-//:: vg_assert(mx !=3D NULL);
-//::=20
-//:: return mx;
-//:: }
-//::=20
-//:: #if 0
-//:: static Bool mx_is_locked(Addr mutexp)
-//:: {
-//:: const struct mutex *mx =3D mutex_get(mutexp);
-//::=20
-//:: return mx && (mx->state =3D=3D MX_Locked);
-//:: }
-//:: #endif
-//::=20
-//:: /* 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 =3D mutex_get(mutexp);
-//::=20
-//:: if (mx =3D=3D NULL) {
-//:: mx =3D VG_(SkipNode_Alloc)(&sk_mutex);
-//:: mx->mutex =3D mutexp;
-//:: VG_(SkipList_Insert)(&sk_mutex, mx);
-//:: } else if (mx->state !=3D MX_Dead)
-//:: mutex_report(tid, mutexp, MXE_ReInit, "initializing");
-//::=20
-//:: mx->owner =3D VG_INVALID_THREADID;
-//::=20
-//:: mutex_setstate(tid, mx, MX_Init);
-//:: mutex_setstate(tid, mx, MX_Free);
-//:: }
-//::=20
-//:: Bool VG_(tm_mutex_exists)(Addr mutexp)
-//:: {
-//:: return mx_is_initialized(mutexp);
-//:: }
-//::=20
-//:: /* 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 =3D mutex_get(mutexp);
-//::=20
-//:: if (mx =3D=3D NULL)
-//:: mutex_report(tid, mutexp, MXE_NotExist, "destroying");
-//:: else {
-//:: switch(mx->state) {
-//:: case MX_Dead:
-//:: mutex_report(tid, mutexp, MXE_NotInit, "destroying");
-//:: break;
-//::=20
-//:: case MX_Locked:
-//:: case MX_Unlocking:
-//:: mutex_report(tid, mutexp, MXE_Locked, "destroying");
-//:: VG_(tm_mutex_unlock)(tid, mutexp);
-//:: break;
-//::=20
-//:: case MX_Init:
-//:: case MX_Free:
-//:: /* OK */
-//:: break;
-//:: }
-//:: mutex_setstate(tid, mx, MX_Dead);
-//:: }
-//:: }
-//::=20
-//:: /* A thread attempts to lock a mutex. If "blocking" then the threa=
d
-//:: is put into a blocked state until the lock is acquired. Fails i=
f:
-//:: - 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;
-//::=20
-//:: mx =3D mutex_check_initialized(tid, mutexp, "trylocking");
-//::=20
-//:: thread_block_mutex(tid, mx);
-//::=20
-//:: if (mx->state =3D=3D MX_Locked && mx->owner =3D=3D tid) /* deadl=
ock */
-//:: mutex_report(tid, mutexp, MXE_Deadlock, "trylocking");
-//::=20
-//:: VG_TRACK( pre_mutex_lock, tid, (void *)mutexp );
-//:: }
-//::=20
-//:: /* 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;
-//::=20
-//:: mx =3D mutex_check_initialized(tid, mutexp, "giving up");
-//::=20
-//:: thread_unblock_mutex(tid, mx, "giving up on mutex");
-//:: }
-//::=20
-//:: /* 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;
-//::=20
-//:: mx =3D mutex_check_initialized(tid, mutexp, "acquiring"); =20
-//:: =20
-//:: switch(mx->state) {
-//:: case MX_Unlocking: /* ownership transfer or relock */
-//:: VG_TRACK( post_mutex_unlock, mx->owner, (void *)mutexp );
-//:: if (mx->owner !=3D tid)
-//:: thread_unblock_mutex(tid, mx, "acquiring mutex");
-//:: break;
-//::=20
-//:: case MX_Free:
-//:: thread_unblock_mutex(tid, mx, "acquiring mutex");
-//:: break;
-//::=20
-//:: case MX_Locked:
-//:: if (debug_mutex)
-//:: VG_(printf)("mutex=3D%p mx->state=3D%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;
-//::=20
-//:: case MX_Init:
-//:: case MX_Dead:
-//:: vg_assert(0);
-//:: }=20
-//:: =20
-//:: mx->owner =3D tid;
-//:: mutex_setstate(tid, mx, MX_Locked);
-//::=20
-//:: VG_TRACK( post_mutex_lock, tid, (void *)mutexp );
-//:: }
-//::=20
-//:: /* Try unlocking a lock. This will move it into a state where it c=
an
-//:: 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;
-//::=20
-//:: mx =3D mutex_check_initialized(tid, mutexp, "try-unlocking");
-//::=20
-//:: th =3D thread_get(tid);
-//::=20
-//:: if (th =3D=3D NULL)
-//:: thread_report(tid, THE_NotExist, "try-unlocking mutex");
-//:: else {
-//:: switch(th->state) {
-//:: case TS_Alive:
-//:: case TS_Running: /* OK */
-//:: break;
-//::=20
-//:: case TS_Dead:
-//:: case TS_Zombie:
-//:: thread_report(tid, THE_NotAlive, "try-unlocking mutex");
-//:: break;
-//::=20
-//:: case TS_JoinBlocked:
-//:: case TS_CVBlocked:
-//:: case TS_MutexBlocked:
-//:: thread_report(tid, THE_Blocked, "try-unlocking mutex");
-//:: do_thread_run(th);
-//:: break;
-//:: }
-//:: }
-//::=20
-//:: switch(mx->state) {
-//:: case MX_Locked:
-//:: if (mx->owner !=3D tid)
-//:: mutex_report(tid, mutexp, MXE_NotOwner, "try-unlocking");
-//:: break;
-//::=20
-//:: case MX_Free:
-//:: mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking");
-//:: break;
-//::=20
-//:: case MX_Unlocking:
-//:: mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking");
-//:: break;
-//::=20
-//:: case MX_Init:
-//:: case MX_Dead:
-//:: vg_assert(0);
-//:: }
-//::=20
-//:: mutex_setstate(tid, mx, MX_Unlocking);
-//:: }
-//::=20
-//:: /* Finish unlocking a Mutex. The mutex can validly be in one of th=
ree
-//:: states:
-//:: - Unlocking
-//:: - Locked, owned by someone else (someone else got it in the mean=
time)
-//:: - Free (someone else completed a lock-unlock cycle)
-//:: */
-//:: void VG_(tm_mutex_unlock)(ThreadId tid, Addr mutexp)
-//:: {
-//:: struct mutex *mx;
-//:: struct thread *th;
-//::=20
-//:: mx =3D mutex_check_initialized(tid, mutexp, "unlocking mutex");
-//::=20
-//:: th =3D thread_get(tid);
-//::=20
-//:: if (th =3D=3D NULL)
-//:: thread_report(tid, THE_NotExist, "unlocking mutex");
-//:: else {
-//:: switch(th->state) {
-//:: case TS_Alive:
-//:: case TS_Running: /* OK */
-//:: break;
-//::=20
-//:: case TS_Dead:
-//:: case TS_Zombie:
-//:: thread_report(tid, THE_NotAlive, "unlocking mutex");
-//:: break;
-//::=20
-//:: case TS_JoinBlocked:
-//:: case TS_CVBlocked:
-//:: case TS_MutexBlocked:
-//:: thread_report(tid, THE_Blocked, "unlocking mutex");
-//:: do_thread_run(th);
-//:: break;
-//:: }
-//:: }
-//::=20
-//:: switch(mx->state) {
-//:: case MX_Locked:
-//:: /* Someone else might have taken ownership in the meantime */
-//:: if (mx->owner =3D=3D tid)
-//:: mutex_report(tid, mutexp, MXE_Locked, "unlocking");
-//:: break;
-//::=20
-//:: case MX_Free:
-//:: /* OK - nothing to do */
-//:: break;
-//::=20
-//:: 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;
-//::=20
-//:: case MX_Init:
-//:: case MX_Dead:
-//:: vg_assert(0);
-//:: }
-//:: }
-//::=20
-//:: /* --------------------------------------------------
-//:: Condition variables
-//:: -------------------------------------------------- */
-//::=20
-//:: struct condvar_waiter
-//:: {
-//:: ThreadId waiter;
-//:: =20
-//:: struct condvar *condvar;
-//:: struct mutex *mutex;
-//::=20
-//:: struct condvar_waiter *next;
-//:: };
-//::=20
-//:: struct condvar=20
-//:: {
-//:: Addr condvar;
-//::=20
-//:: enum condvar_state {
-//:: CV_Dead,
-//:: CV_Alive,
-//:: } state;
-//::=20
-//:: struct condvar_waiter *waiters; // XXX skiplist?
-//:: =20
-//:: ExeContext *ec_created; // where created
-//:: ExeContext *ec_signalled; // where last signalled
-//:: };
-//::=20
-//:: enum condvar_err {
-//:: CVE_NotExist,
-//:: CVE_NotInit,
-//:: CVE_ReInit,
-//:: CVE_Busy,
-//:: CVE_Blocked,
-//:: };
-//::=20
-//:: static SkipList sk_condvar =3D VG_SKIPLIST_INIT(struct condvar, con=
dvar, VG_(cmp_Addr),
-//:: NULL, VG_AR_CORE);
-//::=20
-//:: static struct condvar *condvar_get(Addr condp)
-//:: {
-//:: return VG_(SkipList_Find_Exact)(&sk_condvar, &condp);
-//:: }
-//::=20
-//:: static Bool condvar_is_initialized(Addr condp)
-//:: {
-//:: const struct condvar *cv =3D condvar_get(condp);
-//::=20
-//:: return cv && cv->state !=3D CV_Dead;
-//:: }
-//::=20
-//:: static void condvar_report(ThreadId tid, Addr condp, enum condvar_e=
rr err, const Char *action)
-//:: {
-//:: }
-//::=20
-//:: static struct condvar *condvar_check_initialized(ThreadId tid, Addr=
condp, const Char *action)
-//:: {
-//:: struct condvar *cv;
-//:: vg_assert(tid !=3D VG_INVALID_THREADID);
-//:: =20
-//:: if (!condvar_is_initialized(condp)) {
-//:: condvar_report(tid, condp, CVE_NotInit, action);
-//:: VG_(tm_cond_init)(tid, condp);
-//:: }
-//::=20
-//:: cv =3D condvar_get(condp);
-//:: vg_assert(cv !=3D NULL);
-//::=20
-//:: return cv;
-//:: }
-//::=20
-//:: /* Initialize a condition variable. Fails if:
-//:: - condp has already been initialized
-//:: */
-//:: void VG_(tm_cond_init)(ThreadId tid, Addr condp)
-//:: {
-//:: struct condvar *cv =3D condvar_get(condp);
-//::=20
-//:: if (cv =3D=3D NULL) {
-//:: cv =3D VG_(SkipNode_Alloc)(&sk_condvar);
-//:: cv->condvar =3D condp;
-//:: cv->waiters =3D NULL;
-//:: VG_(SkipList_Insert)(&sk_condvar, cv);
-//:: } else if (cv->state !=3D CV_Dead) {
-//:: condvar_report(tid, condp, CVE_ReInit, "initializing");
-//:: /* ? what about existing waiters? */
-//:: }
-//::=20
-//:: cv->state =3D CV_Alive;
-//:: }
-//::=20
-//:: /* 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 =3D condvar_get(condp);
-//::=20
-//:: if (cv =3D=3D NULL)
-//:: condvar_report(tid, condp, CVE_NotExist, "destroying");
-//:: else {
-//:: if (cv->state !=3D CV_Alive)
-//:: condvar_report(tid, condp, CVE_NotInit, "destroying");
-//:: if (cv->waiters !=3D NULL)
-//:: condvar_report(tid, condp, CVE_Busy, "destroying");
-//:: cv->state =3D CV_Dead;
-//:: }
-//:: }
-//::=20
-//:: static struct condvar_waiter *get_waiter(const struct condvar *cv, =
ThreadId tid)
-//:: {
-//:: struct condvar_waiter *w;
-//::=20
-//:: for(w =3D cv->waiters; w; w =3D w->next)
-//:: if (w->waiter =3D=3D tid)
-//:: return w;
-//:: return NULL;
-//:: }
-//::=20
-//:: /* 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 =3D thread_get(tid);
-//:: struct mutex *mx;
-//:: struct condvar *cv;
-//:: struct condvar_waiter *waiter;
-//::=20
-//:: /* Condvar must exist */
-//:: cv =3D condvar_check_initialized(tid, condp, "waiting");
-//::=20
-//:: /* Mutex must exist */
-//:: mx =3D mutex_check_initialized(tid, mutexp, "waiting on condvar"=
);
-//::=20
-//:: /* Thread must own mutex */
-//:: if (mx->state !=3D 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 !=3D tid) {
-//:: mutex_report(tid, mutexp, MXE_NotOwner, "waiting on condvar")=
;
-//:: mx->owner =3D tid;
-//:: }
-//::=20
-//:: /* Thread must not be already waiting for condvar */
-//:: waiter =3D get_waiter(cv, tid);
-//:: if (waiter !=3D NULL)
-//:: condvar_report(tid, condp, CVE_Blocked, "waiting");
-//:: else {
-//:: waiter =3D VG_(arena_malloc)(VG_AR_CORE, sizeof(*waiter));
-//:: waiter->condvar =3D cv;
-//:: waiter->mutex =3D mx;
-//:: waiter->next =3D cv->waiters;
-//:: cv->waiters =3D waiter;
-//:: }
-//::=20
-//:: /* Thread is now blocking on condvar */
-//:: do_thread_block_condvar(th, cv);
-//::=20
-//:: /* (half) release mutex */
-//:: VG_(tm_mutex_tryunlock)(tid, mutexp);
-//:: }
-//::=20
-//:: /* 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)
-//:: {
-//:: }
-//::=20
-//:: /* Signal a condition variable. Fails if:
-//:: - condp has not been initialized
-//:: */
-//:: void VG_(tm_cond_signal)(ThreadId tid, Addr condp)
-//:: {
-//:: }
-//::=20
-//:: /* --------------------------------------------------
-//:: Error handling
-//:: -------------------------------------------------- */
-//::=20
-//:: UInt VG_(tm_error_update_extra)(Error *err)
-//:: {
-//:: switch (VG_(get_error_kind)(err)) {
-//:: case ThreadErr: {
-//:: struct thread_error_data *errdata =3D VG_(get_error_extra)=
(err);
-//:: struct thread *new_th =3D VG_(arena_malloc)(VG_AR_CORE, si=
zeof(struct thread));
-//::=20
-//:: VG_(memcpy)(new_th, errdata->th, sizeof(struct thread));
-//::=20
-//:: errdata->th =3D new_th;
-//::=20
-//:: return sizeof(struct thread_error_data);
-//:: }
-//::=20
-//:: case MutexErr: {
-//:: struct mutex_error_data *errdata =3D VG_(get_error_extra)(=
err);
-//:: struct mutex *new_mx =3D VG_(arena_malloc)(VG_AR_CORE, siz=
eof(struct mutex));
-//::=20
-//:: VG_(memcpy)(new_mx, errdata->mx, sizeof(struct mutex));
-//::=20
-//:: errdata->mx =3D new_mx;
-//::=20
-//:: return sizeof(struct mutex_error_data);
-//:: }
-//::=20
-//:: default:
-//:: return 0;
-//:: }
-//:: }
-//::=20
-//:: Bool VG_(tm_error_equal)(VgRes res, Error *e1, Error *e2)
-//:: {
-//:: /* Guaranteed by calling function */
-//:: vg_assert(VG_(get_error_kind)(e1) =3D=3D VG_(get_error_kind)(e2)=
);
-//:: =20
-//:: switch (VG_(get_error_kind)(e1)) {
-//:: case ThreadErr: {
-//:: struct thread_error_data *errdata1 =3D VG_(get_error_extra=
)(e1);
-//:: struct thread_error_data *errdata2 =3D VG_(get_error_extra=
)(e2);
-//::=20
-//:: return errdata1->err =3D=3D errdata2->err;
-//:: }
-//::=20
-//:: case MutexErr: {
-//:: struct mutex_error_data *errdata1 =3D VG_(get_error_extra)=
(e1);
-//:: struct mutex_error_data *errdata2 =3D VG_(get_error_extra)=
(e2);
-//::=20
-//:: return errdata1->err =3D=3D errdata2->err;
-//:: }
-//::=20
-//:: default:=20
-//:: 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)=
");
-//:: }
-//:: }
-//::=20
-//:: 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;
-//:: }
-//:: }
-//::=20
-//:: /* --------------------------------------------------
-//:: Initialisation
-//:: -------------------------------------------------- */
-//::=20
-//:: void VG_(tm_init)()
-//:: {
-//:: VG_(needs_core_errors)();
-//:: }
+struct thread;
+struct mutex;
+struct condvar;
=20
+static const Bool debug_thread =3D False;
+static const Bool debug_mutex =3D False;
+
+/* --------------------------------------------------
+ Thread lifetime
+ =20
+ 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 */
+ =20
+ 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=3D=3DTS_M=
utexBlocked) */
+ struct condvar *cv_blocked; /* condvar we're blocked on (state=3D=3DT=
S_CVBlocked) */
+ struct thread *th_blocked; /* thread we're blocked on (state=3D=3DTS_=
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 =3D=3D 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 =3D=3D NULL);
+ vg_assert(th->cv_blocked =3D=3D NULL);
+ vg_assert(th->th_blocked =3D=3D NULL);
+ break;
+
+ case TS_MutexBlocked:
+ vg_assert(th->mx_blocked !=3D NULL);
+ vg_assert(th->cv_blocked =3D=3D NULL);
+ vg_assert(th->th_blocked =3D=3D NULL);
+ break;
+
+ case TS_CVBlocked:
+ vg_assert(th->mx_blocked =3D=3D NULL);
+ vg_assert(th->cv_blocked !=3D NULL);
+ vg_assert(th->th_blocked =3D=3D NULL);
+ break;
+
+ case TS_JoinBlocked:
+ vg_assert(th->mx_blocked =3D=3D NULL);
+ vg_assert(th->cv_blocked =3D=3D NULL);
+ vg_assert(th->th_blocked !=3D NULL);
+ break;
+ }
+}
+
+static void thread_setstate(struct thread *th, enum thread_state state)
+{
+ ExeContext *ec;
+
+ if (th->state =3D=3D state)
+ return;
+
+ ec =3D VG_(record_ExeContext)(th->tid);
+
+ switch(state) {
+ case TS_Alive:
+ case TS_Dead:
+ th->ec_created =3D ec;
+ break;
+
+ case TS_Running:
+ case TS_MutexBlocked:
+ case TS_CVBlocked:
+ case TS_JoinBlocked:
+ case TS_Zombie:
+ th->ec_blocked =3D ec;
+ }
+
+ th->state =3D 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 =3D NULL;
+ th->cv_blocked =3D NULL;
+ th->th_blocked =3D NULL;
+ thread_setstate(th, TS_Running);
+}
+
+static void do_thread_block_mutex(struct thread *th, struct mutex *mx)
+{
+ th->mx_blocked =3D mx;
+ th->cv_blocked =3D NULL;
+ th->th_blocked =3D NULL;
+ thread_setstate(th, TS_MutexBlocked);
+}
+
+static void do_thread_block_condvar(struct thread *th, struct condvar *c=
v)
+{
+ th->mx_blocked =3D NULL;
+ th->cv_blocked =3D cv;
+ th->th_blocked =3D NULL;
+ thread_setstate(th, TS_CVBlocked);
+}
+
+static void do_thread_block_join(struct thread *th, struct thread *joine=
e)
+{
+ th->mx_blocked =3D NULL;
+ th->cv_blocked =3D NULL;
+ th->th_blocked =3D joinee;
+ thread_setstate(th, TS_JoinBlocked);
+}
+
+static void do_thread_block_zombie(struct thread *th)
+{
+ th->mx_blocked =3D NULL;
+ th->cv_blocked =3D NULL;
+ th->th_blocked =3D NULL;
+ thread_setstate(th, TS_Zombie);
+}
+
+static void do_thread_dead(struct thread *th)
+{
+ th->mx_blocked =3D NULL;
+ th->cv_blocked =3D NULL;
+ th->th_blocked =3D NULL;
+ thread_setstate(th, TS_Dead);
+}
+
+static OSet *threadSet =3D 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 Cha=
r *action)
+{
+ Char *errstr =3D "?";
+ struct thread *th =3D thread_get(tid);
+ struct thread_error_data errdata;
+
+ switch(err) {
+ case THE_NotExist: errstr =3D "non existent"; break;
+ case THE_NotAlive: errstr =3D "not alive"; break;
+ case THE_Rebirth: errstr =3D "re-born"; break;
+ case THE_Blocked: errstr =3D "blocked"; break;
+ case THE_NotBlocked: errstr =3D "not blocked"; break;
+ case THE_Detached: errstr =3D "detached"; break;
+ }
+
+ errdata.err =3D err;
+ errdata.th =3D th;
+ errdata.action =3D action;
+ =20
+ VG_(maybe_record_error)(VG_(get_running_tid)(), ThreadErr, 0, errstr,=
&errdata);
+}
+
+static void pp_thread_error(Error *err)
+{
+ struct thread_error_data *errdata =3D VG_(get_error_extra)(err);
+ struct thread *th =3D errdata->th;
+ Char *errstr =3D VG_(get_error_string)(err);
+ =20
+ 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 =3D=3D 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 =3D thread_get(tid);
+
+ if (debug_thread)
+ VG_(printf)("thread %d creates %d %s\n", creator, tid, detached ? =
"detached" : "");
+ if (th !=3D NULL) {
+ if (th->state !=3D TS_Dead)
+ thread_report(tid, THE_Rebirth, "creating");
+ } else {
+ th =3D VG_(OSet_AllocNode)(threadSet, sizeof(struct thread));
+ th->tid =3D tid;
+ VG_(OSet_Insert)(threadSet, th);
+ }
+
+ th->creator =3D creator;
+ th->detached =3D detached;
+ th->mx_blocked =3D NULL;
+ th->cv_blocked =3D NULL;
+ th->th_blocked =3D NULL;
+
+ thread_setstate(th, TS_Alive);
+ do_thread_run(th);
+}
+
+Bool VG_(tm_thread_exists)(ThreadId tid)
+{
+ struct thread *th =3D thread_get(tid);
+
+ return th && th->state !=3D 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 =3D thread_get(tid);
+
+ if (th =3D=3D 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 =3D VG_(OSet_Next)(threadSet)) !=3D NULL) {
+ if (joiner->state =3D=3D TS_JoinBlocked && joiner->th_blocked =3D=3D t=
h) {
+ /* found someone - wake them up */
+ do_thread_run(joiner);
+
+ /* we're dead */
+ do_thread_dead(th);
+ }
+ }
+
+ if (th->state !=3D TS_Dead)
+ do_thread_block_zombie(th);
+ }
+}
+
+void VG_(tm_thread_detach)(ThreadId tid)
+{
+ struct thread *th =3D thread_get(tid);
+
+ if (th =3D=3D NULL)
+ thread_report(tid, THE_NotExist, "detaching");
+ else {
+ if (th->detached)
+ thread_report(tid, THE_Detached, "detaching");
+ else {
+ /* XXX look for waiters */
+ th->detached =3D 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 =3D thread_get(joinerid);
+ struct thread *joinee =3D thread_get(joineeid);
+
+ /* First, check the joinee thread's state */
+ if (joinee =3D=3D 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 =3D=3D 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 =3D=3D 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 =3D thread_get(tid);
+
+ if (th =3D=3D 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 C=
har *action)
+{
+ struct thread *th =3D thread_get(tid);
+
+ if (th =3D=3D 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 =3D=3D 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_st=
ate st)
+{
+ ExeContext *ec =3D VG_(record_ExeContext)(tid);
+
+ switch(st) {
+ case MX_Init:
+ case MX_Dead:
+ mx->ec_create =3D ec;
+ break;
+
+ case MX_Unlocking:
+ case MX_Locked:
+ case MX_Free:
+ mx->ec_locked =3D ec;
+ break;
+ }
+
+ mx->state =3D 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=3D"?";
+ struct mutex *mx =3D mutex_get(mutexp);
+ struct mutex_error_data errdata;
+
+ switch(err) {
+ case MXE_NotExist: errstr=3D"non-existent"; break;
+ case MXE_NotInit: errstr=3D"uninitialized"; break;
+ case MXE_ReInit: errstr=3D"already initialized"; break;
+ case MXE_NotLocked: errstr=3D"not locked"; break;
+ case MXE_Locked: errstr=3D"locked"; break;
+ case MXE_NotOwner: errstr=3D"unowned"; break;
+ case MXE_Deadlock: errstr=3D"deadlock on"; break;
+ }
+
+ errdata.err =3D err;
+ errdata.mx =3D mx;
+ errdata.action =3D action;
+ =20
+ VG_(maybe_record_error)(tid, MutexErr, 0, errstr, &errdata);
+}
+
+static void pp_mutex_error(Error *err)
+{
+ struct mutex_error_data *errdata =3D VG_(get_error_extra)(err);
+ struct mutex *mx =3D errdata->mx;
+ Char *errstr =3D VG_(get_error_string)(err);
+ =20
+ 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 =3D=3D MX_Dead ? "destroyed" : "created");
+ VG_(pp_ExeContext)(mx->ec_create);
+}
+
+static OSet *mutexSet =3D 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 =3D mutex_get(mutexp);
+
+ return mx && mx->state !=3D MX_Dead;
+}
+
+static struct mutex *mutex_check_initialized(ThreadId tid, Addr mutexp, =
const Char *action)
+{
+ struct mutex *mx;
+
+ vg_assert(tid !=3D VG_INVALID_THREADID);
+
+ if (!mx_is_initialized(mutexp)) {
+ mutex_report(tid, mutexp, MXE_NotInit, action);
+ VG_(tm_mutex_init)(tid, mutexp);
+ }
+
+ mx =3D mutex_get(mutexp);
+ vg_assert(mx !=3D NULL);
+
+ return mx;
+}
+
+#if 0
+static Bool mx_is_locked(Addr mutexp)
+{
+ const struct mutex *mx =3D mutex_get(mutexp);
+
+ return mx && (mx->state =3D=3D 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 =3D mutex_get(mutexp);
+
+ if (mx =3D=3D NULL) {
+ mx =3D VG_(OSet_AllocNode)(mutexSet, sizeof(struct mutex));
+ mx->mutex =3D mutexp;
+ VG_(OSet_Insert)(mutexSet, mx);
+ } else if (mx->state !=3D MX_Dead)
+ mutex_report(tid, mutexp, MXE_ReInit, "initializing");
+
+ mx->owner =3D 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 =3D mutex_get(mutexp);
+
+ if (mx =3D=3D 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 =3D mutex_check_initialized(tid, mutexp, "trylocking");
+
+ thread_block_mutex(tid, mx);
+
+ if (mx->state =3D=3D MX_Locked && mx->owner =3D=3D 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 =3D 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 =3D mutex_check_initialized(tid, mutexp, "acquiring"); =20
+ =20
+ switch(mx->state) {
+ case MX_Unlocking: /* ownership transfer or relock */
+ VG_TRACK( post_mutex_unlock, mx->owner, (void *)mutexp );
+ if (mx->owner !=3D 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=3D%p mx->state=3D%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);
+ }=20
+ =20
+ mx->owner =3D 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 =3D mutex_check_initialized(tid, mutexp, "try-unlocking");
+
+ th =3D thread_get(tid);
+
+ if (th =3D=3D 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 !=3D tid)
+ mutex_report(tid, mutexp, MXE_NotOwner, "try-unlocking");
+ break;
+
+ case MX_Free:
+ mutex_report(tid, mutexp, MXE_NotLocked, "try-unlocking");
+ break;
+
+ case...
[truncated message content] |