diff --git a/libc/Android.mk b/libc/Android.mk index 7b577affb..0b70e07b2 100644 --- a/libc/Android.mk +++ b/libc/Android.mk @@ -232,6 +232,7 @@ libc_upstream_freebsd_src_files := \ upstream-freebsd/lib/libc/stdlib/labs.c \ upstream-freebsd/lib/libc/stdlib/llabs.c \ upstream-freebsd/lib/libc/stdlib/qsort.c \ + upstream-freebsd/lib/libc/stdlib/quick_exit.c \ upstream-freebsd/lib/libc/stdlib/realpath.c \ upstream-freebsd/lib/libc/string/wcpcpy.c \ upstream-freebsd/lib/libc/string/wcpncpy.c \ diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h index 6c966f783..834dcda51 100644 --- a/libc/include/stdlib.h +++ b/libc/include/stdlib.h @@ -46,6 +46,11 @@ extern __noreturn void exit(int); extern __noreturn void _Exit(int); extern int atexit(void (*)(void)); +#if __ISO_C_VISIBLE >= 2011 || __cplusplus >= 201103L +int at_quick_exit(void (*)(void)); +void quick_exit(int) __noreturn; +#endif + extern char* getenv(const char*); extern int putenv(char*); extern int setenv(const char*, const char*, int); diff --git a/libc/upstream-freebsd/lib/libc/stdlib/quick_exit.c b/libc/upstream-freebsd/lib/libc/stdlib/quick_exit.c new file mode 100644 index 000000000..ef8cdb1b4 --- /dev/null +++ b/libc/upstream-freebsd/lib/libc/stdlib/quick_exit.c @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 2011 David Chisnall + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include + +/** + * Linked list of quick exit handlers. This is simpler than the atexit() + * version, because it is not required to support C++ destructors or + * DSO-specific cleanups. + */ +struct quick_exit_handler { + struct quick_exit_handler *next; + void (*cleanup)(void); +}; + +/** + * Lock protecting the handlers list. + */ +static pthread_mutex_t atexit_mutex = PTHREAD_MUTEX_INITIALIZER; +/** + * Stack of cleanup handlers. These will be invoked in reverse order when + */ +static struct quick_exit_handler *handlers; + +int +at_quick_exit(void (*func)(void)) +{ + struct quick_exit_handler *h; + + h = malloc(sizeof(*h)); + + if (NULL == h) + return (1); + h->cleanup = func; + pthread_mutex_lock(&atexit_mutex); + h->next = handlers; + handlers = h; + pthread_mutex_unlock(&atexit_mutex); + return (0); +} + +void +quick_exit(int status) +{ + struct quick_exit_handler *h; + + /* + * XXX: The C++ spec requires us to call std::terminate if there is an + * exception here. + */ + for (h = handlers; NULL != h; h = h->next) + h->cleanup(); + _Exit(status); +} diff --git a/tests/Android.mk b/tests/Android.mk index 94474ff74..fccf3f1d1 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -40,6 +40,9 @@ test_cflags = \ test_cflags += -D__STDC_LIMIT_MACROS # For glibc. +test_cppflags = \ + -std=gnu++11 \ + libBionicStandardTests_src_files := \ buffer_tests.cpp \ ctype_test.cpp \ @@ -91,6 +94,9 @@ libBionicStandardTests_src_files := \ libBionicStandardTests_cflags := \ $(test_cflags) \ +libBionicStandardTests_cppflags := \ + $(test_cppflags) \ + libBionicStandardTests_ldlibs_host := \ -lrt \ diff --git a/tests/stdlib_test.cpp b/tests/stdlib_test.cpp index bb395f04f..fc0f0e1a2 100644 --- a/tests/stdlib_test.cpp +++ b/tests/stdlib_test.cpp @@ -203,6 +203,52 @@ TEST(stdlib, strtold) { ASSERT_DOUBLE_EQ(1.23, strtold("1.23", NULL)); } +TEST(stdlib, quick_exit) { + pid_t pid = fork(); + ASSERT_NE(-1, pid) << strerror(errno); + + if (pid == 0) { + quick_exit(99); + } + + int status; + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(99, WEXITSTATUS(status)); +} + +static int quick_exit_status = 0; + +static void quick_exit_1(void) { + ASSERT_EQ(quick_exit_status, 0); + quick_exit_status = 1; +} + +static void quick_exit_2(void) { + ASSERT_EQ(quick_exit_status, 1); +} + +static void not_run(void) { + FAIL(); +} + +TEST(stdlib, at_quick_exit) { + pid_t pid = fork(); + ASSERT_NE(-1, pid) << strerror(errno); + + if (pid == 0) { + ASSERT_EQ(at_quick_exit(quick_exit_2), 0); + ASSERT_EQ(at_quick_exit(quick_exit_1), 0); + atexit(not_run); + quick_exit(99); + } + + int status; + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(99, WEXITSTATUS(status)); +} + TEST(unistd, _Exit) { int pid = fork(); ASSERT_NE(-1, pid) << strerror(errno);