#include "core.h"
#include "ume.h" /* for jmp_with_stack */
+#include "pub_core_debuglog.h"
#include "pub_core_aspacemgr.h"
#include "pub_core_sigframe.h"
#include "pub_core_syscalls.h"
return ((Addr)p) - tst->os_state.valgrind_stack_base;
}
+
+/* Run a thread all the way to the end, then do appropriate exit actions
+ (this is the last-one-out-turn-off-the-lights bit).
+*/
+static void run_a_thread_NORETURN ( Word tidW )
+{
+ ThreadId tid = (ThreadId)tidW;
+
+ VG_(debugLog)(1, "syscalls-x86-linux",
+ "run_a_thread_NORETURN(tid=%lld): "
+ "VGO_(thread_wrapper) called\n",
+ (ULong)tidW);
+
+ /* Run the thread all the way through. */
+ VgSchedReturnCode src = VGO_(thread_wrapper)(tid);
+
+ VG_(debugLog)(1, "syscalls-x86-linux",
+ "run_a_thread_NORETURN(tid=%lld): "
+ "VGO_(thread_wrapper) done\n",
+ (ULong)tidW);
+
+ Int c = VG_(count_living_threads)();
+ vg_assert(c >= 1); /* stay sane */
+
+ if (c == 1) {
+
+ VG_(debugLog)(1, "syscalls-x86-linux",
+ "run_a_thread_NORETURN(tid=%lld): "
+ "last one standing\n",
+ (ULong)tidW);
+
+ /* We are the last one standing. Keep hold of the lock and
+ carry on to show final tool results, then exit the entire system. */
+ VG_(shutdown_actions_NORETURN)(tid, src);
+
+ } else {
+
+ VG_(debugLog)(1, "syscalls-x86-linux",
+ "run_a_thread_NORETURN(tid=%lld): "
+ "not last one standing\n",
+ (ULong)tidW);
+
+ /* OK, thread is dead, but others still exist. Just exit. */
+ ThreadState *tst = VG_(get_ThreadState)(tid);
+
+ /* This releases the run lock */
+ VG_(exit_thread)(tid);
+ vg_assert(tst->status == VgTs_Zombie);
+
+ /* We have to use this sequence to terminate the thread to
+ prevent a subtle race. If VG_(exit_thread)() had left the
+ ThreadState as Empty, then it could have been reallocated,
+ reusing the stack while we're doing these last cleanups.
+ Instead, VG_(exit_thread) leaves it as Zombie to prevent
+ reallocation. We need to make sure we don't touch the stack
+ between marking it Empty and exiting. Hence the
+ assembler. */
+ asm volatile (
+ "movl %1, %0\n" /* set tst->status = VgTs_Empty */
+ "movq %2, %%rax\n" /* set %rax = __NR_exit */
+ "movq %3, %%rdi\n" /* set %rdi = tst->os_state.exitcode */
+ "syscall\n" /* exit(tst->os_state.exitcode) */
+ : "=m" (tst->status)
+ : "n" (VgTs_Empty), "n" (__NR_exit), "m" (tst->os_state.exitcode));
+
+ VG_(core_panic)("Thread exit failed?\n");
+ }
+
+ /*NOTREACHED*/
+ vg_assert(0);
+}
+
+
/*
- Allocate a stack for the main thread, and call VGA_(thread_wrapper)
- on that stack.
- */
-void VGA_(main_thread_wrapper)(ThreadId tid)
+ Allocate a stack for the main thread, and run it all the way to the
+ end.
+*/
+void VGP_(main_thread_wrapper_NORETURN)(ThreadId tid)
{
+ VG_(debugLog)(1, "syscalls-amd64-linux",
+ "entering VGP_(main_thread_wrapper_NORETURN)\n");
+
UWord* rsp = allocstack(tid);
- vg_assert(tid == VG_(master_tid));
+ /* shouldn't be any other threads around yet */
+ vg_assert( VG_(count_living_threads)() == 1 );
call_on_new_stack_0_1(
- (Addr)rsp, /* stack */
- 0, /*bogus return address*/
- VGA_(thread_wrapper), /* fn to call */
- (Word)tid /* arg to give it */
+ (Addr)rsp, /* stack */
+ 0, /*bogus return address*/
+ run_a_thread_NORETURN, /* fn to call */
+ (Word)tid /* arg to give it */
);
/*NOTREACHED*/
vg_assert(0);
}
-static Int start_thread(void *arg)
+
+static Int start_thread_NORETURN ( void* arg )
{
- ThreadState *tst = (ThreadState *)arg;
- ThreadId tid = tst->tid;
-
- VGA_(thread_wrapper)(tid);
-
- /* OK, thread is dead; this releases the run lock */
- VG_(exit_thread)(tid);
-
- vg_assert(tst->status == VgTs_Zombie);
-
- /* Poke the reaper */
- if (VG_(clo_trace_signals))
- VG_(message)(Vg_DebugMsg, "Sending SIGVGCHLD to master tid=%d lwp=%d",
- VG_(master_tid), VG_(threads)[VG_(master_tid)].os_state.lwpid);
-
- VG_(tkill)(VG_(threads)[VG_(master_tid)].os_state.lwpid, VKI_SIGVGCHLD);
-
- /* We have to use this sequence to terminate the thread to prevent
- a subtle race. If VG_(exit_thread)() had left the ThreadState
- as Empty, then it could have been reallocated, reusing the stack
- while we're doing these last cleanups. Instead,
- VG_(exit_thread) leaves it as Zombie to prevent reallocation.
- We need to make sure we don't touch the stack between marking it
- Empty and exiting. Hence the assembler. */
- asm volatile (
- "movl %1, %0\n" /* set tst->status = VgTs_Empty */
- "movq %2, %%rax\n" /* set %rax = __NR_exit */
- "movq %3, %%rdi\n" /* set %rdi = tst->os_state.exitcode */
- "syscall\n" /* exit(tst->os_state.exitcode) */
- : "=m" (tst->status)
- : "n" (VgTs_Empty), "n" (__NR_exit), "m" (tst->os_state.exitcode));
-
- VG_(core_panic)("Thread exit failed?\n");
+ ThreadState* tst = (ThreadState*)arg;
+ ThreadId tid = tst->tid;
+
+ run_a_thread_NORETURN ( (Word)tid );
+ /*NOTREACHED*/
+ vg_assert(0);
}
+
/* ---------------------------------------------------------------------
clone() handling
------------------------------------------------------------------ */
VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
/* Create the new thread */
- ret = VG_(clone)(start_thread, stack, flags, &VG_(threads)[ctid],
+ ret = VG_(clone)(start_thread_NORETURN, stack, flags, &VG_(threads)[ctid],
child_tidptr, parent_tidptr, NULL);
VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);