From: Tom Hughes Date: Tue, 17 Jan 2006 15:09:07 +0000 (+0000) Subject: Get the core thread modelling code compiling again - uncomment the X-Git-Tag: svn/VALGRIND_3_2_0~369 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6c08001d8ee9b1d0f3f914523906290f04514723;p=thirdparty%2Fvalgrind.git Get the core thread modelling code compiling again - uncomment the code, fix the includes and switch to OSets instead of SkipLists. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@5542 --- diff --git a/coregrind/m_threadmodel.c b/coregrind/m_threadmodel.c index 9787ac9e4a..6e3c64f3a0 100644 --- a/coregrind/m_threadmodel.c +++ b/coregrind/m_threadmodel.c @@ -61,1179 +61,1222 @@ */ #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 ---*/ diff --git a/coregrind/pub_core_threadmodel.h b/coregrind/pub_core_threadmodel.h index 292ea89584..f003a6c947 100644 --- a/coregrind/pub_core_threadmodel.h +++ b/coregrind/pub_core_threadmodel.h @@ -35,33 +35,33 @@ // 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