Redo threading model so that we no longer emulate pthreads. Threads are done at the kernel level with clone etc. Vastly simplifies things. dev/null | 5183 ---------------------------- Makefile.am | 2 addrcheck/ac_main.c | 8 cachegrind/x86/cg_arch.c | 39 configure.in | 44 corecheck/tests/pth_rwlock.stderr.exp | 9 corecheck/tests/sigkill.c | 4 corecheck/tests/sigkill.stderr.exp | 12 coregrind/Makefile.am | 21 coregrind/core.h | 455 -- coregrind/core_asm.h | 2 coregrind/linux/Makefile.am | 2 coregrind/linux/core_os.c | 195 + coregrind/linux/core_os.h | 88 coregrind/linux/sema.c | 105 coregrind/linux/syscalls.c | 24 coregrind/vg_execontext.c | 5 coregrind/vg_intercept.c.base | 36 coregrind/vg_main.c | 134 coregrind/vg_mylibc.c | 45 coregrind/vg_scheduler.c | 3222 ++--------------- coregrind/vg_signals.c | 892 +--- coregrind/vg_symtab2.c | 21 coregrind/vg_syscalls.c | 649 +-- coregrind/vg_to_ucode.c | 4 coregrind/x86-linux/core_platform.h | 76 coregrind/x86-linux/ldt.c | 2 coregrind/x86-linux/platform-private.h | 10 coregrind/x86-linux/syscall.S | 130 coregrind/x86-linux/syscalls.c | 471 +- coregrind/x86/core_arch.h | 47 coregrind/x86/dispatch.S | 4 coregrind/x86/signal.c | 9 coregrind/x86/state.c | 15 helgrind/hg_main.c | 22 include/linux/vki.h | 11 include/tool.h.base | 21 include/x86-linux/vki_arch.h | 9 memcheck/mac_leakcheck.c | 3 memcheck/mc_main.c | 26 memcheck/tests/scalar_exit_group.stderr.exp | 2 none/tests/Makefile.am | 4 none/tests/pending.c | 84 none/tests/pending.stderr.exp | 2 none/tests/pending.stdout.exp | 7 none/tests/syscall-restart1.c | 2 tests/vg_regtest.in | 2 47 files changed, 2499 insertions(+), 9661 deletions(-) diff -puN Makefile.am~new-threading Makefile.am --- valgrind/Makefile.am~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/Makefile.am 2005-01-08 17:18:28.000000000 -0800 @@ -46,8 +46,6 @@ EXTRA_DIST = \ install-exec-hook: $(mkinstalldirs) $(DESTDIR)$(valdir) - rm -f $(DESTDIR)$(valdir)/libpthread.so.0 - $(LN_S) libpthread.so $(DESTDIR)$(valdir)/libpthread.so.0 all-local: mkdir -p $(inplacedir) diff -puN addrcheck/ac_main.c~new-threading addrcheck/ac_main.c --- valgrind/addrcheck/ac_main.c~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/addrcheck/ac_main.c 2005-01-08 17:18:28.000000000 -0800 @@ -783,7 +783,7 @@ static void ac_ACCESS4_SLOWLY ( Addr a, if (!MAC_(clo_partial_loads_ok) || ((a & 3) != 0) || (!a0ok && !a1ok && !a2ok && !a3ok)) { - MAC_(record_address_error)( VG_(get_current_tid)(), a, 4, isWrite ); + MAC_(record_address_error)( VG_(get_VCPU_tid)(), a, 4, isWrite ); return; } @@ -810,7 +810,7 @@ static void ac_ACCESS2_SLOWLY ( Addr a, /* If an address error has happened, report it. */ if (aerr) { - MAC_(record_address_error)( VG_(get_current_tid)(), a, 2, isWrite ); + MAC_(record_address_error)( VG_(get_VCPU_tid)(), a, 2, isWrite ); } } @@ -824,7 +824,7 @@ static void ac_ACCESS1_SLOWLY ( Addr a, /* If an address error has happened, report it. */ if (aerr) { - MAC_(record_address_error)( VG_(get_current_tid)(), a, 1, isWrite ); + MAC_(record_address_error)( VG_(get_VCPU_tid)(), a, 1, isWrite ); } } @@ -940,7 +940,7 @@ void ac_fpu_ACCESS_check_SLOWLY ( Addr a } if (aerr) { - MAC_(record_address_error)( VG_(get_current_tid)(), addr, size, isWrite ); + MAC_(record_address_error)( VG_(get_VCPU_tid)(), addr, size, isWrite ); } } diff -puN cachegrind/x86/cg_arch.c~new-threading cachegrind/x86/cg_arch.c --- valgrind/cachegrind/x86/cg_arch.c~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/cachegrind/x86/cg_arch.c 2005-01-08 17:18:28.000000000 -0800 @@ -245,50 +245,19 @@ Int AMD_cache_info(cache_t* I1c, cache_t return 0; } -static jmp_buf cpuid_jmpbuf; - -static -void cpuid_SIGILL_handler(int signum) -{ - __builtin_longjmp(cpuid_jmpbuf, 1); -} - static Int get_caches_from_CPUID(cache_t* I1c, cache_t* D1c, cache_t* L2c) { Int level, res, ret; Char vendor_id[13]; - struct vki_sigaction sigill_new, sigill_saved; - - /* Install own SIGILL handler */ - sigill_new.ksa_handler = cpuid_SIGILL_handler; - sigill_new.sa_flags = 0; - sigill_new.sa_restorer = NULL; - res = VG_(sigemptyset)( &sigill_new.sa_mask ); - sk_assert(res == 0); - - res = VG_(sigaction)( VKI_SIGILL, &sigill_new, &sigill_saved ); - sk_assert(res == 0); - - /* Trap for illegal instruction, in case it's a really old processor that - * doesn't support CPUID. */ - if (__builtin_setjmp(cpuid_jmpbuf) == 0) { - VG_(cpuid)(0, &level, (int*)&vendor_id[0], - (int*)&vendor_id[8], (int*)&vendor_id[4]); - vendor_id[12] = '\0'; - - /* Restore old SIGILL handler */ - res = VG_(sigaction)( VKI_SIGILL, &sigill_saved, NULL ); - sk_assert(res == 0); - } else { + if (!VG_(has_cpuid)()) { VG_(message)(Vg_DebugMsg, "CPUID instruction not supported"); - - /* Restore old SIGILL handler */ - res = VG_(sigaction)( VKI_SIGILL, &sigill_saved, NULL ); - sk_assert(res == 0); return -1; } + VG_(cpuid)(0, &level, (int*)&vendor_id[0], + (int*)&vendor_id[8], (int*)&vendor_id[4]); + vendor_id[12] = '\0'; if (0 == level) { VG_(message)(Vg_DebugMsg, "CPUID level is 0, early Pentium?\n"); diff -puN configure.in~new-threading configure.in --- valgrind/configure.in~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/configure.in 2005-01-08 17:18:28.000000000 -0800 @@ -78,8 +78,8 @@ case "${host_cpu}" in AC_MSG_RESULT([ok (${host_cpu})]) VG_ARCH="x86" KICKSTART_BASE="0xb0000000" - ARCH_CORE_AM_CFLAGS="-fomit-frame-pointer @PREFERRED_STACK_BOUNDARY@ -DELFSZ=32" - ARCH_TOOL_AM_CFLAGS="-fomit-frame-pointer @PREFERRED_STACK_BOUNDARY@" + ARCH_CORE_AM_CFLAGS="@PREFERRED_STACK_BOUNDARY@ -DELFSZ=32" + ARCH_TOOL_AM_CFLAGS="@PREFERRED_STACK_BOUNDARY@" ARCH_CORE_AM_CCASFLAGS="" ;; @@ -220,21 +220,6 @@ case "${glibc}" in ;; esac -# APIs introduced in recent glibc versions - -AC_MSG_CHECKING([whether sched_param has a sched_priority member]) -AC_CACHE_VAL(vg_have_sched_priority, -[ -AC_TRY_COMPILE([#include ],[ -struct sched_param p; p.sched_priority=1;], -vg_have_sched_priority=yes, -vg_have_sched_priority=no) -]) -AC_MSG_RESULT($vg_have_sched_priority) -if test "$vg_have_sched_priority" = yes; then -AC_DEFINE([HAVE_SCHED_PRIORITY], 1, [pthread / sched_priority exists]) -fi - # We don't know how to detect the X client library version # (detecting the server version is easy, bu no help). So we # just use a hack: always include the suppressions for both @@ -267,18 +252,22 @@ CFLAGS=$safe_CFLAGS AC_SUBST(PREFERRED_STACK_BOUNDARY) + # Check for PIE support in the compiler and linker AC_CACHE_CHECK([for PIE support], vg_cv_pie, - [safe_CFLAGS=$CFLAGS - CFLAGS="$CFLAGS -fpie" - safe_LDFLAGS=$LDFLAGS - LDFLAGS="$LDFLAGS -pie" - AC_TRY_LINK([int foo;], - [], - [vg_cv_pie=yes], - [vg_cv_pie=no]) - CFLAGS=$safe_CFLAGS - LDFLAGS=$safe_LDFLAGS]) + [AC_ARG_WITH(pie, AS_HELP_STRING([--with-pie], + [force (or --without to prevent) the use of PIE linking]), + [vg_cv_pie=$withval], + [safe_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -fpie" + safe_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -pie" + AC_TRY_LINK([int foo;], + [], + [vg_cv_pie=yes], + [vg_cv_pie=no]) + CFLAGS=$safe_CFLAGS + LDFLAGS=$safe_LDFLAGS])]) if test "$vg_cv_pie" = yes; then AC_DEFINE([HAVE_PIE], 1, [can create position-independent executables]) fi @@ -294,7 +283,6 @@ AC_TYPE_UID_T AC_TYPE_OFF_T AC_TYPE_SIZE_T AC_HEADER_TIME -AC_CHECK_TYPES(__pthread_unwind_buf_t,,,[#include ]) # Checks for library functions. AC_FUNC_MEMCMP diff -puN corecheck/tests/pth_rwlock.stderr.exp~new-threading corecheck/tests/pth_rwlock.stderr.exp --- valgrind/corecheck/tests/pth_rwlock.stderr.exp~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/corecheck/tests/pth_rwlock.stderr.exp 2005-01-08 17:18:28.000000000 -0800 @@ -1,12 +1,3 @@ -warning: Valgrind's pthread_cond_destroy is incomplete - (it doesn't check if the cond is waited on) - your program may misbehave as a result -warning: Valgrind's pthread_cond_destroy is incomplete - (it doesn't check if the cond is waited on) - your program may misbehave as a result -warning: Valgrind's pthread_cond_destroy is incomplete - (it doesn't check if the cond is waited on) - your program may misbehave as a result ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) diff -puN corecheck/tests/sigkill.c~new-threading corecheck/tests/sigkill.c --- valgrind/corecheck/tests/sigkill.c~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/corecheck/tests/sigkill.c 2005-01-08 17:18:28.000000000 -0800 @@ -19,10 +19,10 @@ main (void) int i; int rc; for (i = 1; i <= 65; i++) { - // skip signals 32 and 33: some systems say "warning, ignored attempt + // skip signals 63 and 64: some systems say "warning, ignored attempt // to catch 32 because it's used internally by Valgrind", others say // "invalid argument". - if (i == 32 || i == 33) { + if (i == 63 || i == 64) { continue; } // different systems sa.sa_flags = 0; diff -puN corecheck/tests/sigkill.stderr.exp~new-threading corecheck/tests/sigkill.stderr.exp --- valgrind/corecheck/tests/sigkill.stderr.exp~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/corecheck/tests/sigkill.stderr.exp 2005-01-08 17:18:28.000000000 -0800 @@ -96,6 +96,12 @@ getting signal 30: Success setting signal 31: Success getting signal 31: Success +setting signal 32: Invalid argument +getting signal 32: Invalid argument + +setting signal 33: Success +getting signal 33: Success + setting signal 34: Success getting signal 34: Success @@ -183,12 +189,6 @@ getting signal 61: Success setting signal 62: Success getting signal 62: Success -setting signal 63: Success -getting signal 63: Success - -setting signal 64: Success -getting signal 64: Success - setting signal 65: Warning: bad signal number 65 in sigaction() Invalid argument getting signal 65: Warning: bad signal number 65 in sigaction() diff -puN coregrind/Makefile.am~new-threading coregrind/Makefile.am --- valgrind/coregrind/Makefile.am~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/coregrind/Makefile.am 2005-01-08 17:18:28.000000000 -0800 @@ -15,7 +15,6 @@ bin_PROGRAMS = \ val_PROGRAMS = \ stage2 \ - libpthread.so \ vg_inject.so noinst_LIBRARIES = lib_replace_malloc.a @@ -29,7 +28,7 @@ noinst_HEADERS = \ vg_toolint.h EXTRA_DIST = \ - vg_libpthread.vs valgrind.vs \ + valgrind.vs \ gen_toolint.pl toolfuncs.def \ gen_intercepts.pl vg_replace_malloc.c.base vg_intercept.c.base @@ -63,7 +62,6 @@ stage2_SOURCES = \ vg_mylibc.c \ vg_needs.c \ vg_procselfmaps.c \ - vg_proxylwp.c \ vg_dummy_profile.c \ vg_signals.c \ vg_symtab2.c \ @@ -128,19 +126,6 @@ vg_toolint.h: $(srcdir)/gen_toolint.pl $(PERL) $(srcdir)/gen_toolint.pl proto < $(srcdir)/toolfuncs.def > $@ || rm -f $@ $(PERL) $(srcdir)/gen_toolint.pl struct < $(srcdir)/toolfuncs.def >> $@ || rm -f $@ -libpthread_so_SOURCES = \ - vg_libpthread.c \ - vg_libpthread_unimp.c \ - ${VG_ARCH}/libpthread.c \ - ${VG_PLATFORM}/syscall.S -libpthread_so_DEPENDENCIES = $(srcdir)/vg_libpthread.vs -libpthread_so_CFLAGS = $(AM_CFLAGS) -fpic -fno-omit-frame-pointer -libpthread_so_LDFLAGS = -Werror -fno-omit-frame-pointer -UVG_LIBDIR \ - -shared -ldl \ - -Wl,-version-script $(srcdir)/vg_libpthread.vs \ - -Wl,-z,nodelete \ - -Wl,--soname=libpthread.so.0 - vg_inject_so_SOURCES = \ vg_intercept.c vg_inject_so_CFLAGS = $(AM_CFLAGS) -fpic @@ -152,12 +137,12 @@ vg_inject_so_LDFLAGS = \ lib_replace_malloc_a_SOURCES = vg_replace_malloc.c lib_replace_malloc_a_CFLAGS = $(AM_CFLAGS) -fpic -fno-omit-frame-pointer -MANUAL_DEPS = $(noinst_HEADERS) $(include_HEADERS) $(inplacedir)/libpthread.so.0 +MANUAL_DEPS = $(noinst_HEADERS) $(include_HEADERS) all-local: mkdir -p $(inplacedir) for i in $(val_PROGRAMS); do \ - to=$(inplacedir)/$$(echo $$i | sed 's,libpthread.so,libpthread.so.0,'); \ + to=$(inplacedir)/$$i; \ rm -f $$$to; \ ln -sf ../$(subdir)/$$i $$to; \ done diff -puN coregrind/core.h~new-threading coregrind/core.h --- valgrind/coregrind/core.h~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/coregrind/core.h 2005-01-11 10:13:24.000000000 -0800 @@ -90,9 +90,9 @@ // Ugly: this is needed by linux/core_os.h typedef struct _ThreadState ThreadState; -#include "core_os.h" // OS-specific stuff, eg. linux/core_os.h #include "core_platform.h" // platform-specific stuff, // eg. x86-linux/core_platform.h +#include "core_os.h" // OS-specific stuff, eg. linux/core_os.h #include "valgrind.h" @@ -155,25 +155,12 @@ typedef struct _ThreadState ThreadState; #define VG_SCHEDULING_QUANTUM 50000 /* Number of file descriptors that Valgrind tries to reserve for - it's own use - two per thread plues a small number of extras. */ -#define VG_N_RESERVED_FDS (VG_N_THREADS*2 + 4) - -/* Stack size for a thread. We try and check that they do not go - beyond it. */ -#define VG_PTHREAD_STACK_SIZE (1 << 20) - -/* Number of entries in each thread's cleanup stack. */ -#define VG_N_CLEANUPSTACK 16 - -/* Number of entries in each thread's fork-handler stack. */ -#define VG_N_FORKHANDLERSTACK 4 + it's own use - just a small constant. */ +#define VG_N_RESERVED_FDS (10) /* Max number of callers for context in a suppression. */ #define VG_N_SUPP_CALLERS 4 -/* Numer of entries in each thread's signal queue. */ -#define VG_N_SIGNALQUEUE 8 - /* Useful macros */ /* a - alignment - must be a power of 2 */ #define ROUNDDN(p, a) ((Addr)(p) & ~((a)-1)) @@ -229,12 +216,6 @@ typedef VgLogTo_Socket } VgLogTo; -/* pid of main process */ -extern Int VG_(main_pid); - -/* pgrp of process (global to all threads) */ -extern Int VG_(main_pgrp); - /* Application-visible file descriptor limits */ extern Int VG_(fd_soft_limit); extern Int VG_(fd_hard_limit); @@ -299,9 +280,6 @@ extern Bool VG_(clo_trace_signals); extern Bool VG_(clo_trace_symtab); /* DEBUG: print thread scheduling events? default: NO */ extern Bool VG_(clo_trace_sched); -/* DEBUG: print pthread (mutex etc) events? default: 0 (none), 1 - (some), 2 (all) */ -extern Int VG_(clo_trace_pthread_level); /* Display gory details for the k'th most popular error. default: Infinity. */ extern Int VG_(clo_dump_error); @@ -309,9 +287,6 @@ extern Int VG_(clo_dump_error); extern Int VG_(clo_backtrace_size); /* Engage miscellaneous weird hacks needed for some progs. */ extern Char* VG_(clo_weird_hacks); -/* How often we should poll for signals, assuming we need to poll for - signals. */ -extern Int VG_(clo_signal_polltime); /* Low latency syscalls and signals */ extern Bool VG_(clo_lowlat_syscalls); @@ -337,7 +312,13 @@ extern Bool VG_(clo_show_below_main); extern Bool VG_(clo_pointercheck); /* Set up the libc freeres wrapper */ -extern void VG_(intercept_libc_freeres_wrapper)(Addr); +extern void VGA_(intercept_libc_freeres_wrapper)(Addr); + +// Clean up the client by calling before the final reports +extern void VGA_(final_tidyup)(ThreadId tid); + +// Arch-specific client requests +extern Bool VGA_(client_requests)(ThreadId tid, UWord *args); /* --------------------------------------------------------------------- Profiling stuff @@ -650,66 +631,35 @@ typedef struct Exports of vg_scheduler.c ------------------------------------------------------------------ */ +/* + Thread state machine: + + Empty -> Init -> Runnable <=> WaitSys/Yielding + ^ | + \-----------------/ + */ typedef enum ThreadStatus { VgTs_Empty, /* this slot is not in use */ - VgTs_Runnable, /* waiting to be scheduled */ - VgTs_WaitJoiner, /* waiting for someone to do join on me */ - VgTs_WaitJoinee, /* waiting for the thread I did join on */ - VgTs_WaitMX, /* waiting on a mutex */ - VgTs_WaitCV, /* waiting on a condition variable */ + VgTs_Init, /* just allocated */ + VgTs_Runnable, /* ready to run */ VgTs_WaitSys, /* waiting for a syscall to complete */ - VgTs_Sleeping, /* sleeping for a while */ + VgTs_Yielding, /* temporarily yielding the CPU */ } ThreadStatus; +/* Return codes from the scheduler. */ typedef - enum CleanupType { - VgCt_None, /* this cleanup entry is not initialised */ - VgCt_Function, /* an old-style function pointer cleanup */ - VgCt_Longjmp /* a new-style longjmp based cleanup */ - } - CleanupType; - -/* Information on a thread's stack. */ -typedef - struct { - Addr base; - UInt size; - UInt guardsize; - } - StackInfo; - -/* An entry in a threads's cleanup stack. */ -typedef - struct { - CleanupType type; - union { - struct { - void (*fn)(void*); - void* arg; - } function; - struct { - void *ub; - int ctype; - } longjmp; - } data; - } - CleanupEntry; - -/* An entry in a thread's fork-handler stack. */ -typedef - struct { - void (*prepare)(void); - void (*parent)(void); - void (*child)(void); + enum { + VgSrc_None, /* not exiting yet */ + VgSrc_ExitSyscall, /* client called exit(). This is the normal + route out. */ + VgSrc_FatalSig /* Killed by the default action of a fatal + signal */ } - ForkHandlerEntry; - -typedef struct ProxyLWP ProxyLWP; + VgSchedReturnCode; -//typedef - struct _ThreadState { +struct _ThreadState { /* ThreadId == 0 (and hence vg_threads[0]) is NEVER USED. The thread identity is simply the index in vg_threads[]. ThreadId == 1 is the root thread and has the special property @@ -719,107 +669,22 @@ typedef struct ProxyLWP ProxyLWP; ALWAYS == the index in vg_threads[]. */ ThreadId tid; - /* Current scheduling status. - - Complications: whenever this is set to VgTs_WaitMX, you - should also set .m_edx to whatever the required return value - is for pthread_mutex_lock / pthread_cond_timedwait for when - the mutex finally gets unblocked. */ + /* Current scheduling status. */ ThreadStatus status; - /* When .status == WaitMX, points to the mutex I am waiting for. - When .status == WaitCV, points to the mutex associated with - the condition variable indicated by the .associated_cv field. - In all other cases, should be NULL. */ - vg_pthread_mutex_t* associated_mx; - - /* When .status == WaitCV, points to the condition variable I am - waiting for. In all other cases, should be NULL. */ - void* /*pthread_cond_t* */ associated_cv; - - /* If VgTs_Sleeping, this is when we should wake up, measured in - milliseconds as supplied by VG_(read_millisecond_timer). - - If VgTs_WaitCV, this indicates the time at which - pthread_cond_timedwait should wake up. If == 0xFFFFFFFF, - this means infinitely far in the future, viz, - pthread_cond_wait. */ - UInt awaken_at; - - /* If VgTs_WaitJoiner, return value, as generated by joinees. */ - void* joinee_retval; - - /* If VgTs_WaitJoinee, place to copy the return value to, and - the identity of the thread we're waiting for. */ - void** joiner_thread_return; - ThreadId joiner_jee_tid; - - /* If VgTs_WaitSys, this is the result of the pre-syscall check */ - void *sys_pre_res; - - /* If VgTs_WaitSys, this is the syscall we're currently running */ - Int syscallno; - - /* If VgTs_WaitSys, this is the syscall flags */ - UInt sys_flags; - - /* Details about this thread's proxy LWP */ - ProxyLWP *proxy; - - /* Whether or not detached. */ - Bool detached; - - /* Cancelability state and type. */ - Bool cancel_st; /* False==PTH_CANCEL_DISABLE; True==.._ENABLE */ - Bool cancel_ty; /* False==PTH_CANC_ASYNCH; True==..._DEFERRED */ - - /* Pointer to fn to call to do cancellation. Indicates whether - or not cancellation is pending. If NULL, not pending. Else - should be &thread_exit_wrapper(), indicating that - cancallation is pending. */ - void (*cancel_pend)(void*); - - /* The cleanup stack. */ - Int custack_used; - CleanupEntry custack[VG_N_CLEANUPSTACK]; - - /* A pointer to the thread's-specific-data. This is handled almost - entirely from vg_libpthread.c. We just provide hooks to get and - set this ptr. This is either NULL, indicating the thread has - read/written none of its specifics so far, OR points to a - void*[VG_N_THREAD_KEYS], allocated and deallocated in - vg_libpthread.c. */ - void** specifics_ptr; + /* This is set if the thread is in the process of exiting for any + reason. The precise details of the exit are in the OS-specific + state. */ + VgSchedReturnCode exitreason; /* This thread's blocked-signals mask. Semantics is that for a signal to be delivered to this thread, the signal must not be blocked by this signal mask. If more than one thread accepts a signal, then it will be delivered to one at random. If all threads block the signal, it will remain pending until either a - thread unblocks it or someone uses sigwaitsig/sigtimedwait. - - sig_mask reflects what the client told us its signal mask should - be, but isn't necessarily the current signal mask of the proxy - LWP: it may have more signals blocked because of signal - handling, or it may be different because of sigsuspend. - */ + thread unblocks it or someone uses sigwaitsig/sigtimedwait. */ vki_sigset_t sig_mask; - /* Effective signal mask. This is the mask which currently - applies; it may be different from sig_mask while a signal - handler is running. - */ - vki_sigset_t eff_sig_mask; - - /* Signal queue. This is used when the kernel doesn't route - signals properly in order to remember the signal information - while we are routing the signal. It is a circular queue with - insertions performed at the head and removals at the tail. - */ - vki_siginfo_t sigqueue[VG_N_SIGNALQUEUE]; - Int sigqueue_head; - Int sigqueue_tail; - /* Stacks. When a thread slot is freed, we don't deallocate its stack; we just leave it lying around for the next use of the slot. If the next use of the slot requires a larger stack, @@ -854,28 +719,95 @@ typedef struct ProxyLWP ProxyLWP; /* Architecture-specific thread state */ arch_thread_t arch; + + /* OS-specific thread state */ + os_thread_t os_state; + + /* Per-thread jmp_buf to resume scheduler after a signal */ + Bool sched_jmpbuf_valid; + jmp_buf sched_jmpbuf; + + /* Info about the signal we just got */ + vki_siginfo_t siginfo; }; //ThreadState; +/* When something interesting happens in a signal handler, we need to + longjmp back into the scheduling loop. This enum is the code which + is returned via longjmp/setjmp to describe what happened. */ +typedef enum { + VgSig_None = 0, /* nothing special happened*/ + VgSig_AsyncSig = 1, /* we were signalled externally */ + VgSig_FaultSig, /* an instruction faulted */ + VgSig_FatalSig, /* a fatal signal */ + VgSig_Exiting, /* we're exiting */ +} VgSigCode; + + /* The thread table. */ extern ThreadState VG_(threads)[VG_N_THREADS]; +/* Allocate a new ThreadState */ +extern ThreadId VG_(alloc_ThreadState)(); + +/* A thread exits. tid must currently be running. */ +extern void VG_(exit_thread)(ThreadId tid); + +/* Kill a thread. This interrupts whatever a thread is doing, and + makes it exit ASAP. This does not set the exitreason or + exitcode. */ +extern void VG_(kill_thread)(ThreadId tid); + /* Check that tid is in range and denotes a non-Empty thread. */ extern Bool VG_(is_valid_tid) ( ThreadId tid ); /* Determine if 'tid' is that of the current running thread (Nb: returns False if no thread is currently running. */ -extern Bool VG_(is_running_thread)(ThreadId tid); +extern Bool VG_(is_VCPU_thread)(ThreadId tid); + +/* Returns the ThreadId (if any) who's state is currently in the + BaseBlock. */ +extern ThreadId VG_(get_VCPU_tid) ( void ); -/* Get the ThreadState for a particular thread */ +/* Get the ThreadState for a particular tid */ extern ThreadState *VG_(get_ThreadState)(ThreadId tid); -/* Similarly ... */ -extern ThreadId VG_(get_current_tid) ( void ); +/* Given an LWP id (ie, real kernel thread id), find the corresponding + ThreadId */ +extern ThreadId VG_(get_lwp_tid)(Int lwpid); + +/* Returns true if a thread is currently running (ie, has the CPU lock) */ +extern Bool VG_(is_running_thread)(ThreadId tid); + +/* Returns true if the thread is in the process of exiting */ +extern Bool VG_(is_exiting)(ThreadId tid); + +/* Return the number of non-dead Threads */ +extern Int VG_(count_living_threads)(void); /* Nuke all threads except tid. */ -extern void VG_(nuke_all_threads_except) ( ThreadId me ); +extern void VG_(nuke_all_threads_except) ( ThreadId me, VgSchedReturnCode reason ); + +/* Make a thread the running thread. The thread must previously been + sleeping, and not holding the CPU semaphore. This will set the + thread state to VgTs_Runnable, and the thread will attempt to take + the CPU semaphore. By the time it returns, tid will be the running + thread. */ +extern void VG_(set_running) ( ThreadId tid ); + +/* Set a thread into a sleeping state. Before the call, the thread + must be runnable, and holding the CPU semaphore. When this call + returns, the thread will be set to the specified sleeping state, + and will not be holding the CPU semaphore. Note that another + thread could be running by the time this call returns, so the + caller must be careful not to touch any shared state. It is also + the caller's responsibility to actually block until the thread is + ready to run again. */ +extern void VG_(set_sleeping) ( ThreadId tid, ThreadStatus state ); + +/* Yield the CPU for a while */ +extern void VG_(vg_yield)(void); /* Give a hint to the scheduler that it may be a good time to find a new runnable thread. If prefer_sched != VG_INVALID_THREADID, then @@ -883,32 +815,20 @@ extern void VG_(nuke_all_threads_except) */ extern void VG_(need_resched) ( ThreadId prefer_sched ); -/* Return codes from the scheduler. */ -typedef - enum { - VgSrc_Deadlock, /* no runnable threads and no prospect of any - even if we wait for a long time */ - VgSrc_ExitSyscall, /* client called exit(). This is the normal - route out. */ - VgSrc_FatalSig /* Killed by the default action of a fatal - signal */ - } - VgSchedReturnCode; - +// The scheduler. +extern VgSchedReturnCode VG_(scheduler) ( ThreadId tid ); -// The scheduler. 'fatal_sigNo' is only set if VgSrc_FatalSig is returned. -extern VgSchedReturnCode VG_(scheduler) - ( Int* exit_code, ThreadId* last_run_thread, Int* fatal_sigNo ); +// Do everything which needs doing before the process finally ends, +// like printing reports, etc +extern void VG_(shutdown_actions)(ThreadId tid); extern void VG_(scheduler_init) ( void ); extern void VG_(pp_sched_status) ( void ); // Longjmp back to the scheduler and thus enter the sighandler immediately. -extern void VG_(resume_scheduler) ( Int sigNo, vki_siginfo_t *info ); - -// Longjmp, ending the scheduler, when a fatal signal occurs in the client. -extern void VG_(scheduler_handle_fatal_signal)( Int sigNo ); +extern void VG_(resume_scheduler) ( ThreadId tid, VgSigCode code, + Int sigNo, const vki_siginfo_t *info ); /* The red-zone size which we put at the bottom (highest address) of thread stacks, for paranoia reasons. This can be arbitrary, and @@ -939,32 +859,31 @@ extern void VG_(scheduler_handle_fatal_s SET_THREAD_REG(zztid, zzval, ARCH_PTHREQ_RET, R_PTHREQ_RET, \ post_reg_write_pthread_return) - /* --------------------------------------------------------------------- Exports of vg_signals.c ------------------------------------------------------------------ */ -extern Bool VG_(do_signal_routing); /* whether scheduler LWP has to route signals */ - -/* RT signal allocation */ -extern Int VG_(sig_rtmin); -extern Int VG_(sig_rtmax); -extern Int VG_(sig_alloc_rtsig) ( Int high ); +/* This is a signal mask with almost everything blocked. The + exceptions are signals which could be synchronous (SIGILL, SIGBUS, + SIGSEGV - if we get one of those when they're blocked, then the + whole process could just die without warning), and the unblockable + ones (SIGKILL and SIGSTOP). */ +extern vki_sigset_t VG_(blocked_mask); extern void VG_(sigstartup_actions) ( void ); -extern void VG_(deliver_signal) ( ThreadId tid, const vki_siginfo_t *, Bool async ); -extern void VG_(unblock_host_signal) ( Int sigNo ); +extern void VG_(deliver_signal) ( ThreadId tid, const vki_siginfo_t * ); extern Bool VG_(is_sig_ign) ( Int sigNo ); -/* Route pending signals from the scheduler LWP to the appropriate - thread LWP. */ -extern void VG_(route_signals) ( void ); +/* Poll a thread's set of pending signals, and update the Thread's context to deliver one */ +extern void VG_(poll_signals) ( ThreadId ); /* Fake system calls for signal handling. */ extern void VG_(do_sys_sigaltstack) ( ThreadId tid ); -extern void VG_(do_sys_sigaction) ( ThreadId tid ); +extern Int VG_(do_sys_sigaction) ( ThreadId tid, Int signo, + const struct vki_sigaction *new_act, + struct vki_sigaction *old_act ); extern void VG_(do_sys_sigprocmask) ( ThreadId tid, Int how, vki_sigset_t* set, vki_sigset_t* oldset ); @@ -972,9 +891,8 @@ extern void VG_(do_pthread_sigmask_SCSS_ vki_sigset_t* set, vki_sigset_t* oldset ); -/* Modify the current thread's state once we have detected it is - returning from a signal handler. */ -extern Bool VG_(signal_returns) ( ThreadId tid ); +/* Restore thread's state on return from a signal handler */ +extern void VG_(signal_returns) ( ThreadId tid ); /* Handy utilities to block/restore all host signals. */ extern void VG_(block_all_host_signals) @@ -1037,6 +955,7 @@ extern Char **VG_(env_setenv) ( Char * extern void VG_(env_unsetenv) ( Char **env, const Char *varname ); extern void VG_(env_remove_valgrind_env_stuff) ( Char** env ); +extern void VG_(nanosleep)(struct vki_timespec *); /* --------------------------------------------------------------------- Exports of vg_message.c ------------------------------------------------------------------ */ @@ -1243,6 +1162,12 @@ extern Int VG_(clexecfd); Char* VG_(build_child_VALGRINDCLO) ( Char* exename ); Char* VG_(build_child_exename) ( void ); +/* The master thread the one which will be responsible for mopping + everything up at exit. Normally it is tid 1, since that's the + first thread created, but it may be something else after a + fork(). */ +extern ThreadId VG_(master_tid); + /* Determine if SP adjustment must be noted */ extern Bool VG_(need_to_handle_SP_assignment) ( void ); @@ -1341,56 +1266,12 @@ extern REGPARM(1) void VG_(unknown_SP_update) ( Addr new_SP ); /* --------------------------------------------------------------------- - Exports of vg_proxylwp.c - ------------------------------------------------------------------ */ - -enum PXState -{ - PXS_BAD = -1, - PXS_WaitReq, /* waiting for a request */ - PXS_RunSyscall, /* running a syscall */ - PXS_IntReply, /* request interrupted - need to send reply */ - PXS_SysDone, /* small window between syscall - complete and results written out */ - PXS_SigACK, /* waiting for a signal ACK */ -}; - - -/* Issue a syscall for thread tid */ -extern Int VG_(sys_issue)(int tid); - -extern void VG_(proxy_init) ( void ); -extern void VG_(proxy_create) ( ThreadId tid ); -extern void VG_(proxy_delete) ( ThreadId tid, Bool force ); -extern void VG_(proxy_results) ( void ); -extern void VG_(proxy_sendsig) ( ThreadId tid, Int signo ); -extern void VG_(proxy_setsigmask)(ThreadId tid); -extern void VG_(proxy_sigack) ( ThreadId tid, const vki_sigset_t *); -extern void VG_(proxy_abort_syscall) ( ThreadId tid ); -extern void VG_(proxy_waitsig) ( void ); -extern void VG_(proxy_wait_sys) (ThreadId tid, Bool restart); - -extern void VG_(proxy_shutdown) ( void ); // shut down the syscall workers -extern Int VG_(proxy_resfd) ( void ); // FD something can select on to know - // a syscall finished - -/* Sanity-check the whole proxy-LWP machinery */ -void VG_(sanity_check_proxy)(void); - -/* Send a signal from a thread's proxy to the thread. This longjmps - back into the proxy's main loop, so it doesn't return. */ -__attribute__ ((__noreturn__)) -extern void VG_(proxy_handlesig)( const vki_siginfo_t *siginfo, - Addr ip, Int sysnum ); - -/* --------------------------------------------------------------------- Exports of vg_syscalls.c ------------------------------------------------------------------ */ extern Char *VG_(resolve_filename)(Int fd); -extern Bool VG_(pre_syscall) ( ThreadId tid ); -extern void VG_(post_syscall)( ThreadId tid, Bool restart ); +extern void VG_(client_syscall) ( ThreadId tid ); extern Bool VG_(is_kerror) ( Word res ); @@ -1415,15 +1296,14 @@ void VG_(record_fd_open)(Int tid, Int fd // Flags describing syscall wrappers #define Special (1 << 0) #define MayBlock (1 << 1) -#define NBRunInLWP (1 << 2) // non-blocking, but must run in LWP context -#define PostOnFail (1 << 3) +#define PostOnFail (1 << 2) // For each generic ("gen") wrapper, we declare the pre-wrapper, the // post-wrapper (which is actually not always needed), and the associated // flags. #define GEN_SYSCALL_WRAPPER(x) \ extern UInt VGA_(gen_##x##_flags); \ - extern void VGA_(gen_##x##_before)(ThreadId tid, ThreadState *tst); \ + extern void VGA_(gen_##x##_before)(ThreadId tid, ThreadState *tst, UInt *flags); \ extern void VGA_(gen_##x##_after) (ThreadId tid, ThreadState *tst) // Generic (platform-independent) syscall wrappers. These are generally @@ -1632,7 +1512,6 @@ GEN_SYSCALL_WRAPPER(sys_lremovexattr); GEN_SYSCALL_WRAPPER(sys_fremovexattr); // * L? GEN_SYSCALL_WRAPPER(sys_sched_setaffinity); // * L? GEN_SYSCALL_WRAPPER(sys_sched_getaffinity); // * L? -GEN_SYSCALL_WRAPPER(sys_exit_group); // * ? GEN_SYSCALL_WRAPPER(sys_lookup_dcookie); // (*/32/64) L GEN_SYSCALL_WRAPPER(sys_set_tid_address); // * ? GEN_SYSCALL_WRAPPER(sys_statfs64); // * (?) @@ -1643,6 +1522,9 @@ GEN_SYSCALL_WRAPPER(sys_mq_timedsend); GEN_SYSCALL_WRAPPER(sys_mq_timedreceive); // * P? GEN_SYSCALL_WRAPPER(sys_mq_notify); // * P? GEN_SYSCALL_WRAPPER(sys_mq_getsetattr); // * P? +GEN_SYSCALL_WRAPPER(sys_tkill); // * L +GEN_SYSCALL_WRAPPER(sys_tgkill); // * L +GEN_SYSCALL_WRAPPER(sys_gettid); // * L? #undef GEN_SYSCALL_WRAPPER @@ -1738,7 +1620,7 @@ extern Int VG_(get_bbs_translated) ( vo extern Int VG_(do_syscall) ( UInt, ... ); extern Int VG_(clone) ( Int (*fn)(void *), void *stack, Int flags, void *arg, - Int *child_tid, Int *parent_tid); + Int *child_tid, Int *parent_tid, vki_modify_ldt_t * ); extern void VG_(sigreturn)(void); /* --------------------------------------------------------------------- @@ -1816,6 +1698,24 @@ extern void VGA_(setup_child) ( arch_ extern void VGA_(set_arg_and_bogus_ret) ( ThreadId tid, UWord arg, Addr ret ); extern void VGA_(thread_initial_stack) ( ThreadId tid, UWord arg, Addr ret ); +// OS/Platform-specific thread clear (after thread exit) +extern void VGA_(os_state_clear)(ThreadState *); + +// OS/Platform-specific thread init (at scheduler init time) +extern void VGA_(os_state_init)(ThreadState *); + +// Run a thread from beginning to end. Does not return. +void VGA_(thread_wrapper)(ThreadId tid); + +// Terminate the process. Does not return. +void VGA_(terminate)(ThreadId tid, VgSchedReturnCode src); + +// wait until all other threads are dead +extern void VGA_(reap_threads)(ThreadId self); + +// handle an arch-specific client request +extern Bool VGA_(client_request)(ThreadId tid, UWord *args); + // Symtab stuff extern UInt* VGA_(reg_addr_from_BB) ( Int reg ); extern UInt* VGA_(reg_addr_from_tst) ( Int reg, arch_thread_t* ); @@ -1845,15 +1745,6 @@ extern void VGA_(push_signal_frame) ( Th const vki_sigset_t *mask); extern Int VGA_(pop_signal_frame) ( ThreadId tid ); -// libpthread stuff -typedef struct arch_thread_aux arch_thread_aux_t; - -void VGA_(thread_create) ( arch_thread_aux_t *aux ); -void VGA_(thread_wrapper)( arch_thread_aux_t *aux ); -void VGA_(thread_exit) ( void ); - -Bool VGA_(has_tls) ( void ); - #define MY__STRING(__str) #__str // Assertion to use in code running on the simulated CPU. @@ -1873,7 +1764,7 @@ extern void VG_(user_assert_fail) ( cons struct SyscallTableEntry { UInt *flags_ptr; - void (*before)(ThreadId tid, ThreadState *tst); + void (*before)(ThreadId tid, ThreadState *tst, UInt *flags); void (*after) (ThreadId tid, ThreadState *tst); }; @@ -1895,10 +1786,28 @@ extern const UInt VGA_(syscall_table_siz extern const Addr vga_sys_before, vga_sys_restarted, vga_sys_after, vga_sys_done; -extern void VGA_(restart_syscall)(arch_thread_t* arch); +/* + Perform a syscall on behalf of a client thread, using a specific + signal mask. On completion, the signal mask is set to restore_mask + (which presumably blocks almost everything). If a signal happens + during the syscall, the handler should call + VGA_(interrupted_syscall)() to adjust the thread's context to do the + right thing. +*/ +extern void VGA_(client_syscall)(Int syscallno, arch_thread_t *regs, + const vki_sigset_t *syscall_mask, + const vki_sigset_t *restore_mask, + Int nsigwords); -extern void VGA_(thread_syscall)(Int syscallno, arch_thread_t* arch, - enum PXState* state, enum PXState poststate); +/* + Fix up the thread's state because a syscall may have been + interrupted with a signal. Returns True if a syscall was hit by a + signal, and the appropriate adjustment has been made to the + thread's state, or False if not. + */ +extern Bool VGA_(interrupted_syscall)(arch_thread_t *th_regs, + struct vki_ucontext *uc, + Bool restart); /* --------------------------------------------------------------------- Finally - autoconf-generated settings diff -puN coregrind/core_asm.h~new-threading coregrind/core_asm.h --- valgrind/coregrind/core_asm.h~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/coregrind/core_asm.h 2005-01-08 17:18:28.000000000 -0800 @@ -52,7 +52,7 @@ #define VG_TRC_INNER_FASTMISS 31 /* TRC only; means fast-cache miss. */ #define VG_TRC_INNER_COUNTERZERO 29 /* TRC only; means bb ctr == 0 */ -#define VG_TRC_UNRESUMABLE_SIGNAL 37 /* TRC only; got sigsegv/sigbus */ +#define VG_TRC_FAULT_SIGNAL 37 /* TRC only; got sigsegv/sigbus */ /* Constants for the fast translation lookup cache. */ #define VG_TT_FAST_BITS 15 diff -puN coregrind/linux/Makefile.am~new-threading coregrind/linux/Makefile.am --- valgrind/coregrind/linux/Makefile.am~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/coregrind/linux/Makefile.am 2005-01-11 10:13:26.000000000 -0800 @@ -9,4 +9,6 @@ noinst_HEADERS = \ noinst_LIBRARIES = libos.a libos_a_SOURCES = \ + core_os.c \ + sema.c \ syscalls.c diff -puN /dev/null coregrind/linux/core_os.c --- /dev/null 2004-02-23 13:02:56.000000000 -0800 +++ valgrind-jeremy/coregrind/linux/core_os.c 2005-01-11 10:13:13.000000000 -0800 @@ -0,0 +1,195 @@ +#include "core.h" + +void VGA_(os_state_clear)(ThreadState *tst) +{ + tst->os_state.lwpid = 0; + tst->os_state.threadgroup = 0; +} + +void VGA_(os_state_init)(ThreadState *tst) +{ + tst->os_state.stack = 0; + tst->os_state.stacksize = 0; + + VGA_(os_state_clear)(tst); +} + +/* Run a thread from beginning to end. Does not return. */ +void VGA_(thread_wrapper)(ThreadId tid) +{ + VgSchedReturnCode ret; + ThreadState *tst = VG_(get_ThreadState)(tid); + + vg_assert(tst->status == VgTs_Init); + + /* make sure we get the CPU lock before doing anything significant */ + VG_(set_running)(tid); + + if (0) + VG_(printf)("thread tid %d started: stack = %p\n", + tid, &tid); + + tst->os_state.lwpid = VG_(gettid)(); + tst->os_state.threadgroup = VG_(getpid)(); + + /* Thread created with all signals blocked; scheduler will set the + appropriate mask */ + + ret = VG_(scheduler)(tid); + + vg_assert(VG_(is_exiting)(tid)); + + vg_assert(tst->status == VgTs_Runnable); + vg_assert(VG_(is_running_thread)(tid)); + + if (tid == VG_(master_tid)) { + VG_(shutdown_actions)(tid); + VGA_(terminate)(tid, ret); + } else { + /* OK, disappear, but tell the reaper */ + vg_assert(tst->status == VgTs_Runnable); + VG_(exit_thread)(tid); + VG_(tkill)(VG_(threads)[VG_(master_tid)].os_state.lwpid, VKI_SIGVGCHLD); + VG_(exit_single)(tst->os_state.exitcode); + } +} + +void VGA_(terminate)(ThreadId tid, VgSchedReturnCode src) +{ + vg_assert(tid == VG_(master_tid)); + vg_assert(VG_(count_living_threads)() == 0); + + //-------------------------------------------------------------- + // Exit, according to the scheduler's return code + //-------------------------------------------------------------- + switch (src) { + case VgSrc_ExitSyscall: /* the normal way out */ + VG_(exit)( VG_(threads)[VG_(master_tid)].os_state.exitcode ); + /* NOT ALIVE HERE! */ + VG_(core_panic)("entered the afterlife in main() -- ExitSyscall"); + break; /* what the hell :) */ + + case VgSrc_FatalSig: + /* We were killed by a fatal signal, so replicate the effect */ + vg_assert(VG_(threads)[VG_(master_tid)].os_state.fatalsig != 0); + VG_(kill_self)(VG_(threads)[VG_(master_tid)].os_state.fatalsig); + VG_(core_panic)("main(): signal was supposed to be fatal"); + break; + + default: + VG_(core_panic)("main(): unexpected scheduler return code"); + } +} + +/* wait until all other threads are dead */ +void VGA_(reap_threads)(ThreadId self) +{ + vki_sigset_t mask; + vki_sigset_t sigchld; + vki_siginfo_t si; + + /* block everything */ + VG_(sigfillset)(&mask); + VG_(sigprocmask)(VKI_SIG_SETMASK, &mask, NULL); + + /* wait for SIGVGCHLD to show up */ + VG_(sigemptyset)(&sigchld); + VG_(sigaddset)(&sigchld, VKI_SIGVGCHLD); + + vg_assert(VG_(threads)[self].status == VgTs_Runnable); + + while(VG_(count_living_threads)() > 1) { + Int sig; + + VG_(set_sleeping)(self, VgTs_WaitSys); /* let other threads run a bit */ + sig = VG_(sigtimedwait)(&sigchld, &si, 0); + VG_(set_running)(self); + + if (sig == -VKI_EINTR) + continue; + + vg_assert(sig == VKI_SIGVGCHLD); + vg_assert(si.si_signo == VKI_SIGVGCHLD); + } +} + +/* The we need to know the address of it so it can be + called at program exit. */ +static Addr __libc_freeres_wrapper; + +void VGA_(intercept_libc_freeres_wrapper)(Addr addr) +{ + __libc_freeres_wrapper = addr; +} + +/* Clean up the client by calling __libc_freeres() (if requested) */ +void VGA_(final_tidyup)(ThreadId tid) +{ + struct vki_sigaction sa; + + vg_assert(VG_(is_running_thread)(tid)); + + if (!VG_(needs).libc_freeres || + !VG_(clo_run_libc_freeres) || + __libc_freeres_wrapper == 0) + return; /* can't/won't do it */ + + if (VG_(clo_verbosity) > 2 || + VG_(clo_trace_syscalls) || + VG_(clo_trace_sched)) + VG_(message)(Vg_DebugMsg, + "Caught __NR_exit; running __libc_freeres()"); + + /* point thread context to point to libc_freeres_wrapper */ + ARCH_INSTR_PTR(VG_(threads)[tid].arch) = __libc_freeres_wrapper; + // XXX should we use a special stack? + + /* block all blockable signals... */ + VG_(threads)[tid].sig_mask = VG_(blocked_mask); + + /* and restore handlers to default */ + sa.ksa_handler = VKI_SIG_DFL; + VG_(sigemptyset)(&sa.sa_mask); + sa.sa_flags = 0; + + VG_(do_sys_sigaction)(tid, VKI_SIGSEGV, &sa, NULL); + VG_(do_sys_sigaction)(tid, VKI_SIGBUS, &sa, NULL); + VG_(do_sys_sigaction)(tid, VKI_SIGILL, &sa, NULL); + VG_(do_sys_sigaction)(tid, VKI_SIGFPE, &sa, NULL); + + // We were exiting, so assert that... + vg_assert(VG_(is_exiting)(tid)); + // ...but now we're not again + VG_(threads)[tid].exitreason = VgSrc_None; + + // run until client thread exits - ideally with LIBC_FREERES_DONE, + // but exit/exitgroup/signal will do + VG_(scheduler)(tid); + + vg_assert(VG_(is_exiting)(tid)); +} + +// Arch-specific client requests +Bool VGA_(client_request)(ThreadId tid, UWord *args) +{ + Bool handled = True; + + vg_assert(VG_(is_running_thread)(tid)); + + switch(args[0]) { + case VG_USERREQ__LIBC_FREERES_DONE: + /* This is equivalent to an exit() syscall, but we don't set the + exitcode (since it might already be set) */ + if (0 || VG_(clo_trace_syscalls) || VG_(clo_trace_sched)) + VG_(message)(Vg_DebugMsg, + "__libc_freeres() done; really quitting!"); + VG_(threads)[tid].exitreason = VgSrc_ExitSyscall; + break; + + default: + handled = False; + break; + } + + return handled; +} diff -puN coregrind/linux/core_os.h~new-threading coregrind/linux/core_os.h --- valgrind/coregrind/linux/core_os.h~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/coregrind/linux/core_os.h 2005-01-10 12:21:41.000000000 -0800 @@ -34,10 +34,12 @@ #define LINUX_SYSCALL_WRAPPER(x) \ extern UInt VGA_(linux_##x##_flags); \ - extern void VGA_(linux_##x##_before)(ThreadId tid, ThreadState *tst); \ + extern void VGA_(linux_##x##_before)(ThreadId tid, ThreadState *tst, UInt *flags); \ extern void VGA_(linux_##x##_after) (ThreadId tid, ThreadState *tst) // These syscalls are Linux-specific, but architecture-independent. +LINUX_SYSCALL_WRAPPER(sys_exit_group); + LINUX_SYSCALL_WRAPPER(sys_mount); LINUX_SYSCALL_WRAPPER(sys_oldumount); LINUX_SYSCALL_WRAPPER(sys_umount); @@ -81,6 +83,90 @@ LINUX_SYSCALL_WRAPPER(sys_io_getevents); LINUX_SYSCALL_WRAPPER(sys_io_submit); LINUX_SYSCALL_WRAPPER(sys_io_cancel); +#define FUTEX_SEMA 0 + +#if FUTEX_SEMA +/* --------------------------------------------------------------------- + Definition for a semaphore. Defined in terms of futex. + + Futex semaphore operations taken from futex-2.2/usersem.h + ------------------------------------------------------------------ */ +typedef struct { + int count; +} vg_sema_t; + +extern Int __futex_down_slow(vg_sema_t *, int, struct vki_timespec *); +extern Int __futex_up_slow(vg_sema_t *); + +void VG_(sema_init)(vg_sema_t *); + +static inline void VG_(sema_down)(vg_sema_t *futx) +{ + Int val, woken = 0; + + /* Returns new value */ + while ((val = __futex_down(&futx->count)) != 0) { + Int ret = __futex_down_slow(futx, val, NULL); + if (ret < 0) + return; /* error */ + else if (ret == 1) + return; /* passed */ + else if (ret == 0) + woken = 1; /* slept */ + else + /* loop */; + } + /* If we were woken, someone else might be sleeping too: set to -1 */ + if (woken) { + futx->count = -1; + } + return; +} + +/* If __futex_up increments count from 0 -> 1, noone was waiting. + Otherwise, set to 1 and tell kernel to wake them up. */ +static inline void VG_(sema_up)(vg_sema_t *futx) +{ + if (!__futex_up(&futx->count)) + __futex_up_slow(futx); +} +#else /* !FUTEX_SEMA */ +/* + Not really a semaphore, but use a pipe for a token-passing scheme + */ +typedef struct { + Int pipe[2]; + Int owner_thread; /* who currently has it */ +} vg_sema_t; + +void VG_(sema_init)(vg_sema_t *); +void VG_(sema_down)(vg_sema_t *sema); +void VG_(sema_up)(vg_sema_t *sema); + +#endif /* FUTEX_SEMA */ + +/* OS-specific thread state */ +typedef struct { + /* who we are */ + Int lwpid; /* PID of kernel task */ + Int threadgroup; /* thread group id */ + + /* how we were started */ + UInt clone_flags; /* flags passed to clone() to create this thread */ + Int *parent_tidptr; + Int *child_tidptr; + + ThreadId parent; /* parent tid (if any) */ + + /* runtime details */ + UInt *stack; /* stack base */ + UInt stacksize; /* stack size in UInts */ + + /* exit details */ + Int exitcode; /* in the case of exitgroup, set by someone else */ + Int fatalsig; /* fatal signal */ +} os_thread_t; + #endif // __LINUX_CORE_OS_H /*--------------------------------------------------------------------*/ diff -puN /dev/null coregrind/linux/sema.c --- /dev/null 2004-02-23 13:02:56.000000000 -0800 +++ valgrind-jeremy/coregrind/linux/sema.c 2005-01-08 17:18:28.000000000 -0800 @@ -0,0 +1,105 @@ +#include "core.h" + +#if FUTEX_SEMA +/* + Futex-based semaphore operations. + + Taken from futex-2.2/usersem.c + Based on work by Matthew Kirkwood . + */ + + +#define FUTEX_PASSED (-(1024 * 1024 * 1024)) + +static inline Int sys_futex(Int *futex, Int op, Int val, struct vki_timespec *rel) +{ + return VG_(do_syscall)(__NR_futex, futex, op, val, rel); +} + +/* Returns -1 on fail, 0 on wakeup, 1 on pass, 2 on didn't sleep */ +int __futex_down_slow(vg_sema_t *futx, int val, struct vki_timespec *rel) +{ + Int ret; + + ret = sys_futex(&futx->count, VKI_FUTEX_WAIT, val, rel); + if (ret == 0) { + /* <= in case someone else decremented it */ + if (futx->count <= FUTEX_PASSED) { + futx->count = -1; + return 1; + } + return 0; + } + /* EWOULDBLOCK just means value changed before we slept: loop */ + if (ret == -VKI_EWOULDBLOCK) + return 2; + return -1; +} + +int __futex_up_slow(vg_sema_t *futx) +{ + futx->count = 1; + __futex_commit(); + return sys_futex(&futx->count, VKI_FUTEX_WAKE, 1, NULL); +} + +void VG_(sema_init)(vg_sema_t *sema) +{ + sema->count = 1; + __futex_commit(); +} + +#else /* !FUTEX_SEMA */ + +/* + Slower but more portable pipe-based token passing scheme. + */ + +void VG_(sema_init)(vg_sema_t *sema) +{ + VG_(pipe)(sema->pipe); + sema->pipe[0] = VG_(safe_fd)(sema->pipe[0]); + sema->pipe[1] = VG_(safe_fd)(sema->pipe[1]); + + sema->owner_thread = -1; + + /* create initial token */ + VG_(write)(sema->pipe[1], "T", 1); +} + +/* get a token */ +void VG_(sema_down)(vg_sema_t *sema) +{ + Char buf[2] = { 'x' }; + Int ret; + Int lwpid = VG_(gettid)(); + + vg_assert(sema->owner_thread != lwpid); /* can't have it already */ + + again: + ret = VG_(read)(sema->pipe[0], buf, 2); + + if (ret == -VKI_EINTR) + goto again; + + vg_assert(ret == 1); /* should get exactly 1 token */ + vg_assert(buf[0] == 'T'); + + sema->owner_thread = lwpid; +} + +/* put token back */ +void VG_(sema_up)(vg_sema_t *sema) +{ + Int ret; + + vg_assert(sema->owner_thread == VG_(gettid)()); /* must have it */ + + sema->owner_thread = 0; + + ret = VG_(write)(sema->pipe[1], "T", 1); + + vg_assert(ret == 1); +} + +#endif /* FUTEX_SEMA */ diff -puN coregrind/linux/syscalls.c~new-threading coregrind/linux/syscalls.c --- valgrind/coregrind/linux/syscalls.c~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/coregrind/linux/syscalls.c 2005-01-11 10:13:05.000000000 -0800 @@ -39,7 +39,7 @@ #define PRE(x,f) \ UInt VGA_(linux_##x##_flags) = f; \ - void VGA_(linux_##x##_before)(ThreadId tid, ThreadState *tst) + void VGA_(linux_##x##_before)(ThreadId tid, ThreadState *tst, UInt *flags) #define POST(x) \ void VGA_(linux_##x##_after) (ThreadId tid, ThreadState *tst) @@ -59,6 +59,28 @@ VG_(printf)(format, ## args) +PRE(sys_exit_group, Special) +{ + ThreadId t; + + PRINT("exit_group( %d )", arg1); + PRE_REG_READ1(void, "exit_group", int, exit_code); + + /* A little complex; find all the threads with the same threadgroup + as this one (including this one), and mark them to exit */ + for(t = 1; t < VG_N_THREADS; t++) { + if (VG_(threads)[t].status == VgTs_Empty || /* not alive */ + VG_(threads)[t].os_state.threadgroup != tst->os_state.threadgroup) /* not our group */ + continue; + + VG_(threads)[t].exitreason = VgSrc_ExitSyscall; + VG_(threads)[t].os_state.exitcode = arg1; + + if (t != tid) + VG_(kill_thread)(t); /* unblock it, if blocked */ + } +} + PRE(sys_mount, MayBlock) { // Nb: depending on 'flags', the 'type' and 'data' args may be ignored. diff -puN coregrind/vg_execontext.c~new-threading coregrind/vg_execontext.c --- valgrind/coregrind/vg_execontext.c~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/coregrind/vg_execontext.c 2005-01-08 17:18:28.000000000 -0800 @@ -305,20 +305,19 @@ ExeContext* VG_(get_ExeContext2) ( Addr void get_needed_regs(ThreadId tid, Addr* ip, Addr* fp, Addr* sp, Addr* stack_highest_word) { - if (VG_(is_running_thread)(tid)) { + if (VG_(is_VCPU_thread)(tid)) { /* thread currently in baseblock */ *ip = VG_(baseBlock)[VGOFF_INSTR_PTR]; *fp = VG_(baseBlock)[VGOFF_FRAME_PTR]; *sp = VG_(baseBlock)[VGOFF_STACK_PTR]; - *stack_highest_word = VG_(threads)[tid].stack_highest_word; } else { /* thread in thread table */ ThreadState* tst = & VG_(threads)[ tid ]; *ip = ARCH_INSTR_PTR(tst->arch); *fp = ARCH_FRAME_PTR(tst->arch); *sp = ARCH_STACK_PTR(tst->arch); - *stack_highest_word = tst->stack_highest_word; } + *stack_highest_word = VG_(threads)[tid].stack_highest_word; /* Nasty little hack to deal with sysinfo syscalls - if libc is using the sysinfo page for syscalls (the TLS version does), then diff -puN -L coregrind/vg_libpthread.c coregrind/vg_libpthread.c~new-threading /dev/null --- valgrind/coregrind/vg_libpthread.c +++ /dev/null 2004-02-23 13:02:56.000000000 -0800 @@ -1,3448 +0,0 @@ - -/*--------------------------------------------------------------------*/ -/*--- A replacement for the standard libpthread.so. ---*/ -/*--- vg_libpthread.c ---*/ -/*--------------------------------------------------------------------*/ - -/* - This file is part of Valgrind, an extensible x86 protected-mode - emulator for monitoring program execution on x86-Unixes. - - Copyright (C) 2000-2004 Julian Seward - jseward@acm.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. -*/ - -/* ALL THIS CODE RUNS ON THE SIMULATED CPU. - - This is a replacement for the standard libpthread.so. It is loaded - as part of the client's image (if required) and directs pthread - calls through to Valgrind's request mechanism. - - A couple of caveats. - - 1. Since it's a binary-compatible replacement for an existing library, - we must take care to used exactly the same data layouts, etc, as - the standard pthread.so does. - - 2. Since this runs as part of the client, there are no specific - restrictions on what headers etc we can include, so long as - this libpthread.so does not end up having dependencies on .so's - which the real one doesn't. - - Later ... it appears we cannot call file-related stuff in libc here, - perhaps fair enough. Be careful what you call from here. Even exit() - doesn't work (gives infinite recursion and then stack overflow); hence - myexit(). Also fprintf doesn't seem safe. -*/ - -#include "valgrind.h" /* For the request-passing mechanism */ -#include "core.h" /* For the VG_USERREQ__* constants */ - -#define __USE_UNIX98 -#include -#include -#undef __USE_UNIX98 - -#define __USE_GNU -#include -#undef __USE_GNU - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -# define strong_alias(name, aliasname) \ - extern __typeof (name) aliasname __attribute__ ((alias (#name))); - -# define weak_alias(name, aliasname) \ - extern __typeof (name) aliasname __attribute__ ((weak, alias (#name))); - - -/* --------------------------------------------------------------------- - Our own definition of types that vary between LinuxThreads and NPTL. - ------------------------------------------------------------------ */ - -/* Moving from LinuxThreads to NPTL, several crucial types (eg. - pthread_mutex_t, pthread_mutexattr_t, etc) in pthreadtypes.h were changed - in binary-compatible, but source-incompatible, ways. We can similarly - use any layout we want, so long as it's binary-compatible. However, we - can no longer use the LinuxThreads types, because they won't work on NPTL - systems. Thus, we have to introduce a layer of indirection, and define - our own versions of these types (vg_pthread_mutex_t, etc). NPTL does - pretty much the same thing, and it keeps many of its internal types - secret. - - We can layout our types however we want, as long as we put the small - number of fields in the right place for binary compatibility (eg. - mutex->kind). To make life easy, our versions have the exact same layout - as the LinuxThreads ones; only the type names and field names are - different (they differ only by include "vg" at the start). - - In our implementation of the pthread operations (pthread_mutex_lock(), - pthread_mutexattr_settype(), etc) we always cast the standard pthread - types to our own types, (eg. pthread_mutex_t --> vg_pthread_mutex_t), - before working with them. - - Note that we have various mutexes (and condvars) in this file that have the - type pthread_mutex_t (and pthread_cond_t). That is fine, because they - are always only handled by calling the standard pthread functions (eg. - pthread_mutex_lock()) on them. Phew. - - WARNING: as a result of all this, we should *never* access these standard - pthread types as is; they *must* be converted to the vg_pthread_foo_t - equivalent. It would be nice if this was enforced... (but compilation - on NPTL-only systems should fail if this rule isn't followed...?) -*/ - -#include // for 'struct __sched_param' - -typedef struct __vg_pthread_attr_s -{ - int __vg_detachstate; - int __vg_schedpolicy; - struct __sched_param __vg_schedparam; - int __vg_inheritsched; - int __vg_scope; - size_t __vg_guardsize; - int __vg_stackaddr_set; - void *__vg_stackaddr; - size_t __vg_stacksize; -} vg_pthread_attr_t; - -typedef struct -{ - int __vg_mutexkind; -} vg_pthread_mutexattr_t; - -typedef struct -{ - int __vg_pshared; -} vg_pthread_condattr_t; - -typedef struct _vg_pthread_rwlock_t -{ - struct _vg_pthread_fastlock __vg_rw_lock; /* Lock to guarantee mutual exclusion */ - int __vg_rw_readers; /* Number of readers */ - /*_pthread_descr*/ void* __vg_rw_writer; /* Identity of writer, or NULL if none */ - /*_pthread_descr*/ void* __vg_rw_read_waiting; /* Threads waiting for reading */ - /*_pthread_descr*/ void* __vg_rw_write_waiting; /* Threads waiting for writing */ - int __vg_rw_kind; /* Reader/Writer preference selection */ - int __vg_rw_pshared; /* Shared between processes or not */ -} vg_pthread_rwlock_t; - -typedef struct -{ - int __vg_lockkind; - int __vg_pshared; -} vg_pthread_rwlockattr_t; - -/* Converting pthread types to vg_pthread types. We always check that the - passed-in type is as big as ours, for safety. We also zero the pointer - to the original struct, to ensure we don't accidentally use it again. */ - -#define CONVERT(foo, x, vg_x) \ - my_assert(sizeof(*x) >= sizeof(vg_pthread_##foo##_t)); \ - vg_x = (vg_pthread_##foo##_t*)x; \ - x = 0; // ensure we don't accidentally use x again! - - -/* --------------------------------------------------------------------- - Our own definition of types that only exist in NPTL. - ------------------------------------------------------------------ */ - -#ifndef HAVE___PTHREAD_UNWIND_BUF_T - -typedef struct -{ - struct - { - jmp_buf __cancel_jmp_buf; - int __mask_was_saved; - } __cancel_jmp_buf[1]; - void *__pad[4]; -} __pthread_unwind_buf_t __attribute__ ((__aligned__)); - -#endif - -/* --------------------------------------------------------------------- - Forwardses. - ------------------------------------------------------------------ */ - -#define WEAK __attribute__((weak)) - -static -__inline__ -int is_kerror ( int res ) -{ - if (res >= -4095 && res <= -1) - return 1; - else - return 0; -} - - -#ifdef GLIBC_2_3 - /* kludge by JRS (not from glibc) ... */ - typedef void* __locale_t; - - /* Copied from locale/locale.h in glibc-2.2.93 sources */ - /* This value can be passed to `uselocale' and may be returned by - it. Passing this value to any other function has undefined - behavior. */ -# define LC_GLOBAL_LOCALE ((__locale_t) -1L) - extern __locale_t __uselocale ( __locale_t ); -#endif - -static void -init_global_thread_specific_state ( void ); - -static void -init_thread_specific_state ( void ); - -static void -set_ret_val ( void* ); -static void * -get_ret_val ( void ); - -/* --------------------------------------------------------------------- - Helpers. We have to be pretty self-sufficient. - ------------------------------------------------------------------ */ - -/* Number of times any given error message is printed. */ -#define N_MOANS 3 - -/* Extract from Valgrind the value of VG_(clo_trace_pthread_level). - Returns 0 (none) if not running on Valgrind. */ -static -int get_pt_trace_level ( void ) -{ - int res; - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__GET_PTHREAD_TRACE_LEVEL, - 0, 0, 0, 0); - return res; -} - -/* Don't do anything if we're not under Valgrind */ -static __inline__ -void ensure_valgrind ( char* caller ) -{ - if (!RUNNING_ON_VALGRIND) { - const char msg[] = "Warning: this libpthread.so should only be run with Valgrind\n"; - VG_(do_syscall)(__NR_write, 2, msg, sizeof(msg)-1); - VG_(do_syscall)(__NR_exit, 1); - } -} - -/* While we're at it ... hook our own startup function into this - game. */ - -static -__attribute__((noreturn)) -void barf ( const char* str ) -{ - char buf[1000]; - strcpy(buf, "\nvalgrind's libpthread.so: "); - strcat(buf, str); - strcat(buf, "\nPlease report this bug at: "); - strcat(buf, VG_BUGS_TO); - strcat(buf, "\n\n"); - VALGRIND_INTERNAL_PRINTF(buf); - _exit(1); - /* We have to persuade gcc into believing this doesn't return. */ - while (1) { }; -} - - -static void cat_n_send ( char* s1, char* s2, char* s3 ) -{ - char buf[1000]; - if (get_pt_trace_level() >= 0) { - snprintf(buf, sizeof(buf), "%s%s%s", s1, s2, s3); - buf[sizeof(buf)-1] = '\0'; - VALGRIND_INTERNAL_PRINTF(buf); - } -} - -static void oh_dear ( char* fn, char* aux, char* s ) -{ - cat_n_send ( "warning: Valgrind's ", fn, s ); - if (NULL != aux) - cat_n_send ( " ", aux, "" ); - cat_n_send ( " your program may misbehave as a result", "", "" ); -} - -static void ignored ( char* fn, char* aux ) -{ - oh_dear ( fn, aux, " does nothing" ); -} - -static void kludged ( char* fn, char* aux ) -{ - oh_dear ( fn, aux, " is incomplete" ); -} - - -__attribute__((noreturn)) -void vgPlain_unimp ( char* fn ) -{ - cat_n_send ( "valgrind's libpthread.so: UNIMPLEMENTED FUNCTION: ", fn, "" ); - barf("unimplemented function"); -} - - -void VG_(user_assert_fail) ( const Char* expr, const Char* file, Int line, const Char* fn ) -{ - char buf[1000]; - static Bool entered = False; - if (entered) - _exit(2); - entered = True; - sprintf(buf, "\n%s: %s:%d (%s): Assertion `%s' failed.\n", - "valgrind", file, line, fn, expr ); - cat_n_send ( "", buf, "" ); - sprintf(buf, "Please report this bug at: %s\n\n", VG_BUGS_TO); - cat_n_send ( "", buf, "" ); - _exit(1); -} - -static -void my_free ( void* ptr ) -{ -#if 0 - int res; - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__FREE, ptr, 0, 0, 0); - my_assert(res == 0); -#else - free(ptr); -#endif -} - - -static -void* my_malloc ( int nbytes ) -{ - void* res; -#if 0 - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__MALLOC, nbytes, 0, 0, 0); -#else - res = malloc(nbytes); -#endif - my_assert(res != (void*)0); - return res; -} - - - -/* --------------------------------------------------------------------- - Pass pthread_ calls to Valgrind's request mechanism. - ------------------------------------------------------------------ */ - - -/* --------------------------------------------------- - Ummm .. - ------------------------------------------------ */ - -static -void pthread_error ( const char* msg ) -{ - int res; - VALGRIND_MAGIC_SEQUENCE(res, 0, - VG_USERREQ__PTHREAD_ERROR, - msg, 0, 0, 0); -} - - -/* --------------------------------------------------- - Here so it can be inlined without complaint. - ------------------------------------------------ */ - -__inline__ -pthread_t pthread_self(void) -{ - int tid; - ensure_valgrind("pthread_self"); - VALGRIND_MAGIC_SEQUENCE(tid, 0 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - if (tid < 1 || tid >= VG_N_THREADS) - barf("pthread_self: invalid ThreadId"); - return tid; -} - - -/* --------------------------------------------------- - THREAD ATTRIBUTES - ------------------------------------------------ */ - -int pthread_attr_init(pthread_attr_t *attr) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - - /* Just initialise the fields which we might look at. */ - vg_attr->__vg_detachstate = PTHREAD_CREATE_JOINABLE; - /* Linuxthreads sets this field to the value __getpagesize(), so I - guess the following is OK. */ - vg_attr->__vg_guardsize = VKI_PAGE_SIZE; - /* No special stack yet. */ - vg_attr->__vg_stackaddr = 0; - vg_attr->__vg_stacksize = VG_PTHREAD_STACK_SIZE; - return 0; -} - -int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - - if (detachstate != PTHREAD_CREATE_JOINABLE - && detachstate != PTHREAD_CREATE_DETACHED) { - pthread_error("pthread_attr_setdetachstate: " - "detachstate is invalid"); - return EINVAL; - } - vg_attr->__vg_detachstate = detachstate; - return 0; -} - -int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - *detachstate = vg_attr->__vg_detachstate; - return 0; -} - -int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inherit) -{ - static int moans = N_MOANS; - if (moans-- > 0) - kludged("pthread_attr_getinheritsched", NULL); - *inherit = PTHREAD_EXPLICIT_SCHED; - return 0; -} - -int pthread_attr_setinheritsched(pthread_attr_t *attr, int inherit) -{ - static int moans = N_MOANS; - if (moans-- > 0) - ignored("pthread_attr_setinheritsched", NULL); - return 0; -} - -WEAK -int pthread_attr_setstacksize (pthread_attr_t *attr, - size_t stacksize) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - vg_attr->__vg_stacksize = stacksize; - return 0; -} - - -/* This is completely bogus. */ -int pthread_attr_getschedparam(const pthread_attr_t *attr, - struct sched_param *param) -{ - static int moans = N_MOANS; - if (moans-- > 0) - kludged("pthread_attr_getschedparam", NULL); -# ifdef HAVE_SCHED_PRIORITY - if (param) param->sched_priority = 0; /* who knows */ -# else - if (param) param->__sched_priority = 0; /* who knows */ -# endif - return 0; -} - -int pthread_attr_setschedparam(pthread_attr_t *attr, - const struct sched_param *param) -{ - static int moans = N_MOANS; - if (moans-- > 0) - ignored("pthread_attr_setschedparam", "(scheduling not changeable)"); - return 0; -} - -int pthread_attr_destroy(pthread_attr_t *attr) -{ - static int moans = N_MOANS; - if (moans-- > 0) - ignored("pthread_attr_destroy", NULL); - return 0; -} - -/* These are no-ops, as with LinuxThreads. */ -int pthread_attr_setscope ( pthread_attr_t *attr, int scope ) -{ - ensure_valgrind("pthread_attr_setscope"); - if (scope == PTHREAD_SCOPE_SYSTEM) - return 0; - pthread_error("pthread_attr_setscope: " - "invalid or unsupported scope"); - if (scope == PTHREAD_SCOPE_PROCESS) - return ENOTSUP; - return EINVAL; -} - -int pthread_attr_getscope ( const pthread_attr_t *attr, int *scope ) -{ - ensure_valgrind("pthread_attr_setscope"); - if (scope) - *scope = PTHREAD_SCOPE_SYSTEM; - return 0; -} - - -/* Pretty bogus. Avoid if possible. */ -int pthread_getattr_np (pthread_t thread, pthread_attr_t *attr) -{ - StackInfo si; - int res; - int detached; - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - - ensure_valgrind("pthread_getattr_np"); - kludged("pthread_getattr_np", NULL); - vg_attr->__vg_detachstate = PTHREAD_CREATE_JOINABLE; - vg_attr->__vg_schedpolicy = SCHED_OTHER; - vg_attr->__vg_schedparam.sched_priority = 0; - vg_attr->__vg_inheritsched = PTHREAD_EXPLICIT_SCHED; - vg_attr->__vg_scope = PTHREAD_SCOPE_SYSTEM; - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__GET_STACK_INFO, - thread, &si, 0, 0 ); - vg_attr->__vg_guardsize = si.guardsize; - vg_attr->__vg_stackaddr = (void *)si.base; - vg_attr->__vg_stackaddr_set = 0; - vg_attr->__vg_stacksize = si.size; - VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */, - VG_USERREQ__SET_OR_GET_DETACH, - 2 /* get */, thread, 0, 0); - my_assert(detached == 0 || detached == 1); - if (detached) - vg_attr->__vg_detachstate = PTHREAD_CREATE_DETACHED; - return 0; -} - - -WEAK -int pthread_attr_getstack ( const pthread_attr_t * attr, - void ** stackaddr, - size_t *stacksize ) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - ensure_valgrind("pthread_attr_getstack"); - if (stackaddr) - *stackaddr = vg_attr->__vg_stackaddr; - if (stacksize) - *stacksize = vg_attr->__vg_stacksize; - return 0; -} - -WEAK -int pthread_attr_getstackaddr ( const pthread_attr_t * attr, - void ** stackaddr ) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - ensure_valgrind("pthread_attr_getstackaddr"); - if (stackaddr) - *stackaddr = vg_attr->__vg_stackaddr; - return 0; -} - -WEAK -int pthread_attr_getstacksize ( const pthread_attr_t * attr, - size_t * stacksize ) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - ensure_valgrind("pthread_attr_getstacksize"); - if (stacksize) - *stacksize = vg_attr->__vg_stacksize; - return 0; -} - -int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - if (policy != SCHED_OTHER && policy != SCHED_FIFO && policy != SCHED_RR) - return EINVAL; - vg_attr->__vg_schedpolicy = policy; - return 0; -} - -int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - *policy = vg_attr->__vg_schedpolicy; - return 0; -} - - -WEAK -int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - vg_attr->__vg_guardsize = guardsize; - return 0; -} - -WEAK -int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize) -{ - vg_pthread_attr_t* vg_attr; - CONVERT(attr, attr, vg_attr); - *guardsize = vg_attr->__vg_guardsize; - return 0; -} - -/* Again, like LinuxThreads. */ - -static int concurrency_current_level = 0; - -WEAK -int pthread_setconcurrency(int new_level) -{ - if (new_level < 0) - return EINVAL; - else { - concurrency_current_level = new_level; - return 0; - } -} - -WEAK -int pthread_getconcurrency(void) -{ - return concurrency_current_level; -} - - -/* All exiting threads eventually pass through here, bearing the - return value, or PTHREAD_CANCELED, in ret_val. */ -static -__attribute__((noreturn)) -void thread_exit_wrapper ( void* ret_val ) -{ - int detached, res; - CleanupEntry cu; - pthread_key_t key; - void** specifics_ptr; - - /* Run this thread's key finalizers. Really this should be run - PTHREAD_DESTRUCTOR_ITERATIONS times. */ - for (key = 0; key < VG_N_THREAD_KEYS; key++) { - VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, - VG_USERREQ__GET_KEY_D_AND_S, - key, &cu, 0, 0 ); - if (res == 0) { - /* valid key */ - my_assert(cu.type == VgCt_Function); - if (cu.data.function.fn && cu.data.function.arg) - cu.data.function.fn /* destructor for key */ - ( cu.data.function.arg /* specific for key for this thread */ ); - continue; - } - my_assert(res == -1); - } - - /* Free up my specifics space, if any. */ - VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, - VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, - pthread_self(), 0, 0, 0); - my_assert(specifics_ptr != (void**)3); - my_assert(specifics_ptr != (void**)1); /* 1 means invalid thread */ - if (specifics_ptr != NULL) - my_free(specifics_ptr); - - VGA_(thread_exit)(); - - /* Decide on my final disposition. */ - VALGRIND_MAGIC_SEQUENCE(detached, (-1) /* default */, - VG_USERREQ__SET_OR_GET_DETACH, - 2 /* get */, pthread_self(), 0, 0); - my_assert(detached == 0 || detached == 1); - - if (detached) { - /* Detached; I just quit right now. */ - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__QUIT, 0, 0, 0, 0); - } else { - /* Not detached; so I wait for a joiner. */ - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__WAIT_JOINER, ret_val, 0, 0, 0); - } - /* NOTREACHED */ - barf("thread_exit_wrapper: still alive?!"); -} - - -/* This function is a wrapper function for running a thread. It runs - the root function specified in pthread_create, and then, should the - root function return a value, it arranges to run the thread's - cleanup handlers and exit correctly. */ - -/* Struct used to convey info from pthread_create to thread_wrapper. - Must be careful not to pass to the child thread any pointers to - objects which might be on the parent's stack. */ -typedef - struct { - int attr__detachstate; - void* (*root_fn) ( void* ); - void* arg; - sigset_t sigmask; - arch_thread_aux_t aux; - } - NewThreadInfo; - -/* Struct used to describe a TDB header, copied from glibc. */ -typedef - struct { - void *tcb; - void *dtv; - void *self; - int multiple_threads; - unsigned long sysinfo; - } - tcbhead_t; - -/* This is passed to the VG_USERREQ__APPLY_IN_NEW_THREAD and so must - not return. Note that this runs in the new thread, not the - parent. */ -static -__attribute__((noreturn)) -void thread_wrapper ( NewThreadInfo* info ) -{ - int attr__detachstate; - void* (*root_fn) ( void* ); - void* arg; - void* ret_val; - __pthread_unwind_buf_t ub; - - attr__detachstate = info->attr__detachstate; - root_fn = info->root_fn; - arg = info->arg; - - VGA_(thread_wrapper)(&info->aux); - - /* Minimally observe the attributes supplied. */ - if (attr__detachstate != PTHREAD_CREATE_DETACHED - && attr__detachstate != PTHREAD_CREATE_JOINABLE) - pthread_error("thread_wrapper: invalid attr->__detachstate"); - if (attr__detachstate == PTHREAD_CREATE_DETACHED) - pthread_detach(pthread_self()); - - /* Initialise thread specific state */ - init_thread_specific_state(); - - /* Now that everything is set up, restore our signal mask (we're - ready to accept signals) */ - sigprocmask(SIG_SETMASK, &info->sigmask, NULL); - - /* Free up the arg block that pthread_create malloced. */ - my_free(info); - - - if (setjmp(ub.__cancel_jmp_buf[0].__cancel_jmp_buf) == 0) { - CleanupEntry cu; - int res; - - cu.type = VgCt_Longjmp; - cu.data.longjmp.ub = &ub; - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__CLEANUP_PUSH, - &cu, 0, 0, 0); - - /* The root function might not return. But if it does we simply - move along to thread_exit_wrapper. All other ways out for the - thread (cancellation, or calling pthread_exit) lead there - too. */ - ret_val = root_fn(arg); - } - else { - ret_val = get_ret_val(); - } - - thread_exit_wrapper(ret_val); - /* NOTREACHED */ -} - - -/* --------------------------------------------------- - CLEANUP STACKS - ------------------------------------------------ */ - -void _pthread_cleanup_push (struct _pthread_cleanup_buffer *__buffer, - void (*__routine) (void *), - void *__arg) -{ - int res; - CleanupEntry cu; - ensure_valgrind("_pthread_cleanup_push"); - cu.type = VgCt_Function; - cu.data.function.fn = __routine; - cu.data.function.arg = __arg; - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__CLEANUP_PUSH, - &cu, 0, 0, 0); - my_assert(res == 0); -} - - -void _pthread_cleanup_push_defer (struct _pthread_cleanup_buffer *__buffer, - void (*__routine) (void *), - void *__arg) -{ - /* As _pthread_cleanup_push, but first save the thread's original - cancellation type in __buffer and set it to Deferred. */ - int orig_ctype; - ensure_valgrind("_pthread_cleanup_push_defer"); - /* Set to Deferred, and put the old cancellation type in res. */ - my_assert(-1 != PTHREAD_CANCEL_DEFERRED); - my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); - my_assert(sizeof(struct _pthread_cleanup_buffer) >= sizeof(int)); - VALGRIND_MAGIC_SEQUENCE(orig_ctype, (-1) /* default */, - VG_USERREQ__SET_CANCELTYPE, - PTHREAD_CANCEL_DEFERRED, 0, 0, 0); - my_assert(orig_ctype != -1); - *((int*)(__buffer)) = orig_ctype; - /* Now push the cleanup. */ - _pthread_cleanup_push(NULL, __routine, __arg); -} - - -void _pthread_cleanup_pop (struct _pthread_cleanup_buffer *__buffer, - int __execute) -{ - int res; - CleanupEntry cu; - ensure_valgrind("_pthread_cleanup_push"); - cu.type = VgCt_None; /* paranoia */ - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__CLEANUP_POP, - &cu, 0, 0, 0); - my_assert(cu.type == VgCt_Function); - if (res == 0) { - /* pop succeeded */ - if (__execute) { - cu.data.function.fn ( cu.data.function.arg ); - } - return; - } - if (res == -1) { - /* stack underflow */ - return; - } - barf("_pthread_cleanup_pop"); -} - - -void _pthread_cleanup_pop_restore (struct _pthread_cleanup_buffer *__buffer, - int __execute) -{ - int orig_ctype, fake_ctype; - /* As _pthread_cleanup_pop, but after popping/running the handler, - restore the thread's original cancellation type from the first - word of __buffer. */ - _pthread_cleanup_pop(NULL, __execute); - orig_ctype = *((int*)(__buffer)); - my_assert(orig_ctype == PTHREAD_CANCEL_DEFERRED - || orig_ctype == PTHREAD_CANCEL_ASYNCHRONOUS); - my_assert(-1 != PTHREAD_CANCEL_DEFERRED); - my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); - my_assert(sizeof(struct _pthread_cleanup_buffer) >= sizeof(int)); - VALGRIND_MAGIC_SEQUENCE(fake_ctype, (-1) /* default */, - VG_USERREQ__SET_CANCELTYPE, - orig_ctype, 0, 0, 0); - my_assert(fake_ctype == PTHREAD_CANCEL_DEFERRED); -} - - -REGPARM(1) -void __pthread_register_cancel (__pthread_unwind_buf_t *__buf) -{ - int res; - CleanupEntry cu; - ensure_valgrind("__pthread_register_cancel"); - cu.type = VgCt_Longjmp; - cu.data.longjmp.ub = __buf; - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__CLEANUP_PUSH, - &cu, 0, 0, 0); - my_assert(res == 0); -} - - -REGPARM(1) -void __pthread_register_cancel_defer (__pthread_unwind_buf_t *__buf) -{ - /* As __pthread_register cancel, but save the thread's original - cancellation type and set it to Deferred. */ - int res; - CleanupEntry cu; - ensure_valgrind("__pthread_register_cancel_defer"); - cu.type = VgCt_Longjmp; - cu.data.longjmp.ub = __buf; - /* Set to Deferred, and save the old cancellation type. */ - my_assert(-1 != PTHREAD_CANCEL_DEFERRED); - my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); - my_assert(sizeof(struct _pthread_cleanup_buffer) >= sizeof(int)); - VALGRIND_MAGIC_SEQUENCE(cu.data.longjmp.ctype, (-1) /* default */, - VG_USERREQ__SET_CANCELTYPE, - PTHREAD_CANCEL_DEFERRED, 0, 0, 0); - my_assert(cu.data.longjmp.ctype != -1); - /* Now push the cleanup. */ - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__CLEANUP_PUSH, - &cu, 0, 0, 0); - my_assert(res == 0); -} - - -REGPARM(1) -void __pthread_unregister_cancel (__pthread_unwind_buf_t *__buf) -{ - int res; - CleanupEntry cu; - ensure_valgrind("__pthread_unregister_cancel"); - cu.type = VgCt_None; /* paranoia */ - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__CLEANUP_POP, - &cu, 0, 0, 0); - my_assert(cu.type == VgCt_Longjmp); - my_assert(cu.data.longjmp.ub == __buf); - return; -} - - -REGPARM(1) -void __pthread_unregister_restore (__pthread_unwind_buf_t *__buf) -{ - int res; - CleanupEntry cu; - int fake_ctype; - /* As __pthread_unregister_cancel, but after popping/running the - handler, restore the thread's original cancellation type. */ - ensure_valgrind("__pthread_unregister_cancel_restore"); - cu.type = VgCt_None; /* paranoia */ - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__CLEANUP_POP, - &cu, 0, 0, 0); - my_assert(cu.type == VgCt_Longjmp); - my_assert(cu.data.longjmp.ub == __buf); - /* Restore the original cancellation type. */ - my_assert(cu.data.longjmp.ctype == PTHREAD_CANCEL_DEFERRED - || cu.data.longjmp.ctype == PTHREAD_CANCEL_ASYNCHRONOUS); - my_assert(-1 != PTHREAD_CANCEL_DEFERRED); - my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); - VALGRIND_MAGIC_SEQUENCE(fake_ctype, (-1) /* default */, - VG_USERREQ__SET_CANCELTYPE, - cu.data.longjmp.ctype, 0, 0, 0); - my_assert(fake_ctype == PTHREAD_CANCEL_DEFERRED); - return; -} - -REGPARM(1) -__attribute ((__noreturn__)) -void __pthread_unwind (__pthread_unwind_buf_t *__buf) -{ - int res; - CleanupEntry cu; - while (1) { - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__CLEANUP_POP, - &cu, 0, 0, 0); - my_assert(res == 0); - if (cu.type == VgCt_Longjmp) break; - if (0) printf("running cleanup handler"); - my_assert(cu.type == VgCt_Function); - cu.data.function.fn ( cu.data.function.arg ); - } - my_assert(cu.type == VgCt_Longjmp); - my_assert(__buf == NULL || __buf == cu.data.longjmp.ub); - __buf = cu.data.longjmp.ub; - longjmp(__buf->__cancel_jmp_buf[0].__cancel_jmp_buf, 1); - /* NOTREACHED */ -} - - -REGPARM(1) -__attribute ((__noreturn__)) -void __pthread_unwind_next (__pthread_unwind_buf_t *__buf) -{ - __pthread_unwind(NULL); - /* NOTREACHED */ -} - - -/* --------------------------------------------------- - THREADs - ------------------------------------------------ */ - -static void __valgrind_pthread_yield ( void ) -{ - int res; - ensure_valgrind("pthread_yield"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_YIELD, 0, 0, 0, 0); -} - -WEAK -int pthread_yield ( void ) -{ - __valgrind_pthread_yield(); - return 0; -} - - -int pthread_equal(pthread_t thread1, pthread_t thread2) -{ - return thread1 == thread2 ? 1 : 0; -} - - -/* Bundle up the args into a malloc'd block and create a new thread - consisting of thread_wrapper() applied to said malloc'd block. */ -int -pthread_create (pthread_t *__restrict __thredd, - __const pthread_attr_t *__restrict __attr, - void *(*__start_routine) (void *), - void *__restrict __arg) -{ - int tid_child; - NewThreadInfo* info; - StackInfo si; - vg_pthread_attr_t* __vg_attr; - CONVERT(attr, __attr, __vg_attr); - - ensure_valgrind("pthread_create"); - - /* make sure the tsd keys, and hence locale info, for the root - thread are initialised before we get into complications making - new threads. */ - init_global_thread_specific_state(); - - /* Allocate space for the arg block. thread_wrapper will free - it. */ - info = my_malloc(sizeof(NewThreadInfo)); - my_assert(info != NULL); - - if (__vg_attr) - info->attr__detachstate = __vg_attr->__vg_detachstate; - else - info->attr__detachstate = PTHREAD_CREATE_JOINABLE; - - VGA_(thread_create)(&info->aux); - - info->root_fn = __start_routine; - info->arg = __arg; - sigprocmask(SIG_SETMASK, NULL, &info->sigmask); - - if (__attr) { - si.base = (Addr)__vg_attr->__vg_stackaddr; - si.size = __vg_attr->__vg_stacksize; - si.guardsize = __vg_attr->__vg_guardsize; - } else { - si.base = (Addr)NULL; - si.size = VG_PTHREAD_STACK_SIZE; - si.guardsize = VKI_PAGE_SIZE; - } - - VALGRIND_MAGIC_SEQUENCE(tid_child, VG_INVALID_THREADID /* default */, - VG_USERREQ__APPLY_IN_NEW_THREAD, - &thread_wrapper, info, &si, 0); - my_assert(tid_child != VG_INVALID_THREADID); - - if (__thredd) - *__thredd = tid_child; - - return 0; /* success */ -} - - -int -pthread_join (pthread_t __th, void **__thread_return) -{ - int res; - ensure_valgrind("pthread_join"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_JOIN, - __th, __thread_return, 0, 0); - return res; -} - - -void pthread_exit(void *retval) -{ - ensure_valgrind("pthread_exit"); - set_ret_val(retval); - __pthread_unwind(NULL); -} - - -int pthread_detach(pthread_t th) -{ - int res; - ensure_valgrind("pthread_detach"); - /* First we enquire as to the current detach state. */ - VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, - VG_USERREQ__SET_OR_GET_DETACH, - 2 /* get */, th, 0, 0); - if (res == -1) { - /* not found */ - pthread_error("pthread_detach: " - "invalid target thread"); - return ESRCH; - } - if (res == 1) { - /* already detached */ - pthread_error("pthread_detach: " - "target thread is already detached"); - return EINVAL; - } - if (res == 0) { - VALGRIND_MAGIC_SEQUENCE(res, (-2) /* default */, - VG_USERREQ__SET_OR_GET_DETACH, - 1 /* set */, th, 0, 0); - my_assert(res == 0); - return 0; - } - barf("pthread_detach"); -} - - -/* --------------------------------------------------- - MUTEX ATTRIBUTES - ------------------------------------------------ */ - -int __pthread_mutexattr_init(pthread_mutexattr_t *attr) -{ - vg_pthread_mutexattr_t* vg_attr; - CONVERT(mutexattr, attr, vg_attr); - vg_attr->__vg_mutexkind = PTHREAD_MUTEX_ERRORCHECK_NP; - return 0; -} - -int __pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) -{ - vg_pthread_mutexattr_t* vg_attr; - CONVERT(mutexattr, attr, vg_attr); - - switch (type) { -# ifndef GLIBC_2_1 - case PTHREAD_MUTEX_TIMED_NP: - case PTHREAD_MUTEX_ADAPTIVE_NP: -# endif -# ifdef GLIBC_2_1 - case PTHREAD_MUTEX_FAST_NP: -# endif - case PTHREAD_MUTEX_RECURSIVE_NP: - case PTHREAD_MUTEX_ERRORCHECK_NP: - vg_attr->__vg_mutexkind = type; - return 0; - default: - pthread_error("pthread_mutexattr_settype: " - "invalid type"); - return EINVAL; - } -} - -int __pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) -{ - vg_pthread_mutexattr_t* vg_attr; - CONVERT(mutexattr, attr, vg_attr); - - *type = vg_attr->__vg_mutexkind; - - return 0; -} - -int __pthread_mutexattr_destroy(pthread_mutexattr_t *attr) -{ - return 0; -} - -int __pthread_mutexattr_setpshared ( pthread_mutexattr_t* attr, int pshared) -{ - if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) - return EINVAL; - - /* For now it is not possible to shared a conditional variable. */ - if (pshared != PTHREAD_PROCESS_PRIVATE) - return ENOSYS; - - return 0; -} - - -/* --------------------------------------------------- - MUTEXes - ------------------------------------------------ */ - -int __pthread_mutex_init(pthread_mutex_t *mutex, - const pthread_mutexattr_t *mutexattr) -{ - vg_pthread_mutex_t* vg_mutex; - vg_pthread_mutexattr_t* vg_mutexattr; - CONVERT(mutex, mutex, vg_mutex); - CONVERT(mutexattr, mutexattr, vg_mutexattr); - - vg_mutex->__vg_m_count = 0; - vg_mutex->__vg_m_owner = (/*_pthread_descr*/void*)VG_INVALID_THREADID; - vg_mutex->__vg_m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; - if (vg_mutexattr) - vg_mutex->__vg_m_kind = vg_mutexattr->__vg_mutexkind; - return 0; -} - - -int __pthread_mutex_lock(pthread_mutex_t *mutex) -{ - int res; - vg_pthread_mutex_t* vg_mutex; - CONVERT(mutex, mutex, vg_mutex); - - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_MUTEX_LOCK, - vg_mutex, 0, 0, 0); - return res; -} - - -int __pthread_mutex_timedlock(pthread_mutex_t *mutex, - const struct timespec *abstime ) -{ - int res; - unsigned int ms_now, ms_end; - struct timeval timeval_now; - unsigned long long int ull_ms_now_after_1970; - unsigned long long int ull_ms_end_after_1970; - unsigned long long int ull_ms_now; - unsigned long long int ull_ms_end; - vg_pthread_mutex_t* vg_mutex; - CONVERT(mutex, mutex, vg_mutex); - - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - my_assert(ms_now != 0xFFFFFFFF); - res = gettimeofday(&timeval_now, NULL); - my_assert(res == 0); - - ull_ms_now_after_1970 - = 1000ULL * ((unsigned long long int)(timeval_now.tv_sec)) - + ((unsigned long long int)(timeval_now.tv_usec / 1000)); - ull_ms_end_after_1970 - = 1000ULL * ((unsigned long long int)(abstime->tv_sec)) - + ((unsigned long long int)(abstime->tv_nsec / 1000000)); - if (ull_ms_end_after_1970 < ull_ms_now_after_1970) - ull_ms_end_after_1970 = ull_ms_now_after_1970; - ull_ms_now = ((unsigned long long int)(ms_now)); - ull_ms_end = ull_ms_now + (ull_ms_end_after_1970 - ull_ms_now_after_1970); - if (ull_ms_end >= (unsigned long long int)(0xFFFFFFFFUL)) { - /* use 0xFFFFFFFEUL because 0xFFFFFFFFUL is reserved for no timeout - (the fine difference between a long wait and a possible abort - due to a detected deadlock). - */ - ms_end = 0xFFFFFFFEUL; - } else { - ms_end = (unsigned int)(ull_ms_end); - } - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_MUTEX_TIMEDLOCK, - vg_mutex, ms_end, 0, 0); - return res; -} - - -int __pthread_mutex_trylock(pthread_mutex_t *mutex) -{ - int res; - vg_pthread_mutex_t* vg_mutex; - CONVERT(mutex, mutex, vg_mutex); - - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_MUTEX_TRYLOCK, - vg_mutex, 0, 0, 0); - return res; -} - - -int __pthread_mutex_unlock(pthread_mutex_t *mutex) -{ - int res; - vg_pthread_mutex_t* vg_mutex; - CONVERT(mutex, mutex, vg_mutex); - - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_MUTEX_UNLOCK, - vg_mutex, 0, 0, 0); - return res; -} - - -int __pthread_mutex_destroy(pthread_mutex_t *mutex) -{ - vg_pthread_mutex_t* vg_mutex; - CONVERT(mutex, mutex, vg_mutex); - - /* Valgrind doesn't hold any resources on behalf of the mutex, so no - need to involve it. */ - if (vg_mutex->__vg_m_count > 0) { - /* Oh, the horror. glibc's internal use of pthreads "knows" - that destroying a lock does an implicit unlock. Make it - explicit. */ - __pthread_mutex_unlock( (pthread_mutex_t*)vg_mutex ); - pthread_error("pthread_mutex_destroy: mutex is still in use"); - return EBUSY; - } - vg_mutex->__vg_m_count = 0; - vg_mutex->__vg_m_owner = (/*_pthread_descr*/void*)VG_INVALID_THREADID; - vg_mutex->__vg_m_kind = PTHREAD_MUTEX_ERRORCHECK_NP; - return 0; -} - - -/* --------------------------------------------------- - CONDITION VARIABLES - ------------------------------------------------ */ - -/* LinuxThreads supports no attributes for conditions. Hence ... */ - -int pthread_condattr_init(pthread_condattr_t *attr) -{ - vg_pthread_condattr_t* vg_attr; - CONVERT(condattr, attr, vg_attr); - - vg_attr->__vg_pshared = 0; - return 0; -} - -int pthread_condattr_destroy(pthread_condattr_t *attr) -{ - return 0; -} - -int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) -{ - static int moans = N_MOANS; - vg_pthread_condattr_t* vg_attr; - CONVERT(condattr, attr, vg_attr); - - if (pshared != PTHREAD_PROCESS_PRIVATE && - pshared != PTHREAD_PROCESS_SHARED) - return EINVAL; - - if (pshared == PTHREAD_PROCESS_SHARED && moans-- > 0) - kludged("pthread_setschedparam", "(process shared condition variables not supported)"); - - vg_attr->__vg_pshared = pshared; - return 0; -} - -int pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared) -{ - vg_pthread_condattr_t* vg_attr; - CONVERT(condattr, attr, vg_attr); - - *pshared = vg_attr->__vg_pshared; - return 0; -} - -int pthread_cond_init( pthread_cond_t *cond, - const pthread_condattr_t *cond_attr) -{ - vg_pthread_cond_t* vg_cond; - CONVERT(cond, cond, vg_cond); - vg_cond->__vg_c_waiting = (/*_pthread_descr*/void*)VG_INVALID_THREADID; - return 0; -} - -int pthread_cond_destroy(pthread_cond_t *cond) -{ - /* should check that no threads are waiting on this CV */ - static int moans = N_MOANS; - if (moans-- > 0) - kludged("pthread_cond_destroy", - "(it doesn't check if the cond is waited on)" ); - return 0; -} - -/* --------------------------------------------------- - SCHEDULING - ------------------------------------------------ */ - -/* This is completely bogus. */ -int pthread_getschedparam(pthread_t target_thread, - int *policy, - struct sched_param *param) -{ - static int moans = N_MOANS; - if (moans-- > 0) - kludged("pthread_getschedparam", NULL); - if (policy) *policy = SCHED_OTHER; -# ifdef HAVE_SCHED_PRIORITY - if (param) param->sched_priority = 0; /* who knows */ -# else - if (param) param->__sched_priority = 0; /* who knows */ -# endif - return 0; -} - -int pthread_setschedparam(pthread_t target_thread, - int policy, - const struct sched_param *param) -{ - static int moans = N_MOANS; - if (moans-- > 0) - ignored("pthread_setschedparam", "(scheduling not changeable)"); - return 0; -} - -int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) -{ - int res; - vg_pthread_mutex_t* vg_mutex; - CONVERT(mutex, mutex, vg_mutex); - - ensure_valgrind("pthread_cond_wait"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_COND_WAIT, - cond, vg_mutex, 0, 0); - return res; -} - -int pthread_cond_timedwait ( pthread_cond_t *cond, - pthread_mutex_t *mutex, - const struct timespec *abstime ) -{ - int res; - unsigned int ms_now, ms_end; - struct timeval timeval_now; - unsigned long long int ull_ms_now_after_1970; - unsigned long long int ull_ms_end_after_1970; - unsigned long long int ull_ms_now; - unsigned long long int ull_ms_end; - vg_pthread_mutex_t* vg_mutex; - CONVERT(mutex, mutex, vg_mutex); - - ensure_valgrind("pthread_cond_timedwait"); - VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, - VG_USERREQ__READ_MILLISECOND_TIMER, - 0, 0, 0, 0); - my_assert(ms_now != 0xFFFFFFFF); - res = gettimeofday(&timeval_now, NULL); - my_assert(res == 0); - - ull_ms_now_after_1970 - = 1000ULL * ((unsigned long long int)(timeval_now.tv_sec)) - + ((unsigned long long int)(timeval_now.tv_usec / 1000)); - ull_ms_end_after_1970 - = 1000ULL * ((unsigned long long int)(abstime->tv_sec)) - + ((unsigned long long int)(abstime->tv_nsec / 1000000)); - if (ull_ms_end_after_1970 < ull_ms_now_after_1970) - ull_ms_end_after_1970 = ull_ms_now_after_1970; - ull_ms_now = ((unsigned long long int)(ms_now)); - ull_ms_end = ull_ms_now + (ull_ms_end_after_1970 - ull_ms_now_after_1970); - if (ull_ms_end >= (unsigned long long int)(0xFFFFFFFFUL)) { - /* use 0xFFFFFFFEUL because 0xFFFFFFFFUL is reserved for no timeout - (the fine difference between a long wait and a possible abort - due to a detected deadlock). - */ - ms_end = 0xFFFFFFFEUL; - } else { - ms_end = (unsigned int)(ull_ms_end); - } - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_COND_TIMEDWAIT, - cond, vg_mutex, ms_end, 0); - return res; -} - - -int pthread_cond_signal(pthread_cond_t *cond) -{ - int res; - ensure_valgrind("pthread_cond_signal"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_COND_SIGNAL, - cond, 0, 0, 0); - return res; -} - -int pthread_cond_broadcast(pthread_cond_t *cond) -{ - int res; - ensure_valgrind("pthread_cond_broadcast"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_COND_BROADCAST, - cond, 0, 0, 0); - return res; -} - - -/* --------------------------------------------------- - CANCELLATION - ------------------------------------------------ */ - -int pthread_setcancelstate(int state, int *oldstate) -{ - int res; - ensure_valgrind("pthread_setcancelstate"); - if (state != PTHREAD_CANCEL_ENABLE - && state != PTHREAD_CANCEL_DISABLE) { - pthread_error("pthread_setcancelstate: " - "invalid state"); - return EINVAL; - } - my_assert(-1 != PTHREAD_CANCEL_ENABLE); - my_assert(-1 != PTHREAD_CANCEL_DISABLE); - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__SET_CANCELSTATE, - state, 0, 0, 0); - my_assert(res != -1); - if (oldstate) - *oldstate = res; - return 0; -} - -int pthread_setcanceltype(int type, int *oldtype) -{ - int res; - ensure_valgrind("pthread_setcanceltype"); - if (type != PTHREAD_CANCEL_DEFERRED - && type != PTHREAD_CANCEL_ASYNCHRONOUS) { - pthread_error("pthread_setcanceltype: " - "invalid type"); - return EINVAL; - } - my_assert(-1 != PTHREAD_CANCEL_DEFERRED); - my_assert(-1 != PTHREAD_CANCEL_ASYNCHRONOUS); - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__SET_CANCELTYPE, - type, 0, 0, 0); - my_assert(res != -1); - if (oldtype) - *oldtype = res; - return 0; -} - -int pthread_cancel(pthread_t thread) -{ - int res; - ensure_valgrind("pthread_cancel"); - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__SET_CANCELPEND, - thread, &pthread_exit, 0, 0); - my_assert(res != -1); - return res; -} - -static -void __my_pthread_testcancel(void) -{ - int res; - ensure_valgrind("__my_pthread_testcancel"); - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__TESTCANCEL, - 0, 0, 0, 0); - my_assert(res == 0); -} - -void pthread_testcancel ( void ) -{ - __my_pthread_testcancel(); -} - - -/* Not really sure what this is for. I suspect for doing the POSIX - requirements for fork() and exec(). We do this internally anyway - whenever those syscalls are observed, so this could be superfluous, - but hey ... -*/ -void __pthread_kill_other_threads_np ( void ) -{ - int res; - ensure_valgrind("__pthread_kill_other_threads_np"); - VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, - VG_USERREQ__NUKE_OTHER_THREADS, - 0, 0, 0, 0); - my_assert(res == 0); -} - - -/* --------------------------------------------------- - SIGNALS - ------------------------------------------------ */ - -#include - -int pthread_sigmask(int how, const sigset_t *newmask, - sigset_t *oldmask) -{ - int res; - - /* A bit subtle, because the scheduler expects newmask and oldmask - to be vki_sigset_t* rather than sigset_t*, and the two are - different. Fortunately the first 64 bits of a sigset_t are - exactly a vki_sigset_t, so we just pass the pointers through - unmodified. Haaaack! - - Also mash the how value so that the SIG_ constants from glibc - constants to VKI_ constants, so that the former do not have to - be included into vg_scheduler.c. */ - - ensure_valgrind("pthread_sigmask"); - - switch (how) { - case SIG_SETMASK: how = VKI_SIG_SETMASK; break; - case SIG_BLOCK: how = VKI_SIG_BLOCK; break; - case SIG_UNBLOCK: how = VKI_SIG_UNBLOCK; break; - default: pthread_error("pthread_sigmask: invalid how"); - return EINVAL; - } - - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_SIGMASK, - how, newmask, oldmask, 0); - - /* The scheduler tells us of any memory violations. */ - return res == 0 ? 0 : EFAULT; -} - -int sigwait ( const sigset_t* set, int* sig ) -{ - int res; - siginfo_t si; - - __my_pthread_testcancel(); - - si.si_signo = 0; - res = sigtimedwait(set, &si, NULL); - *sig = si.si_signo; - - return 0; /* always returns 0 */ -} - - -int pthread_kill(pthread_t thread, int signo) -{ - int res; - ensure_valgrind("pthread_kill"); - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_KILL, - thread, signo, 0, 0); - return res; -} - - -/* Copied verbatim from Linuxthreads */ -/* Redefine raise() to send signal to calling thread only, - as per POSIX 1003.1c */ -int raise (int sig) -{ - int retcode = pthread_kill(pthread_self(), sig); - if (retcode == 0) { - return 0; - } else { - *(__errno_location()) = retcode; - return -1; - } -} - - - -/* --------------------------------------------------- - THREAD-SPECIFICs - ------------------------------------------------ */ - -static -int key_is_valid (pthread_key_t key) -{ - int res; - VALGRIND_MAGIC_SEQUENCE(res, 2 /* default */, - VG_USERREQ__PTHREAD_KEY_VALIDATE, - key, 0, 0, 0); - my_assert(res != 2); - return res; -} - - -/* Returns NULL if thread is invalid. Otherwise, if the thread - already has a specifics area, return that. Otherwise allocate it - one. */ -static -void** get_or_allocate_specifics_ptr ( pthread_t thread ) -{ - int res, i; - void** specifics_ptr; - ensure_valgrind("get_or_allocate_specifics_ptr"); - - /* Returns zero if the thread has no specific_ptr. One if thread - is invalid. Otherwise, the specific_ptr value. This is - allocated with my_malloc and so is aligned and cannot be - confused with 1 or 3. */ - VALGRIND_MAGIC_SEQUENCE(specifics_ptr, 3 /* default */, - VG_USERREQ__PTHREAD_GETSPECIFIC_PTR, - thread, 0, 0, 0); - my_assert(specifics_ptr != (void**)3); - - if (specifics_ptr == (void**)1) - return NULL; /* invalid thread */ - - if (specifics_ptr != NULL) - return specifics_ptr; /* already has a specifics ptr. */ - - /* None yet ... allocate a new one. Should never fail. */ - specifics_ptr = my_malloc( VG_N_THREAD_KEYS * sizeof(void*) ); - my_assert(specifics_ptr != NULL); - - VALGRIND_MAGIC_SEQUENCE(res, -1 /* default */, - VG_USERREQ__PTHREAD_SETSPECIFIC_PTR, - specifics_ptr, 0, 0, 0); - my_assert(res == 0); - - /* POSIX sez: "Upon thread creation, the value NULL shall be - associated with all defined keys in the new thread." This - allocation is in effect a delayed allocation of the specific - data for a thread, at its first-use. Hence we initialise it - here. */ - for (i = 0; i < VG_N_THREAD_KEYS; i++) { - specifics_ptr[i] = NULL; - } - - return specifics_ptr; -} - - -int __pthread_key_create(pthread_key_t *key, - void (*destr_function) (void *)) -{ - void** specifics_ptr; - int res, i; - ensure_valgrind("pthread_key_create"); - - /* This writes *key if successful. It should never fail. */ - VALGRIND_MAGIC_SEQUENCE(res, 1 /* default */, - VG_USERREQ__PTHREAD_KEY_CREATE, - key, destr_function, 0, 0); - - if (res == 0) { - /* POSIX sez: "Upon key creation, the value NULL shall be - associated with the new key in all active threads." */ - for (i = 0; i < VG_N_THREADS; i++) { - specifics_ptr = get_or_allocate_specifics_ptr(i); - /* we get NULL if i is an invalid thread. */ - if (specifics_ptr != NULL) - specifics_ptr[*key] = NULL; - } - } - - return res; -} - -int pthread_key_delete(pthread_key_t key) -{ - int res; - ensure_valgrind("pthread_key_delete"); - if (!key_is_valid(key)) - return EINVAL; - VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, - VG_USERREQ__PTHREAD_KEY_DELETE, - key, 0, 0, 0); - my_assert(res == 0); - return 0; -} - -int __pthread_setspecific(pthread_key_t key, const void *pointer) -{ - void** specifics_ptr; - ensure_valgrind("pthread_setspecific"); - - if (!key_is_valid(key)) - return EINVAL; - - specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); - specifics_ptr[key] = (void*)pointer; - return 0; -} - -void * __pthread_getspecific(pthread_key_t key) -{ - void** specifics_ptr; - ensure_valgrind("pthread_getspecific"); - - if (!key_is_valid(key)) - return NULL; - - specifics_ptr = get_or_allocate_specifics_ptr(pthread_self()); - return specifics_ptr[key]; -} - - -/* --------------------------------------------------- - ONCEry - ------------------------------------------------ */ - -/* This protects reads and writes of the once_control variable - supplied. It is never held whilst any particular initialiser is - running. */ -static pthread_mutex_t once_masterlock = PTHREAD_MUTEX_INITIALIZER; - -/* Initialiser needs to be run. */ -#define P_ONCE_NOT_DONE ((PTHREAD_ONCE_INIT) + 0) - -/* Initialiser currently running. */ -#define P_ONCE_RUNNING ((PTHREAD_ONCE_INIT) + 1) - -/* Initialiser has completed. */ -#define P_ONCE_COMPLETED ((PTHREAD_ONCE_INIT) + 2) - -/* Take the master lock */ -#define TAKE_LOCK \ - res = __pthread_mutex_lock(&once_masterlock); \ - my_assert(res == 0); - -/* Release the master lock */ -#define RELEASE_LOCK \ - res = __pthread_mutex_unlock(&once_masterlock); \ - my_assert(res == 0); - -static void once_cleanup(void *v) { - int res; - TAKE_LOCK; - *(pthread_once_t *)v = P_ONCE_NOT_DONE; - RELEASE_LOCK; -} - -int __pthread_once ( pthread_once_t *once_control, - void (*init_routine) (void) ) -{ - int res; - int done; - - ensure_valgrind("pthread_once"); - - /* Grab the lock transiently, so we can safely see what state this - once_control is in. */ - - TAKE_LOCK; - - switch (*once_control) { - - case P_ONCE_NOT_DONE: - /* Not started. Change state to indicate running, drop the - lock and run. */ - *once_control = P_ONCE_RUNNING; - _pthread_cleanup_push(NULL, once_cleanup, once_control); - RELEASE_LOCK; - init_routine(); - /* re-take the lock, and set state to indicate done. */ - TAKE_LOCK; - _pthread_cleanup_pop(NULL, False); - *once_control = P_ONCE_COMPLETED; - RELEASE_LOCK; - break; - - case P_ONCE_RUNNING: - /* This is the tricky case. The initialiser is running in - some other thread, but we have to delay this thread till - the other one completes. So we sort-of busy wait. In - fact it makes sense to yield now, because what we want to - happen is for the thread running the initialiser to - complete ASAP. */ - RELEASE_LOCK; - done = 0; - while (1) { - /* Let others run for a while. */ - __valgrind_pthread_yield(); - /* Grab the lock and see if we're done waiting. */ - TAKE_LOCK; - if (*once_control == P_ONCE_COMPLETED) - done = 1; - RELEASE_LOCK; - if (done) - break; - } - break; - - case P_ONCE_COMPLETED: - default: - /* Easy. It's already done. Just drop the lock. */ - RELEASE_LOCK; - break; - } - - return 0; -} - -#undef TAKE_LOCK -#undef RELEASE_LOCK -#undef P_ONCE_NOT_DONE -#undef P_ONCE_RUNNING -#undef P_ONCE_COMPLETED - - -/* --------------------------------------------------- - MISC - ------------------------------------------------ */ - -static pthread_mutex_t pthread_atfork_lock - = PTHREAD_MUTEX_INITIALIZER; - -int __pthread_atfork ( void (*prepare)(void), - void (*parent)(void), - void (*child)(void) ) -{ - int n, res; - ForkHandlerEntry entry; - - ensure_valgrind("pthread_atfork"); - __pthread_mutex_lock(&pthread_atfork_lock); - - /* Fetch old counter */ - VALGRIND_MAGIC_SEQUENCE(n, -2 /* default */, - VG_USERREQ__GET_FHSTACK_USED, - 0, 0, 0, 0); - my_assert(n >= 0 && n < VG_N_FORKHANDLERSTACK); - if (n == VG_N_FORKHANDLERSTACK-1) - barf("pthread_atfork: VG_N_FORKHANDLERSTACK is too low; " - "increase and recompile"); - - /* Add entry */ - entry.prepare = *prepare; - entry.parent = *parent; - entry.child = *child; - VALGRIND_MAGIC_SEQUENCE(res, -2 /* default */, - VG_USERREQ__SET_FHSTACK_ENTRY, - n, &entry, 0, 0); - my_assert(res == 0); - - /* Bump counter */ - VALGRIND_MAGIC_SEQUENCE(res, -2 /* default */, - VG_USERREQ__SET_FHSTACK_USED, - n+1, 0, 0, 0); - my_assert(res == 0); - - __pthread_mutex_unlock(&pthread_atfork_lock); - return 0; -} - - -#ifdef GLIBC_2_3 -/* This seems to be a hook which appeared in glibc-2.3.2. */ -int __register_atfork ( void (*prepare)(void), - void (*parent)(void), - void (*child)(void) ) -{ - return __pthread_atfork(prepare,parent,child); -} -#endif - -WEAK -void __pthread_initialize ( void ) -{ - ensure_valgrind("__pthread_initialize"); -} - - -/* --------------------------------------------------- - LIBRARY-PRIVATE THREAD SPECIFIC STATE - ------------------------------------------------ */ - -#include - -/* The allowable libc TSD keys (indices) from glibc source. */ -enum __libc_tsd_key_t { _LIBC_TSD_KEY_MALLOC = 0, - _LIBC_TSD_KEY_DL_ERROR, - _LIBC_TSD_KEY_RPC_VARS, - _LIBC_TSD_KEY_LOCALE, - _LIBC_TSD_KEY_CTYPE_B, - _LIBC_TSD_KEY_CTYPE_TOLOWER, - _LIBC_TSD_KEY_CTYPE_TOUPPER, - _LIBC_TSD_KEY_N }; - -typedef - struct { - void *ret_val; - int *errno_ptr; - int *h_errno_ptr; - struct __res_state *res_state_ptr; - int errno_data; - int h_errno_data; - struct __res_state res_state_data; - void *libc_specifics[_LIBC_TSD_KEY_N]; - } - ThreadSpecificState; - -static ThreadSpecificState thread_specific_state[VG_N_THREADS]; - -/* Auto-initialising subsystem. global_init_done is set - after initialisation. global_init_done_mx guards it. */ -static int global_init_done = 0; -static pthread_mutex_t global_init_done_mx = PTHREAD_MUTEX_INITIALIZER; - -static -void cleanup_root(void *arg) -{ - thread_exit_wrapper(get_ret_val()); - /* NOTREACHED */ -} - -static void __attribute__((constructor)) -init_global_thread_specific_state ( void ) -{ - int res; - - /* Don't fall into deadlock if we get called again whilst we still - hold the lock, via the __uselocale() call herein. */ - if (global_init_done != 0) - return; - - /* Take the lock. */ - res = __pthread_mutex_lock(&global_init_done_mx); - if (res != 0) barf("init_global_thread_specific_state: lock"); - - /* Now test again, to be sure there is no mistake. */ - if (global_init_done != 0) { - res = __pthread_mutex_unlock(&global_init_done_mx); - if (res != 0) barf("init_global_thread_specific_state: unlock(1)"); - return; - } - - /* assert that we are the root thread. */ - my_assert(pthread_self() == 1); - - /* Signify init done - we shouldn't really do this until after - the call to init_thread_specific_state() but that routine makes - a call to __uselocale() that may bring us back here as that - routine will call __libc_tsd_set() which will call us. - - We can get away with marking the init as done now because - the important bits of init_thread_specific_state() are done - before the call to __uselocale() is made. */ - global_init_done = 1; - - /* Initialise thread specific data for the root thread. */ - init_thread_specific_state(); - - /* Install a cleanup routine to handle the root thread exiting */ - _pthread_cleanup_push(NULL, cleanup_root, NULL); - - /* Unlock and return. */ - res = __pthread_mutex_unlock(&global_init_done_mx); - if (res != 0) barf("init_global_thread_specific_state: unlock"); -} - -static void -init_thread_specific_state ( void ) -{ - int tid = pthread_self(); - int i; - - /* No return value yet */ - thread_specific_state[tid].ret_val = NULL; - - /* Initialise the errno and resolver state pointers. */ - thread_specific_state[tid].errno_ptr = NULL; - thread_specific_state[tid].h_errno_ptr = NULL; - thread_specific_state[tid].res_state_ptr = NULL; - - /* Initialise the per-thread libc data. */ - for (i = 0; i < _LIBC_TSD_KEY_N; i++) { - thread_specific_state[tid].libc_specifics[i] = NULL; - } - -# ifdef GLIBC_2_3 - /* Set this thread's locale to the global (default) locale. A hack - in support of glibc-2.3. - */ - __uselocale(LC_GLOBAL_LOCALE); -# endif -} - -static void -set_ret_val ( void* ret_val ) -{ - int tid; - VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - thread_specific_state[tid].ret_val = ret_val; -} - -static void * -get_ret_val ( void ) -{ - int tid; - VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - return thread_specific_state[tid].ret_val; -} - -int* __errno_location ( void ) -{ - int tid; - - ensure_valgrind("__errno_location"); - VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - /* 'cos I'm paranoid ... */ - if (tid < 1 || tid >= VG_N_THREADS) - barf("__errno_location: invalid ThreadId"); - if (thread_specific_state[tid].errno_ptr == NULL) { - if (VGA_(has_tls)()) - thread_specific_state[tid].errno_ptr = dlsym(RTLD_DEFAULT, "errno"); - else if (tid == 1) - thread_specific_state[tid].errno_ptr = dlvsym(RTLD_DEFAULT, "errno", "GLIBC_2.0"); - else - thread_specific_state[tid].errno_ptr = &thread_specific_state[tid].errno_data; - } - return thread_specific_state[tid].errno_ptr; -} - -int* __h_errno_location ( void ) -{ - int tid; - /* ensure_valgrind("__h_errno_location"); */ - VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - /* 'cos I'm paranoid ... */ - if (tid < 1 || tid >= VG_N_THREADS) - barf("__h_errno_location: invalid ThreadId"); - if (thread_specific_state[tid].h_errno_ptr == NULL) { - if (VGA_(has_tls)()) - thread_specific_state[tid].h_errno_ptr = dlsym(RTLD_DEFAULT, "h_errno"); - else if (tid == 1) - thread_specific_state[tid].h_errno_ptr = dlvsym(RTLD_DEFAULT, "h_errno", "GLIBC_2.0"); - else - thread_specific_state[tid].h_errno_ptr = &thread_specific_state[tid].h_errno_data; - } - return thread_specific_state[tid].h_errno_ptr; -} - -struct __res_state* __res_state ( void ) -{ - int tid; - /* ensure_valgrind("__res_state"); */ - VALGRIND_MAGIC_SEQUENCE(tid, 1 /* default */, - VG_USERREQ__PTHREAD_GET_THREADID, - 0, 0, 0, 0); - /* 'cos I'm paranoid ... */ - if (tid < 1 || tid >= VG_N_THREADS) - barf("__res_state: invalid ThreadId"); - if (thread_specific_state[tid].res_state_ptr == NULL) { - if (VGA_(has_tls)()) { - struct __res_state **resp = dlsym(RTLD_DEFAULT, "__resp"); - - thread_specific_state[tid].res_state_ptr = *resp; - } else if (tid == 1) { - thread_specific_state[tid].res_state_ptr = dlvsym(RTLD_DEFAULT, "_res", "GLIBC_2.0"); - } else { - thread_specific_state[tid].res_state_ptr = &thread_specific_state[tid].res_state_data; - } - } - return thread_specific_state[tid].res_state_ptr; -} - - -/* --------------------------------------------------- - LIBC-PRIVATE SPECIFIC DATA - ------------------------------------------------ */ - -static int -libc_internal_tsd_set ( enum __libc_tsd_key_t key, - const void * pointer ) -{ - int tid = pthread_self(); - /* printf("SET SET SET key %d ptr %p\n", key, pointer); */ - if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) - barf("libc_internal_tsd_set: invalid key"); - init_global_thread_specific_state(); - thread_specific_state[tid].libc_specifics[key] = (void *)pointer; - return 0; -} - -static void * -libc_internal_tsd_get ( enum __libc_tsd_key_t key ) -{ - int tid = pthread_self(); - /* printf("GET GET GET key %d\n", key); */ - if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) - barf("libc_internal_tsd_get: invalid key"); - init_global_thread_specific_state(); - return thread_specific_state[tid].libc_specifics[key]; -} - - -int (*__libc_internal_tsd_set) - (enum __libc_tsd_key_t key, const void * pointer) - = libc_internal_tsd_set; - -void* (*__libc_internal_tsd_get) - (enum __libc_tsd_key_t key) - = libc_internal_tsd_get; - - -#ifdef GLIBC_2_3 -/* This one was first spotted be me in the glibc-2.2.93 sources. */ -static void** -libc_internal_tsd_address ( enum __libc_tsd_key_t key ) -{ - int tid = pthread_self(); - /* printf("ADDR ADDR ADDR key %d\n", key); */ - if (key < _LIBC_TSD_KEY_MALLOC || key >= _LIBC_TSD_KEY_N) - barf("libc_internal_tsd_address: invalid key"); - init_global_thread_specific_state(); - return &thread_specific_state[tid].libc_specifics[key]; -} - -void ** (*__libc_internal_tsd_address) - (enum __libc_tsd_key_t key) - = libc_internal_tsd_address; -#endif - - -/* --------------------------------------------------------------------- - These are here (I think) because they are deemed cancellation - points by POSIX. For the moment we'll simply pass the call along - to the corresponding thread-unaware (?) libc routine. - ------------------------------------------------------------------ */ - -static void *get_libc_handle(void) -{ - static void *libc_handle = NULL; - - if (libc_handle == NULL) { - libc_handle = dlopen("libc.so.6", RTLD_LAZY); - my_assert(libc_handle != NULL); - } - - return libc_handle; -} - -static void *get_libpthread_handle(void) -{ - static void *libpthread_handle = NULL; - - if (libpthread_handle == NULL) { - libpthread_handle = dlopen("libpthread.so.0", RTLD_LAZY); - my_assert(libpthread_handle != NULL); - } - - return libpthread_handle; -} - -#define FORWARD(name, args...) \ - ({ \ - static name##_t name##_ptr = NULL; \ - if (name##_ptr == NULL) { \ - name##_ptr = (name##_t)dlsym(get_libc_handle(), #name); \ - my_assert(name##_ptr != NULL && name##_ptr != dlsym(get_libpthread_handle(), #name)); \ - } \ - name##_ptr(args); \ - }) - -typedef -int (*sigaction_t) - (int signum, - const struct sigaction *act, - struct sigaction *oldact); -int sigaction(int signum, - const struct sigaction *act, - struct sigaction *oldact) -{ - __my_pthread_testcancel(); - return FORWARD(sigaction, signum, act, oldact); -} - -typedef -int (*accept_t)(int fd, struct sockaddr *addr, socklen_t *len); - -WEAK -int accept(int fd, struct sockaddr *addr, socklen_t *len) -{ - __my_pthread_testcancel(); - return FORWARD(accept, fd, addr, len); -} - -typedef -int (*connect_t)(int sockfd, - const struct sockaddr *serv_addr, - socklen_t addrlen); -WEAK -int connect(int sockfd, - const struct sockaddr *serv_addr, - socklen_t addrlen) -{ - __my_pthread_testcancel(); - return FORWARD(connect, sockfd, serv_addr, addrlen); -} - - -typedef -int (*fcntl_t)(int fd, int cmd, long arg); -WEAK -int fcntl(int fd, int cmd, long arg) -{ - __my_pthread_testcancel(); - return FORWARD(fcntl, fd, cmd, arg); -} - - -typedef -ssize_t (*write_t)(int fd, const void *buf, size_t count); -WEAK -ssize_t write(int fd, const void *buf, size_t count) -{ - __my_pthread_testcancel(); - return FORWARD(write, fd, buf, count); -} - - -typedef -ssize_t (*read_t)(int fd, void *buf, size_t count); -WEAK -ssize_t read(int fd, void *buf, size_t count) -{ - __my_pthread_testcancel(); - return FORWARD(read, fd, buf, count); -} - -typedef -int (*open64_t)(const char *pathname, int flags, mode_t mode); -/* WEAK */ -int open64(const char *pathname, int flags, mode_t mode) -{ - return FORWARD(open64, pathname, flags, mode); -} - -typedef -int (*open_t)(const char *pathname, int flags, mode_t mode); -/* WEAK */ -int open(const char *pathname, int flags, mode_t mode) -{ - return FORWARD(open, pathname, flags, mode); -} - -typedef -int (*close_t)(int fd); -WEAK -int close(int fd) -{ - __my_pthread_testcancel(); - return FORWARD(close, fd); -} - - -typedef -pid_t (*waitpid_t)(pid_t pid, int *status, int options); -WEAK -pid_t waitpid(pid_t pid, int *status, int options) -{ - __my_pthread_testcancel(); - return FORWARD(waitpid, pid, status, options); -} - - -typedef -int (*__nanosleep_t)(const struct timespec *req, struct timespec *rem); -WEAK -int __nanosleep(const struct timespec *req, struct timespec *rem) -{ - __my_pthread_testcancel(); - return FORWARD(__nanosleep, req, rem); -} - -typedef -int (*pause_t)(void); -WEAK -int pause(void) -{ - __my_pthread_testcancel(); - return FORWARD(pause); -} - - -typedef -int (*__tcdrain_t)(int fd); -WEAK -int __tcdrain(int fd) -{ - __my_pthread_testcancel(); - return FORWARD(__tcdrain, fd); -} - - -typedef -int (*fsync_t)(int fd); -WEAK -int fsync(int fd) -{ - __my_pthread_testcancel(); - return FORWARD(fsync, fd); -} - - -typedef -off_t (*lseek_t)(int fildes, off_t offset, int whence); -WEAK -off_t lseek(int fildes, off_t offset, int whence) -{ - __my_pthread_testcancel(); - return FORWARD(lseek, fildes, offset, whence); -} - - -typedef -__off64_t (*lseek64_t)(int fildes, __off64_t offset, int whence); -WEAK -__off64_t lseek64(int fildes, __off64_t offset, int whence) -{ - __my_pthread_testcancel(); - return FORWARD(lseek64, fildes, offset, whence); -} - - -typedef -ssize_t (*__pread64_t) (int __fd, void *__buf, size_t __nbytes, - __off64_t __offset); -ssize_t __pread64 (int __fd, void *__buf, size_t __nbytes, - __off64_t __offset) -{ - __my_pthread_testcancel(); - return FORWARD(__pread64, __fd, __buf, __nbytes, __offset); -} - - -typedef -ssize_t (*__pwrite64_t) (int __fd, const void *__buf, size_t __nbytes, - __off64_t __offset); -ssize_t __pwrite64 (int __fd, const void *__buf, size_t __nbytes, - __off64_t __offset) -{ - __my_pthread_testcancel(); - return FORWARD(__pwrite64, __fd, __buf, __nbytes, __offset); -} - - -typedef -ssize_t (*pwrite_t)(int fd, const void *buf, size_t count, off_t offset); -WEAK -ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) -{ - __my_pthread_testcancel(); - return FORWARD(pwrite, fd, buf, count, offset); -} - - -typedef -ssize_t (*pread_t)(int fd, void *buf, size_t count, off_t offset); -WEAK -ssize_t pread(int fd, void *buf, size_t count, off_t offset) -{ - __my_pthread_testcancel(); - return FORWARD(pread, fd, buf, count, offset); -} - -typedef -ssize_t (*recv_t)(int s, void *msg, size_t len, int flags); -WEAK -ssize_t recv(int s, void *msg, size_t len, int flags) -{ - __my_pthread_testcancel(); - return FORWARD(recv, s, msg, len, flags); -} - -typedef -ssize_t (*send_t)(int s, const void *msg, size_t len, int flags); -WEAK -ssize_t send(int s, const void *msg, size_t len, int flags) -{ - __my_pthread_testcancel(); - return FORWARD(send, s, msg, len, flags); -} - - -typedef -ssize_t (*sendmsg_t)(int s, const struct msghdr *msg, int flags); -WEAK -ssize_t sendmsg(int s, const struct msghdr *msg, int flags) -{ - __my_pthread_testcancel(); - return FORWARD(sendmsg, s, msg, flags); -} - - -typedef -ssize_t (*recvmsg_t)(int s, struct msghdr *msg, int flags); -WEAK -ssize_t recvmsg(int s, struct msghdr *msg, int flags) -{ - __my_pthread_testcancel(); - return FORWARD(recvmsg, s, msg, flags); -} - - -typedef -ssize_t (*recvfrom_t)(int s, void *buf, size_t len, int flags, - struct sockaddr *from, socklen_t *fromlen); -WEAK -ssize_t recvfrom(int s, void *buf, size_t len, int flags, - struct sockaddr *from, socklen_t *fromlen) -{ - __my_pthread_testcancel(); - return FORWARD(recvfrom, s, buf, len, flags, from, fromlen); -} - - -typedef -ssize_t (*sendto_t)(int s, const void *msg, size_t len, int flags, - const struct sockaddr *to, socklen_t tolen); -WEAK -ssize_t sendto(int s, const void *msg, size_t len, int flags, - const struct sockaddr *to, socklen_t tolen) -{ - __my_pthread_testcancel(); - return FORWARD(sendto, s, msg, len, flags, to, tolen); -} - - -typedef -int (*system_t)(const char* str); -WEAK -int system(const char* str) -{ - __my_pthread_testcancel(); - return FORWARD(system, str); -} - - -typedef -pid_t (*wait_t)(int *status); -WEAK -pid_t wait(int *status) -{ - __my_pthread_testcancel(); - return FORWARD(wait, status); -} - - -typedef -int (*msync_t)(const void *start, size_t length, int flags); -WEAK -int msync(const void *start, size_t length, int flags) -{ - __my_pthread_testcancel(); - return FORWARD(msync, start, length, flags); -} - -strong_alias(close, __close) -strong_alias(fcntl, __fcntl) -strong_alias(lseek, __lseek) -strong_alias(open, __open) -strong_alias(open64, __open64) -strong_alias(read, __read) -strong_alias(wait, __wait) -strong_alias(write, __write) -strong_alias(connect, __connect) -strong_alias(send, __send) -strong_alias(pause, __pause) - -weak_alias (__pread64, pread64) -weak_alias (__pwrite64, pwrite64) -weak_alias(__nanosleep, nanosleep) -weak_alias(__tcdrain, tcdrain) - - -typedef -void (*longjmp_t)(jmp_buf env, int val) __attribute((noreturn)); -/* not weak: WEAK */ -void longjmp(jmp_buf env, int val) -{ - FORWARD(longjmp, env, val); -} - - -typedef void (*siglongjmp_t) (sigjmp_buf env, int val) - __attribute__ ((noreturn)); -void siglongjmp(sigjmp_buf env, int val) -{ - kludged("siglongjmp", "(it ignores cleanup handlers)"); - FORWARD(siglongjmp, env, val); -} - - -/*--- fork and its helper ---*/ - -static -void run_fork_handlers ( int what ) -{ - ForkHandlerEntry entry; - int n_h, n_handlers, i, res; - - my_assert(what == 0 || what == 1 || what == 2); - - /* Fetch old counter */ - VALGRIND_MAGIC_SEQUENCE(n_handlers, -2 /* default */, - VG_USERREQ__GET_FHSTACK_USED, - 0, 0, 0, 0); - my_assert(n_handlers >= 0 && n_handlers < VG_N_FORKHANDLERSTACK); - - /* Prepare handlers (what == 0) are called in opposite order of - calls to pthread_atfork. Parent and child handlers are called - in the same order as calls to pthread_atfork. */ - if (what == 0) - n_h = n_handlers - 1; - else - n_h = 0; - - for (i = 0; i < n_handlers; i++) { - VALGRIND_MAGIC_SEQUENCE(res, -2 /* default */, - VG_USERREQ__GET_FHSTACK_ENTRY, - n_h, &entry, 0, 0); - my_assert(res == 0); - switch (what) { - case 0: if (entry.prepare) entry.prepare(); - n_h--; break; - case 1: if (entry.parent) entry.parent(); - n_h++; break; - case 2: if (entry.child) entry.child(); - n_h++; break; - default: barf("run_fork_handlers: invalid what"); - } - } - - if (what != 0 /* prepare */) { - /* Empty out the stack. */ - VALGRIND_MAGIC_SEQUENCE(res, -2 /* default */, - VG_USERREQ__SET_FHSTACK_USED, - 0, 0, 0, 0); - my_assert(res == 0); - } -} - -typedef -pid_t (*__fork_t)(void); -pid_t __fork(void) -{ - pid_t pid; - __my_pthread_testcancel(); - __pthread_mutex_lock(&pthread_atfork_lock); - - run_fork_handlers(0 /* prepare */); - pid = FORWARD(__fork); - if (pid == 0) { - /* I am the child */ - run_fork_handlers(2 /* child */); - __pthread_mutex_unlock(&pthread_atfork_lock); - __pthread_mutex_init(&pthread_atfork_lock, NULL); - } else { - /* I am the parent */ - run_fork_handlers(1 /* parent */); - __pthread_mutex_unlock(&pthread_atfork_lock); - } - return pid; -} - - -pid_t __vfork(void) -{ - return __fork(); -} - - - -/* --------------------------------------------------------------------- - Hacky implementation of semaphores. - ------------------------------------------------------------------ */ - -#include - -typedef - struct { - pthread_mutex_t se_mx; - pthread_cond_t se_cv; - int count; - int waiters; - } - vg_sem_t; - -#define SEM_CHECK_MAGIC 0x5b1d0772 - -typedef - struct { - union { - vg_sem_t* p; - int i; - } shadow; - int err_check; - } - user_sem_t; - - -static vg_sem_t* se_new ( sem_t* orig ) -{ - user_sem_t* u_sem = (user_sem_t*)orig; - vg_sem_t* vg_sem; - - vg_sem = my_malloc(sizeof(vg_sem_t)); - - u_sem->shadow.p = vg_sem; - u_sem->err_check = u_sem->shadow.i ^ SEM_CHECK_MAGIC; - - return vg_sem; -} - -static vg_sem_t* se_lookup ( sem_t* orig ) -{ - user_sem_t* u_sem = (user_sem_t*) orig; - - if(!u_sem->shadow.p || ((u_sem->shadow.i ^ SEM_CHECK_MAGIC) != u_sem->err_check)) - return NULL; - - return u_sem->shadow.p; -} - -static void se_free( sem_t* orig ) -{ - user_sem_t* u_sem = (user_sem_t*) orig; - - my_free(u_sem->shadow.p); - - u_sem->shadow.p = NULL; - u_sem->err_check = 0; - - return; -} - -int sem_init(sem_t *sem, int pshared, unsigned int value) -{ - int res; - vg_sem_t* vg_sem; - ensure_valgrind("sem_init"); - if (pshared != 0) { - pthread_error("sem_init: unsupported pshared value"); - *(__errno_location()) = ENOSYS; - return -1; - } - vg_sem = se_new(sem); - - res = pthread_mutex_init(&vg_sem->se_mx, NULL); - my_assert(res == 0); - res = pthread_cond_init(&vg_sem->se_cv, NULL); - my_assert(res == 0); - vg_sem->count = value; - vg_sem->waiters = 0; - return 0; -} - -int sem_wait ( sem_t* sem ) -{ - int res; - vg_sem_t* vg_sem; - ensure_valgrind("sem_wait"); - vg_sem = se_lookup(sem); - if(!vg_sem) { - pthread_error("sem_wait: semaphore overwritten or not initialized"); - *(__errno_location()) = EINVAL; - return -1; - } - res = __pthread_mutex_lock(&vg_sem->se_mx); - my_assert(res == 0); - while (vg_sem->count == 0) { - ++vg_sem->waiters; - res = pthread_cond_wait(&vg_sem->se_cv, &vg_sem->se_mx); - --vg_sem->waiters; - my_assert(res == 0); - } - vg_sem->count--; - res = __pthread_mutex_unlock(&vg_sem->se_mx); - my_assert(res == 0); - return 0; -} - -int sem_post ( sem_t* sem ) -{ - int res; - vg_sem_t* vg_sem; - ensure_valgrind("sem_post"); - vg_sem = se_lookup(sem); - if(!vg_sem) { - pthread_error("sem_post: semaphore overwritten or not initialized"); - *(__errno_location()) = EINVAL; - return -1; - } - res = __pthread_mutex_lock(&vg_sem->se_mx); - my_assert(res == 0); - if (vg_sem->count == 0) { - vg_sem->count++; - res = pthread_cond_broadcast(&vg_sem->se_cv); - my_assert(res == 0); - } else { - vg_sem->count++; - } - res = __pthread_mutex_unlock(&vg_sem->se_mx); - my_assert(res == 0); - return 0; -} - - -int sem_trywait ( sem_t* sem ) -{ - int ret, res; - vg_sem_t* vg_sem; - ensure_valgrind("sem_trywait"); - vg_sem = se_lookup(sem); - if(!vg_sem) { - pthread_error("sem_trywait: semaphore overwritten or not initialized"); - *(__errno_location()) = EINVAL; - return -1; - } - res = __pthread_mutex_lock(&vg_sem->se_mx); - my_assert(res == 0); - if (vg_sem->count > 0) { - vg_sem->count--; - ret = 0; - } else { - ret = -1; - *(__errno_location()) = EAGAIN; - } - res = __pthread_mutex_unlock(&vg_sem->se_mx); - my_assert(res == 0); - return ret; -} - - -int sem_getvalue(sem_t* sem, int * sval) -{ - int res; - vg_sem_t* vg_sem; - ensure_valgrind("sem_getvalue"); - vg_sem = se_lookup(sem); - if(!vg_sem) { - pthread_error("sem_getvalue: semaphore overwritten or not initialized"); - *(__errno_location()) = EINVAL; - return -1; - } - res = __pthread_mutex_lock(&vg_sem->se_mx); - my_assert(res == 0); - *sval = vg_sem->count; - res = __pthread_mutex_unlock(&vg_sem->se_mx); - my_assert(res == 0); - return 0; -} - - -int sem_destroy(sem_t * sem) -{ - /* if someone waiting on this semaphore, errno = EBUSY, return -1 */ - vg_sem_t* vg_sem; - int res; - ensure_valgrind("sem_destroy"); - vg_sem = se_lookup(sem); - if(!vg_sem) { - pthread_error("sem_destroy: semaphore overwritten or not initialized"); - *(__errno_location()) = EINVAL; - return -1; - } - res = __pthread_mutex_lock(&vg_sem->se_mx); - my_assert(res == 0); - if (vg_sem->waiters > 0) - { - *(__errno_location()) = EBUSY; - res = __pthread_mutex_unlock(&vg_sem->se_mx); - my_assert(res == 0); - return -1; - } - res = pthread_cond_destroy(&vg_sem->se_cv); - my_assert(res == 0); - res = __pthread_mutex_unlock(&vg_sem->se_mx); - my_assert(res == 0); - res = pthread_mutex_destroy(&vg_sem->se_mx); - my_assert(res == 0); - se_free(sem); - return 0; -} - - -int sem_timedwait(sem_t* sem, const struct timespec *abstime) -{ - int res; - vg_sem_t* vg_sem; - ensure_valgrind("sem_timedwait"); - vg_sem = se_lookup(sem); - if(!vg_sem) { - pthread_error("sem_timedwait: semaphore overwritten or not initialized"); - *(__errno_location()) = EINVAL; - return -1; - } - res = __pthread_mutex_lock(&vg_sem->se_mx); - my_assert(res == 0); - while ( vg_sem->count == 0 && res != ETIMEDOUT ) { - ++vg_sem->waiters; - res = pthread_cond_timedwait(&vg_sem->se_cv, &vg_sem->se_mx, abstime); - --vg_sem->waiters; - } - if ( vg_sem->count > 0 ) { - vg_sem->count--; - res = __pthread_mutex_unlock(&vg_sem->se_mx); - my_assert(res == 0 ); - return 0; - } else { - res = __pthread_mutex_unlock(&vg_sem->se_mx); - my_assert(res == 0 ); - *(__errno_location()) = ETIMEDOUT; - return -1; - } -} - - -/* --------------------------------------------------------------------- - Reader-writer locks. - ------------------------------------------------------------------ */ - -typedef - struct { - int prefer_w; /* != 0 --> prefer writer */ - int nwait_r; /* # of waiting readers */ - int nwait_w; /* # of waiting writers */ - pthread_cond_t cv_r; /* for signalling readers */ - pthread_cond_t cv_w; /* for signalling writers */ - pthread_mutex_t mx; - int status; - /* allowed range for status: >= -1. -1 means 1 writer currently - active, >= 0 means N readers currently active. */ - } - vg_rwlock_t; - - -static pthread_mutex_t rw_new_mx = PTHREAD_MUTEX_INITIALIZER; - -#define RWLOCK_CHECK_MAGIC 0xb5d17027 - - -static void init_vg_rwlock ( vg_rwlock_t* vg_rwl ) -{ - int res = 0; - vg_rwl->prefer_w = 1; - vg_rwl->nwait_r = 0; - vg_rwl->nwait_w = 0; - vg_rwl->status = 0; - res = pthread_mutex_init(&vg_rwl->mx, NULL); - res |= pthread_cond_init(&vg_rwl->cv_r, NULL); - res |= pthread_cond_init(&vg_rwl->cv_w, NULL); - my_assert(res == 0); -} - -static vg_rwlock_t* rw_new ( pthread_rwlock_t* orig ) -{ - int res; - vg_rwlock_t* rwl; - vg_pthread_rwlock_t* vg_orig; - CONVERT(rwlock, orig, vg_orig); - - res = __pthread_mutex_lock(&rw_new_mx); - my_assert(res == 0); - - rwl = my_malloc(sizeof(vg_rwlock_t)); - - vg_orig->__vg_rw_writer = rwl; - vg_orig->__vg_rw_read_waiting = (void *)((Addr)rwl ^ RWLOCK_CHECK_MAGIC); - - init_vg_rwlock(rwl); - if (vg_orig->__vg_rw_kind == PTHREAD_RWLOCK_PREFER_READER_NP) - rwl->prefer_w = 0; - - res = __pthread_mutex_unlock(&rw_new_mx); - my_assert(res == 0); - - return rwl; -} - -static vg_rwlock_t* rw_lookup ( pthread_rwlock_t* orig ) -{ - vg_rwlock_t* rwl; - vg_pthread_rwlock_t* vg_orig; - CONVERT(rwlock, orig, vg_orig); - - if (vg_orig->__vg_rw_writer == NULL) - rwl = rw_new ((pthread_rwlock_t*)vg_orig); - else if (((Addr)vg_orig->__vg_rw_writer ^ RWLOCK_CHECK_MAGIC) == (Addr)vg_orig->__vg_rw_read_waiting) - rwl = vg_orig->__vg_rw_writer; - else - rwl = NULL; - - return rwl; -} - -static void rw_free ( pthread_rwlock_t* orig ) -{ - int res; - vg_rwlock_t* rwl; - vg_pthread_rwlock_t* vg_orig; - CONVERT(rwlock, orig, vg_orig); - - rwl = vg_orig->__vg_rw_writer; - - vg_orig->__vg_rw_writer = NULL; - vg_orig->__vg_rw_read_waiting = NULL; - - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - - res = pthread_cond_destroy(&rwl->cv_w); - res |= pthread_cond_destroy(&rwl->cv_r); - res |= pthread_mutex_destroy(&rwl->mx); - my_assert(res == 0); - - my_free(rwl); - - return; -} - -int pthread_rwlock_init ( pthread_rwlock_t* orig, - const pthread_rwlockattr_t* attr ) -{ - vg_rwlock_t* rwl; - vg_pthread_rwlock_t* vg_orig; - vg_pthread_rwlockattr_t* vg_attr; - CONVERT(rwlock, orig, vg_orig); - CONVERT(rwlockattr, attr, vg_attr); - - if (0) printf ("pthread_rwlock_init\n"); - /* Install the lock preference; the remapper needs to know it. */ - if (vg_attr) - vg_orig->__vg_rw_kind = vg_attr->__vg_lockkind; - else - vg_orig->__vg_rw_kind = PTHREAD_RWLOCK_DEFAULT_NP; - /* Allocate the shadow */ - rwl = rw_new ((pthread_rwlock_t *)vg_orig); - return 0; -} - - -static -void pthread_rwlock_rdlock_CANCEL_HDLR ( void* rwl_v ) -{ - vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v; - rwl->nwait_r--; - pthread_mutex_unlock (&rwl->mx); -} - - -int pthread_rwlock_rdlock ( pthread_rwlock_t* orig ) -{ - int res; - vg_rwlock_t* rwl; - - if (0) printf ("pthread_rwlock_rdlock\n"); - rwl = rw_lookup (orig); - if(!rwl) { - pthread_error("pthread_rwlock_rdlock: lock overwritten or not initialized"); - return EINVAL; - } - res = __pthread_mutex_lock(&rwl->mx); - my_assert(res == 0); - if (rwl->status < 0) { - my_assert(rwl->status == -1); - rwl->nwait_r++; - pthread_cleanup_push( pthread_rwlock_rdlock_CANCEL_HDLR, rwl ); - while (1) { - if (rwl->status == 0) break; - res = pthread_cond_wait(&rwl->cv_r, &rwl->mx); - my_assert(res == 0); - } - pthread_cleanup_pop(0); - rwl->nwait_r--; - } - my_assert(rwl->status >= 0); - rwl->status++; - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return 0; -} - - -int pthread_rwlock_tryrdlock ( pthread_rwlock_t* orig ) -{ - int res; - vg_rwlock_t* rwl; - - if (0) printf ("pthread_rwlock_tryrdlock\n"); - rwl = rw_lookup (orig); - if(!rwl) { - pthread_error("pthread_rwlock_tryrdlock: lock overwritten or not initialized"); - return EINVAL; - } - res = __pthread_mutex_lock(&rwl->mx); - my_assert(res == 0); - if (rwl->status == -1) { - /* Writer active; we have to give up. */ - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return EBUSY; - } - /* Success */ - my_assert(rwl->status >= 0); - rwl->status++; - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return 0; -} - - -static -void pthread_rwlock_wrlock_CANCEL_HDLR ( void* rwl_v ) -{ - vg_rwlock_t* rwl = (vg_rwlock_t*)rwl_v; - rwl->nwait_w--; - pthread_mutex_unlock (&rwl->mx); -} - - -int pthread_rwlock_wrlock ( pthread_rwlock_t* orig ) -{ - int res; - vg_rwlock_t* rwl; - - if (0) printf ("pthread_rwlock_wrlock\n"); - rwl = rw_lookup (orig); - if(!rwl) { - pthread_error("pthread_rwlock_wrlock: lock overwritten or not initialized"); - return EINVAL; - } - res = __pthread_mutex_lock(&rwl->mx); - my_assert(res == 0); - if (rwl->status != 0) { - rwl->nwait_w++; - pthread_cleanup_push( pthread_rwlock_wrlock_CANCEL_HDLR, rwl ); - while (1) { - if (rwl->status == 0) break; - res = pthread_cond_wait(&rwl->cv_w, &rwl->mx); - my_assert(res == 0); - } - pthread_cleanup_pop(0); - rwl->nwait_w--; - } - my_assert(rwl->status == 0); - rwl->status = -1; - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return 0; -} - - -int pthread_rwlock_trywrlock ( pthread_rwlock_t* orig ) -{ - int res; - vg_rwlock_t* rwl; - if (0) printf ("pthread_wrlock_trywrlock\n"); - rwl = rw_lookup (orig); - if(!rwl) { - pthread_error("pthread_rwlock_trywrlock: lock overwritten or not initialized"); - return EINVAL; - } - res = __pthread_mutex_lock(&rwl->mx); - my_assert(res == 0); - if (rwl->status != 0) { - /* Reader(s) or a writer active; we have to give up. */ - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return EBUSY; - } - /* Success */ - my_assert(rwl->status == 0); - rwl->status = -1; - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return 0; -} - - -int pthread_rwlock_unlock ( pthread_rwlock_t* orig ) -{ - int res; - vg_rwlock_t* rwl; - if (0) printf ("pthread_rwlock_unlock\n"); - rwl = rw_lookup (orig); - if(!rwl) { - pthread_error("pthread_rwlock_unlock: lock overwritten or not initialized"); - return EINVAL; - } - res = __pthread_mutex_lock(&rwl->mx); - my_assert(res == 0); - if (rwl->status == 0) { - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return EPERM; - } - my_assert(rwl->status != 0); - if (rwl->status == -1) { - rwl->status = 0; - } else { - my_assert(rwl->status > 0); - rwl->status--; - } - - my_assert(rwl->status >= 0); - - if (rwl->prefer_w) { - - /* Favour waiting writers, if any. */ - if (rwl->nwait_w > 0) { - /* Writer(s) are waiting. */ - if (rwl->status == 0) { - /* We can let a writer in. */ - res = pthread_cond_signal(&rwl->cv_w); - my_assert(res == 0); - } else { - /* There are still readers active. Do nothing; eventually - they will disappear, at which point a writer will be - admitted. */ - } - } - else - /* No waiting writers. */ - if (rwl->nwait_r > 0) { - /* Let in a waiting reader. */ - res = pthread_cond_signal(&rwl->cv_r); - my_assert(res == 0); - } - - } else { - - /* Favour waiting readers, if any. */ - if (rwl->nwait_r > 0) { - /* Reader(s) are waiting; let one in. */ - res = pthread_cond_signal(&rwl->cv_r); - my_assert(res == 0); - } - else - /* No waiting readers. */ - if (rwl->nwait_w > 0 && rwl->status == 0) { - /* We have waiting writers and no active readers; let a - writer in. */ - res = pthread_cond_signal(&rwl->cv_w); - my_assert(res == 0); - } - } - - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return 0; -} - - -int pthread_rwlock_destroy ( pthread_rwlock_t *orig ) -{ - int res; - vg_rwlock_t* rwl; - if (0) printf ("pthread_rwlock_destroy\n"); - rwl = rw_lookup (orig); - if(!rwl) { - pthread_error("pthread_rwlock_destroy: lock overwritten or not initialized"); - return EINVAL; - } - res = __pthread_mutex_lock(&rwl->mx); - my_assert(res == 0); - if (rwl->status != 0 || rwl->nwait_r > 0 || rwl->nwait_w > 0) { - res = __pthread_mutex_unlock(&rwl->mx); - my_assert(res == 0); - return EBUSY; - } - rw_free (orig); - return 0; -} - - -/* Copied directly from LinuxThreads. */ -int -pthread_rwlockattr_init (pthread_rwlockattr_t *attr) -{ - vg_pthread_rwlockattr_t* vg_attr; - CONVERT(rwlockattr, attr, vg_attr); - vg_attr->__vg_lockkind = 0; - vg_attr->__vg_pshared = PTHREAD_PROCESS_PRIVATE; - return 0; -} - -/* Copied directly from LinuxThreads. */ -int -pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr) -{ - return 0; -} - -/* Copied directly from LinuxThreads. */ -int -pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) -{ - vg_pthread_rwlockattr_t* vg_attr; - CONVERT(rwlockattr, attr, vg_attr); - - if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED) - return EINVAL; - - /* For now it is not possible to shared a conditional variable. */ - if (pshared != PTHREAD_PROCESS_PRIVATE) - return ENOSYS; - - vg_attr->__vg_pshared = pshared; - - return 0; -} - - - -/* --------------------------------------------------------------------- - Manage the allocation and use of RT signals. The Valgrind core - uses one. glibc needs us to implement this to make RT signals - work; things just seem to crash if we don't. - ------------------------------------------------------------------ */ -int __libc_current_sigrtmin (void) -{ - int res; - - VALGRIND_MAGIC_SEQUENCE(res, 0, - VG_USERREQ__GET_SIGRT_MIN, - 0, 0, 0, 0); - - return res; -} - -int __libc_current_sigrtmax (void) -{ - int res; - - VALGRIND_MAGIC_SEQUENCE(res, 0, - VG_USERREQ__GET_SIGRT_MAX, - 0, 0, 0, 0); - - return res; -} - -int __libc_allocate_rtsig (int high) -{ - int res; - - VALGRIND_MAGIC_SEQUENCE(res, 0, - VG_USERREQ__ALLOC_RTSIG, - high, 0, 0, 0); - - return res; -} - -/* --------------------------------------------------------------------- - B'stard. - ------------------------------------------------------------------ */ -strong_alias(__pthread_mutex_lock, pthread_mutex_lock) -strong_alias(__pthread_mutex_timedlock, pthread_mutex_timedlock) -strong_alias(__pthread_mutex_trylock, pthread_mutex_trylock) -strong_alias(__pthread_mutex_unlock, pthread_mutex_unlock) -strong_alias(__pthread_mutexattr_init, pthread_mutexattr_init) - weak_alias(__pthread_mutexattr_settype, pthread_mutexattr_settype) - weak_alias(__pthread_mutexattr_gettype, pthread_mutexattr_gettype) - weak_alias(__pthread_mutexattr_setpshared, pthread_mutexattr_setpshared) -strong_alias(__pthread_mutex_init, pthread_mutex_init) -strong_alias(__pthread_mutexattr_destroy, pthread_mutexattr_destroy) -strong_alias(__pthread_mutex_destroy, pthread_mutex_destroy) -strong_alias(__pthread_once, pthread_once) -strong_alias(__pthread_atfork, pthread_atfork) -strong_alias(__pthread_key_create, pthread_key_create) -strong_alias(__pthread_getspecific, pthread_getspecific) -strong_alias(__pthread_setspecific, pthread_setspecific) - -#ifndef GLIBC_2_1 -strong_alias(sigaction, __sigaction) -#endif - -weak_alias(__fork, fork) -weak_alias(__vfork, vfork) -weak_alias (__pthread_kill_other_threads_np, pthread_kill_other_threads_np) - -/*--------------------------------------------------*/ - -weak_alias(pthread_rwlock_rdlock, __pthread_rwlock_rdlock) -weak_alias(pthread_rwlock_unlock, __pthread_rwlock_unlock) -weak_alias(pthread_rwlock_wrlock, __pthread_rwlock_wrlock) - -weak_alias(pthread_rwlock_destroy, __pthread_rwlock_destroy) -weak_alias(pthread_rwlock_init, __pthread_rwlock_init) -weak_alias(pthread_rwlock_tryrdlock, __pthread_rwlock_tryrdlock) -weak_alias(pthread_rwlock_trywrlock, __pthread_rwlock_trywrlock) - - -#ifndef __UCLIBC__ -/* These are called as part of stdio to lock the FILE structure for MT - programs. Unfortunately, the lock is not always a pthreads lock - - the NPTL version uses a lighter-weight lock which uses futex - directly (and uses a structure which is smaller than - pthread_mutex). So basically, this is completely broken on recent - glibcs. */ - -#undef _IO_flockfile -void _IO_flockfile ( _IO_FILE * file ) -{ - pthread_mutex_lock(file->_lock); -} -strong_alias(_IO_flockfile, __flockfile); -weak_alias(_IO_flockfile, flockfile); - -#undef _IO_funlockfile -void _IO_funlockfile ( _IO_FILE * file ) -{ - pthread_mutex_unlock(file->_lock); -} -strong_alias(_IO_funlockfile, __funlockfile); -weak_alias(_IO_funlockfile, funlockfile); -#endif - - -/* This doesn't seem to be needed to simulate libpthread.so's external - interface, but many people complain about its absence. */ - -strong_alias(__pthread_mutexattr_settype, __pthread_mutexattr_setkind_np) -weak_alias(__pthread_mutexattr_setkind_np, pthread_mutexattr_setkind_np) - -/*--------------------------------------------------------------------*/ -/*--- end ---*/ -/*--------------------------------------------------------------------*/ diff -puN coregrind/vg_main.c~new-threading coregrind/vg_main.c --- valgrind/coregrind/vg_main.c~new-threading 2005-01-08 17:18:27.000000000 -0800 +++ valgrind-jeremy/coregrind/vg_main.c 2005-01-11 10:13:13.000000000 -0800 @@ -126,11 +126,11 @@ const Char *VG_(libdir) = VG_LIBDIR; static Int vg_argc; static Char **vg_argv; -/* PID of the main thread */ -Int VG_(main_pid); - -/* PGRP of process */ -Int VG_(main_pgrp); +/* The master thread the one which will be responsible for mopping + everything up at exit. Normally it is tid 1, since that's the + first thread created, but it may be something else after a + fork(). */ +ThreadId VG_(master_tid) = VG_INVALID_THREADID; /* Application-visible file descriptor limits */ Int VG_(fd_soft_limit) = -1; @@ -222,7 +222,7 @@ static void print_all_stats ( void ) static Int ptrace_setregs(Int pid, ThreadId tid) { - if (VG_(is_running_thread)( tid )) + if (VG_(is_VCPU_thread)( tid )) return VGA_(ptrace_setregs_from_BB)(pid); else return VGA_(ptrace_setregs_from_tst)(pid, &VG_(threads)[tid].arch); @@ -346,15 +346,6 @@ void VG_(oynk) ( Int n ) OINK(n); } -/* Initialize the PID and PGRP of scheduler LWP; this is also called - in any new children after fork. */ -static void newpid(ThreadId unused) -{ - /* PID of scheduler LWP */ - VG_(main_pid) = VG_(getpid)(); - VG_(main_pgrp) = VG_(getpgrp)(); -} - /*====================================================================*/ /*=== Check we were launched by stage 1 ===*/ /*====================================================================*/ @@ -1506,7 +1497,6 @@ Bool VG_(clo_trace_syscalls) = False; Bool VG_(clo_trace_signals) = False; Bool VG_(clo_trace_symtab) = False; Bool VG_(clo_trace_sched) = False; -Int VG_(clo_trace_pthread_level) = 0; Int VG_(clo_dump_error) = 0; Int VG_(clo_backtrace_size) = 4; Char* VG_(clo_weird_hacks) = NULL; @@ -1519,10 +1509,6 @@ Bool VG_(clo_branchpred) = False; static Bool VG_(clo_wait_for_gdb) = False; -/* If we're doing signal routing, poll for signals every 50mS by - default. */ -Int VG_(clo_signal_polltime) = 50; - /* These flags reduce thread wakeup latency on syscall completion and signal delivery, respectively. The downside is possible unfairness. */ Bool VG_(clo_lowlat_syscalls) = False; /* low-latency syscalls */ @@ -1548,7 +1534,6 @@ void usage ( Bool debug_help ) " uncommon user options for all Valgrind tools:\n" " --run-libc-freeres=no|yes free up glibc memory at exit? [yes]\n" " --weird-hacks=hack1,hack2,... recognised hacks: lax-ioctls [none]\n" -" --signal-polltime=