+++ /dev/null
-
-/*--------------------------------------------------------------------*/
-/*--- Pthreads library modelling. m_pthreadmodel.c ---*/
-/*--------------------------------------------------------------------*/
-
-/*
- This file is part of Valgrind, a dynamic binary instrumentation
- framework.
-
- Copyright (C) 2005 Jeremy Fitzhardinge
- jeremy@goop.org
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307, USA.
-
- The GNU General Public License is contained in the file COPYING.
-*/
-
-/*
- This file wraps the client's use of libpthread functions and calls
- on vg_threadmodel.c to model the state of the the client's threads.
- The intent is to 1) look for problem's in the client's use of the
- pthread API, and 2) tell tools which care about thread events (eg,
- helgrind).
-
- This file is intended to be implementation-independent. It assumes
- that the client is using the same pthread.h as the one we include
- here, but makes minimal assumptions about the actual structures
- defined and so on (ie, the exact nature of pthread_t).
-
- (For now we assume there's a 1:1 relationship between pthread_t's
- and Valgrind-visible threads; N:M implementations will need further
- work.)
-
- The model is based on the pthread standard rather than any
- particular implementation, in order to encourage portable use of
- libpthread. On the other hand, we will probably need to implement
- particular implementation extensions if they're widely used.
-
- One tricky problem we need to solve is the mapping between
- pthread_t identifiers and internal thread identifiers.
- */
-
-#include "pub_core_basics.h"
-
-#if 0
-
-#define __USE_GNU
-#define __USE_UNIX98
-#include <pthread.h>
-
-static const Bool debug = False;
-
-static Bool check_wrappings(void);
-
-#define ENTER(x) \
- do { \
- if (VG_(clo_trace_pthreads)) \
- VG_(message)(Vg_DebugMsg, ">>> %d entering %s", \
- VG_(get_running_tid)(), #x); \
- } while(0)
-
-static const Char *pp_retval(enum return_type rt, Word retval)
-{
- static Char buf[50];
-
- switch(rt) {
- case RT_RETURN:
- VG_(sprintf)(buf, "return %d 0x%x", retval, retval);
- return buf;
-
- case RT_LONGJMP:
- return "LONGJMPed out";
-
- case RT_EXIT:
- return "thread exit";
- }
- return "??";
-}
-
-#define LEAVE(x, rt, retval) \
- do { \
- if (VG_(clo_trace_pthreads)) \
- VG_(message)(Vg_DebugMsg, "<<< %d leaving %s -> %s", \
- VG_(get_running_tid)(), #x, pp_retval(rt, retval)); \
- } while(0)
-
-struct pthread_map
-{
- pthread_t id;
-
- ThreadId tid;
-};
-
-static Int pthread_cmp(const void *v1, const void *v2)
-{
- const pthread_t *a = (const pthread_t *)v1;
- const pthread_t *b = (const pthread_t *)v2;
-
- return VG_(memcmp)(a, b, sizeof(*a));
-}
-
-static SkipList sk_pthread_map = VG_SKIPLIST_INIT(struct pthread_map, id, pthread_cmp,
- NULL, VG_AR_CORE);
-
-/* Find a ThreadId for a particular pthread_t; block until it becomes available */
-static ThreadId get_pthread_mapping(pthread_t id)
-{
- /* Nasty little spin loop; revise if this turns out to be a
- problem. This should only spin for as long as it takes for the
- child thread to register the pthread_t. */
- for(;;) {
- struct pthread_map *m = VG_(SkipList_Find_Exact)(&sk_pthread_map, &id);
-
- if (m && m->tid != VG_INVALID_THREADID)
- return m->tid;
-
- //VG_(printf)("find %x -> %p\n", id, m);
- VG_(vg_yield)();
- }
-}
-
-/* Create a mapping between a ThreadId and a pthread_t */
-static void pthread_id_mapping(ThreadId tid, Addr idp, UInt idsz)
-{
- pthread_t id = *(pthread_t *)idp;
- struct pthread_map *m = VG_(SkipList_Find_Exact)(&sk_pthread_map, &id);
-
- if (debug)
- VG_(printf)("Thread %d maps to %p\n", tid, id);
-
- if (m == NULL) {
- m = VG_(SkipNode_Alloc)(&sk_pthread_map);
- m->id = id;
- m->tid = tid;
- VG_(SkipList_Insert)(&sk_pthread_map, m);
- } else {
- if (m->tid != VG_INVALID_THREADID && m->tid != tid)
- VG_(message)(Vg_UserMsg, "Thread %d is creating duplicate mapping for pthread identifier %x; previously mapped to %d\n",
- tid, (UInt)id, m->tid);
- m->tid = tid;
- }
-}
-
-static void check_thread_exists(ThreadId tid)
-{
- if (!VG_(tm_thread_exists)(tid)) {
- if (debug)
- VG_(printf)("creating thread %d\n", tid);
- VG_(tm_thread_create)(VG_INVALID_THREADID, tid, False);
- }
-}
-
-static Addr startfunc_wrapper = 0;
-
-void VG_(pthread_startfunc_wrapper)(Addr wrapper)
-{
- startfunc_wrapper = wrapper;
-}
-
-struct pthread_create_nonce {
- Bool detached;
- pthread_t *threadid;
-};
-
-static void *before_pthread_create(va_list va)
-{
- pthread_t *threadp = va_arg(va, pthread_t *);
- const pthread_attr_t *attr = va_arg(va, const pthread_attr_t *);
- void *(*start)(void *) = va_arg(va, void *(*)(void *));
- void *arg = va_arg(va, void *);
- struct pthread_create_nonce *n;
- struct vg_pthread_newthread_data *data;
- ThreadState *tst;
-
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_create);
-
- /* Data is in the client heap and is freed by the client in the
- startfunc_wrapper. */
- vg_assert(startfunc_wrapper != 0);
-
- tst = VG_(get_ThreadState)(VG_(get_running_tid)());
-
- // XXX: why use TL_(malloc)() here? What characteristics does this
- // allocation require?
- // [Possible: When using a tool that replaces malloc(), we want to call
- // the replacement version. Otherwise, we want to use VG_(cli_malloc)().
- // So we go via the default version of TL_(malloc)() in vg_default?]
- tl_assert2(0, "read the comment in the code about this...");
-
- // XXX: These three lines are going to have to change. They relied on
- // TL_(malloc) being a weak symbol, and it just doesn't fit with the
- // VG_(tdict) approach that we've switched to. The right way to do this
- // will be to provide a function in the core that checks if
- // VG_(tdict).malloc_malloc has been set; if so, it should
- // call it, if not, it should call VG_(cli_malloc)().
-// VG_(tl_malloc_called_deliberately) = True;
-// data = TL_(malloc)(sizeof(*data));
-// VG_(tl_malloc_called_deliberately) = False;
-
- VG_TRACK(pre_mem_write, Vg_CorePThread, tst->tid, "new thread data",
- (Addr)data, sizeof(*data));
- data->startfunc = start;
- data->arg = arg;
- VG_TRACK(post_mem_write, (Addr)data, sizeof(*data));
-
- /* Substitute arguments
- XXX hack: need an API to do this. */
- ((Word *)tst->arch.m_esp)[3] = startfunc_wrapper;
- ((Word *)tst->arch.m_esp)[4] = (Word)data;
-
- if (debug)
- VG_(printf)("starting thread at wrapper %p\n", startfunc_wrapper);
-
- n = VG_(arena_malloc)(VG_AR_CORE, sizeof(*n));
- n->detached = attr && !!attr->__detachstate;
- n->threadid = threadp;
-
- return n;
-}
-
-static void after_pthread_create(void *nonce, enum return_type rt, Word retval)
-{
- struct pthread_create_nonce *n = (struct pthread_create_nonce *)nonce;
- ThreadId tid = VG_(get_running_tid)();
-
- if (n == NULL)
- return;
-
- if (rt == RT_RETURN && retval == 0) {
- if (!VG_(tm_thread_exists)(tid))
- VG_(tm_thread_create)(tid, get_pthread_mapping(*n->threadid),
- n->detached);
- else {
- if (n->detached)
- VG_(tm_thread_detach)(tid);
- /* XXX set creator tid as well? */
- }
- }
-
- VG_(arena_free)(VG_AR_CORE, n);
-
- LEAVE(pthread_create, rt, retval);
-}
-
-static void *before_pthread_join(va_list va)
-{
- pthread_t pt_joinee = va_arg(va, pthread_t);
- ThreadId joinee;
-
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_join);
-
- joinee = get_pthread_mapping(pt_joinee);
-
- VG_(tm_thread_join)(VG_(get_running_tid)(), joinee);
-
- return NULL;
-}
-
-static void after_pthread_join(void *v, enum return_type rt, Word retval)
-{
- /* nothing to be done? */
- if (!check_wrappings())
- return;
-
- LEAVE(pthread_join, rt, retval);
-}
-
-struct pthread_detach_data {
- pthread_t id;
-};
-
-static void *before_pthread_detach(va_list va)
-{
- pthread_t id = va_arg(va, pthread_t);
- struct pthread_detach_data *data;
-
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_detach);
-
- data = VG_(arena_malloc)(VG_AR_CORE, sizeof(*data));
- data->id = id;
-
- return data;
-}
-
-static void after_pthread_detach(void *nonce, enum return_type rt, Word retval)
-{
- struct pthread_detach_data *data = (struct pthread_detach_data *)nonce;
- ThreadId tid;
-
- if (data == NULL)
- return;
-
- tid = get_pthread_mapping(data->id);
-
- VG_(arena_free)(VG_AR_CORE, data);
-
- if (rt == RT_RETURN && retval == 0)
- VG_(tm_thread_detach)(tid);
-
- LEAVE(pthread_detach, rt, retval);
-}
-
-
-
-static void *before_pthread_self(va_list va)
-{
- /* If pthread_t is a structure, then this might be passed a pointer
- to the return value. On Linux/glibc, it's a simple scalar, so it is
- returned normally. */
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_self);
-
- check_thread_exists(VG_(get_running_tid)());
- return NULL;
-}
-
-static void after_pthread_self(void *nonce, enum return_type rt, Word retval)
-{
- pthread_t ret = (pthread_t)retval;
-
- if (!check_wrappings())
- return;
-
- pthread_id_mapping(VG_(get_running_tid)(), (Addr)&ret, sizeof(ret));
-
- LEAVE(pthread_self, rt, retval);
-}
-
-
-/* If a mutex hasn't been initialized, check it against all the static
- initializers to see if it appears to have been statically
- initialized. */
-static void check_mutex_init(ThreadId tid, pthread_mutex_t *mx)
-{
- static const pthread_mutex_t initializers[] = {
- PTHREAD_MUTEX_INITIALIZER,
- PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP,
- PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP,
- PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP,
- };
- Int i;
-
- if (VG_(tm_mutex_exists)((Addr)mx))
- return;
-
- VG_TRACK(pre_mem_read, Vg_CorePThread, tid, "pthread_mutex_t", (Addr)mx, sizeof(*mx));
-
- for(i = 0; i < sizeof(initializers)/sizeof(*initializers); i++)
- if (VG_(memcmp)(&initializers[i], mx, sizeof(*mx)) == 0) {
- VG_(tm_mutex_init)(tid, (Addr)mx);
- break;
- }
-}
-
-static void *before_pthread_mutex_init(va_list va)
-{
- pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
- const pthread_mutexattr_t *attr = va_arg(va, const pthread_mutexattr_t *);
-
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_mutex_init);
-
- /* XXX look for recursive mutex */
- /* XXX look for non-process scope */
- (void)attr;
-
- return mx;
-}
-
-static void after_pthread_mutex_init(void *nonce, enum return_type rt, Word retval)
-{
- if (!check_wrappings())
- return;
-
- if (rt == RT_RETURN && retval == 0)
- VG_(tm_mutex_init)(VG_(get_running_tid)(), (Addr)nonce);
-
- LEAVE(pthread_mutex_init, rt, retval);
-}
-
-static void *before_pthread_mutex_destroy(va_list va)
-{
- pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
-
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_mutex_destroy);
-
- VG_(tm_mutex_destroy)(VG_(get_running_tid)(), (Addr)mx);
-
- return NULL;
-}
-
-static void after_pthread_mutex_destroy(void *nonce, enum return_type rt, Word retval)
-{
- if (!check_wrappings())
- return;
-
- LEAVE(pthread_mutex_destroy, rt, retval);
-}
-
-static void *before_pthread_mutex_lock(va_list va)
-{
- pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
-
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_mutex_lock);
-
- if (debug)
- VG_(printf)("%d locking %p\n", VG_(get_running_tid)(), mx);
- check_thread_exists(VG_(get_running_tid)());
- check_mutex_init(VG_(get_running_tid)(), mx); /* mutex might be statically initialized */
- VG_(tm_mutex_trylock)(VG_(get_running_tid)(), (Addr)mx);
-
- return mx;
-}
-
-static void after_pthread_mutex_lock(void *nonce, enum return_type rt, Word retval)
-{
- if (!check_wrappings())
- return;
-
- if (rt == RT_RETURN && retval == 0)
- VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce);
- else {
- if (debug)
- VG_(printf)("after mutex_lock failed: rt=%d ret=%d\n", rt, retval);
- VG_(tm_mutex_giveup)(VG_(get_running_tid)(), (Addr)nonce);
- }
-
- LEAVE(pthread_mutex_lock, rt, retval);
-}
-
-static void *before_pthread_mutex_trylock(va_list va)
-{
- pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
-
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_mutex_trylock);
-
- if (debug)
- VG_(printf)("%d trylocking %p\n", VG_(get_running_tid)(), mx);
- check_thread_exists(VG_(get_running_tid)());
- check_mutex_init(VG_(get_running_tid)(), mx); /* mutex might be statically initialized */
- VG_(tm_mutex_trylock)(VG_(get_running_tid)(), (Addr)mx);
-
- return mx;
-}
-
-static void after_pthread_mutex_trylock(void *nonce, enum return_type rt, Word retval)
-{
- if (nonce == NULL)
- return;
-
- if (rt == RT_RETURN && retval == 0)
- VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce);
- else {
- if (debug)
- VG_(printf)("after mutex_trylock failed: rt=%d ret=%d\n", rt, retval);
- VG_(tm_mutex_giveup)(VG_(get_running_tid)(), (Addr)nonce);
- }
-
- LEAVE(pthread_mutex_trylock, rt, retval);
-}
-
-static void *before_pthread_mutex_unlock(va_list va)
-{
- pthread_mutex_t *mx = va_arg(va, pthread_mutex_t *);
-
- if (!check_wrappings())
- return NULL;
-
- ENTER(pthread_mutex_unlock);
-
- VG_(tm_mutex_tryunlock)(VG_(get_running_tid)(), (Addr)mx);
-
- return mx;
-}
-
-static void after_pthread_mutex_unlock(void *nonce, enum return_type rt, Word retval)
-{
- if (nonce == NULL)
- return;
-
- if (rt == RT_RETURN && retval == 0)
- VG_(tm_mutex_unlock)(VG_(get_running_tid)(), (Addr)nonce); /* complete unlock */
- else
- VG_(tm_mutex_acquire)(VG_(get_running_tid)(), (Addr)nonce); /* re-acquire */
-
- LEAVE(pthread_mutex_unlock, rt, retval);
-}
-
-
-static struct pt_wraps {
- const Char *name;
- FuncWrapper wrapper;
- const CodeRedirect *redir;
-} wraps[] = {
-#define WRAP(func, extra) { #func extra, { before_##func, after_##func } }
- WRAP(pthread_create, "@@GLIBC_2.1"), /* XXX TODO: 2.0 ABI (?) */
- WRAP(pthread_join, ""),
- WRAP(pthread_detach, ""),
-
- WRAP(pthread_self, ""),
-
- WRAP(pthread_mutex_init, ""),
- WRAP(pthread_mutex_destroy, ""),
- WRAP(pthread_mutex_lock, ""),
- WRAP(pthread_mutex_trylock, ""),
- WRAP(pthread_mutex_unlock, ""),
-#undef WRAP
-};
-
-/* Check to see if all the wrappers are resolved */
-static Bool check_wrappings()
-{
- Int i;
- static Bool ok = True;
- static Bool checked = False;
-
- if (checked)
- return ok;
-
- for(i = 0; i < sizeof(wraps)/sizeof(*wraps); i++) {
- if (!VG_(is_resolved)(wraps[i].redir)) {
- VG_(message)(Vg_DebugMsg, "Pthread wrapper for \"%s\" is not resolved",
- wraps[i].name);
- ok = False;
- }
- }
-
- if (startfunc_wrapper == 0) {
- VG_(message)(Vg_DebugMsg, "Pthread wrapper for thread start function is not resolved");
- ok = False;
- }
-
- if (!ok)
- VG_(message)(Vg_DebugMsg, "Missing intercepts; model disabled");
-
- checked = True;
- return ok;
-}
-
-/*
- Set up all the wrappers for interesting functions.
- */
-void VG_(pthread_init)()
-{
- Int i;
-
- for(i = 0; i < sizeof(wraps)/sizeof(*wraps); i++) {
- //VG_(printf)("adding pthread wrapper for %s\n", wraps[i].name);
- wraps[i].redir = VG_(add_wrapper)("soname:libpthread.so.0",
- wraps[i].name, &wraps[i].wrapper);
- }
- VG_(tm_init)();
- VG_(tm_thread_create)(VG_INVALID_THREADID, VG_(master_tid), True);
-}
-
-#else /* !0 */
-/* Stubs for now */
-//:: void VG_(pthread_init)()
-//:: {
-//:: }
-//::
-//:: void VG_(pthread_startfunc_wrapper)(Addr wrapper)
-//:: {
-//:: }
-#endif /* 0 */
-
-/*--------------------------------------------------------------------*/
-/*--- end ---*/
-/*--------------------------------------------------------------------*/
+++ /dev/null
-
-/*--------------------------------------------------------------------*/
-/*--- Thread modelling. m_threadmodel.c ---*/
-/*--------------------------------------------------------------------*/
-
-/*
- This file is part of Valgrind, a dynamic binary instrumentation
- framework.
-
- Copyright (C) 2005-2006 Jeremy Fitzhardinge
- jeremy@goop.org
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
- 02111-1307, USA.
-
- The GNU General Public License is contained in the file COPYING.
-*/
-
-/*
- This file implements an abstract thread model, by which a client's
- thread usage is validated. The model the file implements is
- intended to be enough to contain pthreads, or perhaps a superset,
- but it is not pthread-specific; that's done in in vg_pthreadmodel.c
-
- While the primary client of this file is vg_pthreadmodel.c, it is
- also intended that clients can make direct use of this file for
- home-grown threading libraries. It is therefore useful for both
- validating the library itself as well as the users of the library.
-
- A note on terminology:
-
- The states referred to in this file ("blocked state", "zombie
- state") are specific to this threads model, and have nothing do to
- with the scheduler status for a thread. For example, a thread
- could be "blocked" in a lock but be in VgTs_Runnable status,
- because the lock is actually a spinlock.
-
- "Fails" means "reports an error" and possibly means that the model
- is getting out of sync with the actual implementation. This model
- only reports problems to the user, and doesn't attempt to actually
- change the behaviour of the implementation.
-
- NB:
-
- This file assumes there's a 1:1 relationship between application
- threads and Valgrind threads, which means that 1:N and M:N thread
- models are not (yet) supported. At some point we may need to
- introduce a separate notion of a "thread" for modelling purposes.
- */
-
-#include "pub_core_basics.h"
-#include "pub_core_vki.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 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, 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, 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, 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, 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, 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 ---*/
-/*--------------------------------------------------------------------*/