|
@@ -135,7 +135,7 @@ int try_process_exit (int error_code, int term_signal)
|
|
|
struct shim_thread * cur_thread = get_cur_thread();
|
|
|
|
|
|
cur_thread->exit_code = -error_code;
|
|
|
- cur_process.exit_code = error_code;
|
|
|
+ cur_process.exit_code = term_signal ? term_signal : error_code;
|
|
|
cur_thread->term_signal = term_signal;
|
|
|
|
|
|
if (cur_thread->in_vm)
|
|
@@ -170,6 +170,15 @@ noreturn int shim_do_exit_group (int error_code)
|
|
|
struct shim_thread * cur_thread = get_cur_thread();
|
|
|
assert(!is_internal(cur_thread));
|
|
|
|
|
|
+ /* If exit_group() is invoked multiple times, only a single invocation proceeds past this
|
|
|
+ * point. Kill signals are delivered asynchronously, which will eventually kick the execution
|
|
|
+ * out of this loop.*/
|
|
|
+ static struct atomic_int first = ATOMIC_INIT(0);
|
|
|
+ if (atomic_cmpxchg(&first, 0, 1) == 1) {
|
|
|
+ while (1)
|
|
|
+ DkThreadYieldExecution();
|
|
|
+ }
|
|
|
+
|
|
|
if (debug_handle)
|
|
|
sysparser_printf("---- shim_exit_group (returning %d)\n", error_code);
|
|
|
|
|
@@ -183,6 +192,15 @@ noreturn int shim_do_exit_group (int error_code)
|
|
|
|
|
|
debug("now kill other threads in the process\n");
|
|
|
do_kill_proc(cur_thread->tgid, cur_thread->tgid, SIGKILL, false);
|
|
|
+ /* This loop ensures that the current thread, which issues exit_group(), wins in setting the
|
|
|
+ * process's exit code. try_process_exit() first sets the exit_code before updating the thread's
|
|
|
+ * state to "dead". Once check_last_thread() indicates that the current thread is the last
|
|
|
+ * thread, all the children will already have set thread->exit_code. Hence, this thread's
|
|
|
+ * execution of try_process_exit() gets to determine the final exit_code, which is the desired
|
|
|
+ * outcome. */
|
|
|
+ while (check_last_thread(cur_thread)) {
|
|
|
+ DkThreadYieldExecution();
|
|
|
+ }
|
|
|
|
|
|
debug("now exit the process\n");
|
|
|
try_process_exit(error_code, 0);
|