/* Copyright (C) 2014 Stony Brook University This file is part of Graphene Library OS. Graphene Library OS is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Graphene Library OS 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ /* * shim_exit.c * * Implementation of system call "exit" and "exit_group". */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include void release_robust_list (struct robust_list_head * head); int thread_exit(struct shim_thread* self, bool send_ipc) { bool sent_exit_msg = false; /* Chia-Che: Broadcast exit message as early as possible, so other process can start early on responding. */ if (self->in_vm && send_ipc) { ipc_cld_exit_send(self->ppid, self->tid, self->exit_code, self->term_signal); sent_exit_msg = true; } lock(&self->lock); if (!self->is_alive) { debug("thread %d is dead\n", self->tid); out: unlock(&self->lock); return 0; } #ifdef PROFILE self->exit_time = GET_PROFILE_INTERVAL(); #endif int exit_code = self->exit_code; if (!self->in_vm) { /* thread is in another process, we cannot rely on Async Helper thread to clean it */ self->is_alive = false; } if (is_internal(self)) goto out; struct shim_handle_map * handle_map = self->handle_map; struct shim_handle * exec = self->exec; struct shim_thread * parent = self->parent; self->handle_map = NULL; self->exec = NULL; if (parent) { assert(parent != self); assert(parent->child_exit_event); debug("thread exits, notifying thread %d\n", parent->tid); lock(&parent->lock); LISTP_DEL_INIT(self, &parent->children, siblings); LISTP_ADD_TAIL(self, &parent->exited_children, siblings); if (!self->in_vm) { debug("deliver SIGCHLD (thread = %d, exitval = %d)\n", self->tid, exit_code); siginfo_t info; memset(&info, 0, sizeof(siginfo_t)); info.si_signo = SIGCHLD; info.si_pid = self->tid; info.si_uid = self->uid; info.si_status = (exit_code & 0xff) << 8; append_signal(parent, SIGCHLD, &info, true); } unlock(&parent->lock); DkEventSet(parent->child_exit_event); } else if (!sent_exit_msg) { debug("parent not here, need to tell another process\n"); ipc_cld_exit_send(self->ppid, self->tid, self->exit_code, self->term_signal); } struct robust_list_head* robust_list = self->robust_list; self->robust_list = NULL; unlock(&self->lock); if (handle_map) put_handle_map(handle_map); if (exec) put_handle(exec); if (robust_list) release_robust_list(robust_list); DkEventSet(self->exit_event); return 0; } /* note that term_signal argument may contain WCOREDUMP bit (0x80) */ noreturn void thread_or_process_exit(int error_code, int term_signal) { struct shim_thread * cur_thread = get_cur_thread(); cur_thread->exit_code = -error_code; cur_thread->term_signal = term_signal; if (cur_thread->in_vm) thread_exit(cur_thread, true); if (check_last_thread(cur_thread)) { /* ask Async Helper thread to cleanup this thread */ cur_thread->clear_child_tid_pal = 1; /* any non-zero value suffices */ int64_t ret = install_async_event(NULL, 0, &cleanup_thread, cur_thread); if (ret < 0) { debug("failed to set up async cleanup_thread (exiting without clear child tid)," " return code: %ld\n", ret); DkThreadExit(NULL); } DkThreadExit(&cur_thread->clear_child_tid_pal); } struct shim_thread * async_thread = terminate_async_helper(); if (async_thread) /* TODO: wait for the thread to exit in host. * This is tracked by the following issue. * https://github.com/oscarlab/graphene/issues/440 */ put_thread(async_thread); /* free resources of the thread */ struct shim_thread * ipc_thread = terminate_ipc_helper(); if (ipc_thread) /* TODO: wait for the thread to exit in host. * This is tracked by the following issue. * https://github.com/oscarlab/graphene/issues/440 */ put_thread(ipc_thread); /* free resources of the thread */ shim_clean_and_exit(term_signal ? term_signal : error_code); } noreturn int shim_do_exit_group (int error_code) { INC_PROFILE_OCCURENCE(syscall_use_ipc); 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); #ifndef ALIAS_VFORK_AS_FORK if (cur_thread->dummy) { cur_thread->term_signal = 0; thread_exit(cur_thread, true); switch_dummy_thread(cur_thread); } #endif debug("now kill other threads in the process\n"); do_kill_proc(cur_thread->tgid, cur_thread->tgid, SIGKILL, false); while (check_last_thread(cur_thread)) { DkThreadYieldExecution(); } debug("now exit the process\n"); #ifdef PROFILE if (ENTER_TIME) SAVE_PROFILE_INTERVAL_SINCE(syscall_exit_group, ENTER_TIME); #endif thread_or_process_exit(error_code, 0); } noreturn int shim_do_exit (int error_code) { INC_PROFILE_OCCURENCE(syscall_use_ipc); struct shim_thread * cur_thread = get_cur_thread(); __UNUSED(cur_thread); assert(!is_internal(cur_thread)); if (debug_handle) sysparser_printf("---- shim_exit (returning %d)\n", error_code); #ifndef ALIAS_VFORK_AS_FORK if (cur_thread->dummy) { cur_thread->term_signal = 0; thread_exit(cur_thread, true); switch_dummy_thread(cur_thread); } #endif #ifdef PROFILE if (ENTER_TIME) SAVE_PROFILE_INTERVAL_SINCE(syscall_exit, ENTER_TIME); #endif thread_or_process_exit(error_code, 0); }