diff options
24 files changed, 1290 insertions, 135 deletions
diff --git a/benchmarks/Android.bp b/benchmarks/Android.bp index 06c0ba384..370e040ef 100644 --- a/benchmarks/Android.bp +++ b/benchmarks/Android.bp @@ -61,8 +61,8 @@ cc_defaults { } // Build benchmarks for the device (with bionic's .so). Run with: -// adb shell bionic-benchmarks32 -// adb shell bionic-benchmarks64 +// adb shell /data/benchmarktest/bionic-benchmarks/bionic-benchmarks +// adb shell /data/benchmarktest64/bionic-benchmarks/bionic-benchmarks cc_benchmark { name: "bionic-benchmarks", defaults: ["bionic-benchmarks-defaults"], diff --git a/docs/status.md b/docs/status.md index d6a2f4c0a..e1a5d349d 100644 --- a/docs/status.md +++ b/docs/status.md @@ -37,6 +37,9 @@ list of POSIX functions implemented by glibc but not by bionic. Current libc symbols: https://android.googlesource.com/platform/bionic/+/master/libc/libc.map.txt +New libc functions in R (API level 30): + * Full C11 `<threads.h>` (available as inlines for older API levels). + New libc functions in Q (API level 29): * `timespec_get` (C11 `<time.h>` addition) * `reallocarray` (BSD/GNU extension in `<malloc.h>` and `<stdlib.h>`) diff --git a/libc/Android.bp b/libc/Android.bp index d8a1d70d5..174783fa6 100644 --- a/libc/Android.bp +++ b/libc/Android.bp @@ -762,12 +762,13 @@ cc_library_static { "arch-arm/generic/bionic/strlen.c", "arch-arm/bionic/__aeabi_read_tp.S", - "arch-arm/bionic/atomics_arm.c", "arch-arm/bionic/__bionic_clone.S", + "arch-arm/bionic/__restore.S", "arch-arm/bionic/_exit_with_stack_teardown.S", + "arch-arm/bionic/atomics_arm.c", + "arch-arm/bionic/bpabi.c", "arch-arm/bionic/libcrt_compat.c", "arch-arm/bionic/popcount_tab.c", - "arch-arm/bionic/__restore.S", "arch-arm/bionic/setjmp.S", "arch-arm/bionic/syscall.S", "arch-arm/bionic/vfork.S", @@ -1153,6 +1154,7 @@ cc_library_static { "bionic/tdestroy.cpp", "bionic/termios.cpp", "bionic/thread_private.cpp", + "bionic/threads.cpp", "bionic/timespec_get.cpp", "bionic/tmpfile.cpp", "bionic/umount.cpp", @@ -1461,12 +1463,7 @@ cc_library_static { srcs: ["arch-x86/dynamic_function_dispatch.cpp"], }, arm: { - srcs: [ - "arch-arm/dynamic_function_dispatch.cpp", - - // Workaround for b/120254692. - "arch-arm/dynamic_function_wrapper.S", - ], + srcs: ["arch-arm/dynamic_function_dispatch.cpp"], }, }, diff --git a/libc/arch-arm/bionic/bpabi.c b/libc/arch-arm/bionic/bpabi.c new file mode 100644 index 000000000..5c9cd99c6 --- /dev/null +++ b/libc/arch-arm/bionic/bpabi.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +extern long long __divdi3(long long, long long); +extern unsigned long long __udivdi3(unsigned long long, unsigned long long); + +long long __gnu_ldivmod_helper(long long a, long long b, long long* remainder) { + long long quotient = __divdi3(a, b); + *remainder = a - b * quotient; + return quotient; +} + +unsigned long long __gnu_uldivmod_helper(unsigned long long a, unsigned long long b, + unsigned long long* remainder) { + unsigned long long quotient = __udivdi3(a, b); + *remainder = a - b * quotient; + return quotient; +} diff --git a/libc/arch-arm/dynamic_function_dispatch.cpp b/libc/arch-arm/dynamic_function_dispatch.cpp index 09fd8f30e..640f3306a 100644 --- a/libc/arch-arm/dynamic_function_dispatch.cpp +++ b/libc/arch-arm/dynamic_function_dispatch.cpp @@ -89,15 +89,11 @@ static int ifunc_close(int fd) { return r0; } -#define DEFINE_IFUNC_WITH_SUFFIX(name, suffix) \ - name##_func name##suffix __attribute__((ifunc(#name "_resolver"))); \ +#define DEFINE_IFUNC(name) \ + name##_func name __attribute__((ifunc(#name "_resolver"))); \ __attribute__((visibility("hidden"))) \ name##_func* name##_resolver() -#define DEFINE_IFUNC(name) DEFINE_IFUNC_WITH_SUFFIX(name, ) - -#define DEFINE_INTERNAL_IFUNC(name) DEFINE_IFUNC_WITH_SUFFIX(name, _internal) - #define DECLARE_FUNC(type, name) \ __attribute__((visibility("hidden"))) \ type name @@ -291,7 +287,7 @@ DEFINE_IFUNC(__strcat_chk) { } typedef int strcmp_func(const char* __lhs, const char* __rhs); -DEFINE_INTERNAL_IFUNC(strcmp) { +DEFINE_IFUNC(strcmp) { switch(get_cpu_variant()) { case kCortexA9: RETURN_FUNC(strcmp_func, strcmp_a9); @@ -305,7 +301,7 @@ DEFINE_INTERNAL_IFUNC(strcmp) { } typedef size_t strlen_func(const char* __s); -DEFINE_INTERNAL_IFUNC(strlen) { +DEFINE_IFUNC(strlen) { switch(get_cpu_variant()) { case kCortexA9: RETURN_FUNC(strlen_func, strlen_a9); diff --git a/libc/bionic/grp_pwd.cpp b/libc/bionic/grp_pwd.cpp index dadda49cb..ca6ea220c 100644 --- a/libc/bionic/grp_pwd.cpp +++ b/libc/bionic/grp_pwd.cpp @@ -163,37 +163,19 @@ static group* android_iinfo_to_group(group_state_t* state, return gr; } -static passwd* android_id_to_passwd(passwd_state_t* state, unsigned id) { +static const android_id_info* find_android_id_info(unsigned id) { for (size_t n = 0; n < android_id_count; ++n) { if (android_ids[n].aid == id) { - return android_iinfo_to_passwd(state, android_ids + n); + return &android_ids[n]; } } return nullptr; } -static passwd* android_name_to_passwd(passwd_state_t* state, const char* name) { +static const android_id_info* find_android_id_info(const char* name) { for (size_t n = 0; n < android_id_count; ++n) { if (!strcmp(android_ids[n].name, name)) { - return android_iinfo_to_passwd(state, android_ids + n); - } - } - return nullptr; -} - -static group* android_id_to_group(group_state_t* state, unsigned id) { - for (size_t n = 0; n < android_id_count; ++n) { - if (android_ids[n].aid == id) { - return android_iinfo_to_group(state, android_ids + n); - } - } - return nullptr; -} - -static group* android_name_to_group(group_state_t* state, const char* name) { - for (size_t n = 0; n < android_id_count; ++n) { - if (!strcmp(android_ids[n].name, name)) { - return android_iinfo_to_group(state, android_ids + n); + return &android_ids[n]; } } return nullptr; @@ -332,15 +314,9 @@ static id_t app_id_from_name(const char* name, bool is_group) { } else if (end[1] == 'i' && isdigit(end[2])) { // end will point to \0 if the strtoul below succeeds. appid = strtoul(end+2, &end, 10) + AID_ISOLATED_START; - } else { - for (size_t n = 0; n < android_id_count; n++) { - if (!strcmp(android_ids[n].name, end + 1)) { - appid = android_ids[n].aid; - // Move the end pointer to the null terminator. - end += strlen(android_ids[n].name) + 1; - break; - } - } + } else if (auto* android_id_info = find_android_id_info(end + 1); android_id_info != nullptr) { + appid = android_id_info->aid; + end += strlen(android_id_info->name) + 1; } // Check that the entire string was consumed by one of the 3 cases above. @@ -370,11 +346,8 @@ static void print_app_name_from_uid(const uid_t uid, char* buffer, const int buf if (appid >= AID_ISOLATED_START) { snprintf(buffer, bufferlen, "u%u_i%u", userid, appid - AID_ISOLATED_START); } else if (appid < AID_APP_START) { - for (size_t n = 0; n < android_id_count; n++) { - if (android_ids[n].aid == appid) { - snprintf(buffer, bufferlen, "u%u_%s", userid, android_ids[n].name); - return; - } + if (auto* android_id_info = find_android_id_info(appid); android_id_info != nullptr) { + snprintf(buffer, bufferlen, "u%u_%s", userid, android_id_info->name); } } else { snprintf(buffer, bufferlen, "u%u_a%u", userid, appid - AID_APP_START); @@ -391,11 +364,8 @@ static void print_app_name_from_gid(const gid_t gid, char* buffer, const int buf } else if (appid >= AID_CACHE_GID_START && appid <= AID_CACHE_GID_END) { snprintf(buffer, bufferlen, "u%u_a%u_cache", userid, appid - AID_CACHE_GID_START); } else if (appid < AID_APP_START) { - for (size_t n = 0; n < android_id_count; n++) { - if (android_ids[n].aid == appid) { - snprintf(buffer, bufferlen, "u%u_%s", userid, android_ids[n].name); - return; - } + if (auto* android_id_info = find_android_id_info(appid); android_id_info != nullptr) { + snprintf(buffer, bufferlen, "u%u_%s", userid, android_id_info->name); } } else { snprintf(buffer, bufferlen, "u%u_a%u", userid, appid - AID_APP_START); @@ -520,12 +490,12 @@ passwd* getpwuid(uid_t uid) { // NOLINT: implementing bad function. return nullptr; } - passwd* pw = android_id_to_passwd(state, uid); - if (pw != nullptr) { - return pw; + if (auto* android_id_info = find_android_id_info(uid); android_id_info != nullptr) { + return android_iinfo_to_passwd(state, android_id_info); } + // Handle OEM range. - pw = oem_id_to_passwd(uid, state); + passwd* pw = oem_id_to_passwd(uid, state); if (pw != nullptr) { return pw; } @@ -538,9 +508,8 @@ passwd* getpwnam(const char* login) { // NOLINT: implementing bad function. return nullptr; } - passwd* pw = android_name_to_passwd(state, login); - if (pw != nullptr) { - return pw; + if (auto* android_id_info = find_android_id_info(login); android_id_info != nullptr) { + return android_iinfo_to_passwd(state, android_id_info); } if (vendor_passwd.FindByName(login, state)) { @@ -550,7 +519,7 @@ passwd* getpwnam(const char* login) { // NOLINT: implementing bad function. } // Handle OEM range. - pw = oem_id_to_passwd(oem_id_from_name(login), state); + passwd* pw = oem_id_to_passwd(oem_id_from_name(login), state); if (pw != nullptr) { return pw; } @@ -634,12 +603,12 @@ passwd* getpwent() { } static group* getgrgid_internal(gid_t gid, group_state_t* state) { - group* grp = android_id_to_group(state, gid); - if (grp != nullptr) { - return grp; + if (auto* android_id_info = find_android_id_info(gid); android_id_info != nullptr) { + return android_iinfo_to_group(state, android_id_info); } + // Handle OEM range. - grp = oem_id_to_group(gid, state); + group* grp = oem_id_to_group(gid, state); if (grp != nullptr) { return grp; } @@ -655,9 +624,8 @@ group* getgrgid(gid_t gid) { // NOLINT: implementing bad function. } static group* getgrnam_internal(const char* name, group_state_t* state) { - group* grp = android_name_to_group(state, name); - if (grp != nullptr) { - return grp; + if (auto* android_id_info = find_android_id_info(name); android_id_info != nullptr) { + return android_iinfo_to_group(state, android_id_info); } if (vendor_group.FindByName(name, state)) { @@ -667,7 +635,7 @@ static group* getgrnam_internal(const char* name, group_state_t* state) { } // Handle OEM range. - grp = oem_id_to_group(oem_id_from_name(name), state); + group* grp = oem_id_to_group(oem_id_from_name(name), state); if (grp != nullptr) { return grp; } diff --git a/libc/bionic/libc_init_common.cpp b/libc/bionic/libc_init_common.cpp index b229cda2f..1f099cfcc 100644 --- a/libc/bionic/libc_init_common.cpp +++ b/libc/bionic/libc_init_common.cpp @@ -232,6 +232,7 @@ static bool __is_unsafe_environment_variable(const char* name) { "LD_AOUT_LIBRARY_PATH", "LD_AOUT_PRELOAD", "LD_AUDIT", + "LD_CONFIG_FILE", "LD_DEBUG", "LD_DEBUG_OUTPUT", "LD_DYNAMIC_WEAK", @@ -242,6 +243,7 @@ static bool __is_unsafe_environment_variable(const char* name) { "LD_SHOW_AUXV", "LD_USE_LOAD_BIAS", "LIBC_DEBUG_MALLOC_OPTIONS", + "LIBC_HOOKS_ENABLE", "LOCALDOMAIN", "LOCPATH", "MALLOC_CHECK_", diff --git a/libc/bionic/malloc_common.cpp b/libc/bionic/malloc_common.cpp index e9f9db20a..9dc4d12d1 100644 --- a/libc/bionic/malloc_common.cpp +++ b/libc/bionic/malloc_common.cpp @@ -32,14 +32,7 @@ // calls and add special debugging code to attempt to catch allocation // errors. All of the debugging code is implemented in a separate shared // library that is only loaded when the property "libc.debug.malloc.options" -// is set to a non-zero value. There are two functions exported to -// allow ddms, or other external users to get information from the debug -// allocation. -// get_malloc_leak_info: Returns information about all of the known native -// allocations that are currently in use. -// free_malloc_leak_info: Frees the data allocated by the call to -// get_malloc_leak_info. -// write_malloc_leak_info: Writes the leak info data to a file. +// is set to a non-zero value. #include <errno.h> #include <stdint.h> diff --git a/libc/bionic/malloc_common_dynamic.cpp b/libc/bionic/malloc_common_dynamic.cpp index 64f9f6f7b..599ac6a58 100644 --- a/libc/bionic/malloc_common_dynamic.cpp +++ b/libc/bionic/malloc_common_dynamic.cpp @@ -409,39 +409,29 @@ __LIBC_HIDDEN__ void __libc_init_malloc(libc_globals* globals) { // ============================================================================= // Functions to support dumping of native heap allocations using malloc debug. // ============================================================================= - -// Retrieve native heap information. -// -// "*info" is set to a buffer we allocate -// "*overall_size" is set to the size of the "info" buffer -// "*info_size" is set to the size of a single entry -// "*total_memory" is set to the sum of all allocations we're tracking; does -// not include heap overhead -// "*backtrace_size" is set to the maximum number of entries in the back trace -extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overall_size, - size_t* info_size, size_t* total_memory, size_t* backtrace_size) { +bool GetMallocLeakInfo(android_mallopt_leak_info_t* leak_info) { void* func = gFunctions[FUNC_GET_MALLOC_LEAK_INFO]; if (func == nullptr) { - return; + errno = ENOTSUP; + return false; } - reinterpret_cast<get_malloc_leak_info_func_t>(func)(info, overall_size, info_size, total_memory, - backtrace_size); + reinterpret_cast<get_malloc_leak_info_func_t>(func)( + &leak_info->buffer, &leak_info->overall_size, &leak_info->info_size, + &leak_info->total_memory, &leak_info->backtrace_size); + return true; } -extern "C" void free_malloc_leak_info(uint8_t* info) { +bool FreeMallocLeakInfo(android_mallopt_leak_info_t* leak_info) { void* func = gFunctions[FUNC_FREE_MALLOC_LEAK_INFO]; if (func == nullptr) { - return; + errno = ENOTSUP; + return false; } - reinterpret_cast<free_malloc_leak_info_func_t>(func)(info); + reinterpret_cast<free_malloc_leak_info_func_t>(func)(leak_info->buffer); + return true; } -extern "C" void write_malloc_leak_info(FILE* fp) { - if (fp == nullptr) { - error_log("write_malloc_leak_info called with a nullptr"); - return; - } - +bool WriteMallocLeakInfo(FILE* fp) { void* func = gFunctions[FUNC_WRITE_LEAK_INFO]; bool written = false; if (func != nullptr) { @@ -453,7 +443,9 @@ extern "C" void write_malloc_leak_info(FILE* fp) { fprintf(fp, "# adb shell stop\n"); fprintf(fp, "# adb shell setprop libc.debug.malloc.options backtrace\n"); fprintf(fp, "# adb shell start\n"); + errno = ENOTSUP; } + return written; } // ============================================================================= @@ -484,6 +476,27 @@ extern "C" bool android_mallopt(int opcode, void* arg, size_t arg_size) { if (opcode == M_SET_ALLOCATION_LIMIT_BYTES) { return LimitEnable(arg, arg_size); } + if (opcode == M_WRITE_MALLOC_LEAK_INFO_TO_FILE) { + if (arg == nullptr || arg_size != sizeof(FILE*)) { + errno = EINVAL; + return false; + } + return WriteMallocLeakInfo(reinterpret_cast<FILE*>(arg)); + } + if (opcode == M_GET_MALLOC_LEAK_INFO) { + if (arg == nullptr || arg_size != sizeof(android_mallopt_leak_info_t)) { + errno = EINVAL; + return false; + } + return GetMallocLeakInfo(reinterpret_cast<android_mallopt_leak_info_t*>(arg)); + } + if (opcode == M_FREE_MALLOC_LEAK_INFO) { + if (arg == nullptr || arg_size != sizeof(android_mallopt_leak_info_t)) { + errno = EINVAL; + return false; + } + return FreeMallocLeakInfo(reinterpret_cast<android_mallopt_leak_info_t*>(arg)); + } return HeapprofdMallopt(opcode, arg, arg_size); } // ============================================================================= diff --git a/libc/bionic/pthread_getschedparam.cpp b/libc/bionic/pthread_getschedparam.cpp index ed1853bdc..765287fa4 100644 --- a/libc/bionic/pthread_getschedparam.cpp +++ b/libc/bionic/pthread_getschedparam.cpp @@ -28,9 +28,11 @@ #include <errno.h> +#include "private/bionic_defs.h" #include "private/ErrnoRestorer.h" #include "pthread_internal.h" +__BIONIC_WEAK_FOR_NATIVE_BRIDGE int pthread_getschedparam(pthread_t t, int* policy, sched_param* param) { ErrnoRestorer errno_restorer; diff --git a/libc/bionic/pthread_setschedparam.cpp b/libc/bionic/pthread_setschedparam.cpp index 8a0272868..656fe3208 100644 --- a/libc/bionic/pthread_setschedparam.cpp +++ b/libc/bionic/pthread_setschedparam.cpp @@ -30,9 +30,11 @@ #include <pthread.h> #include <sched.h> +#include "private/bionic_defs.h" #include "private/ErrnoRestorer.h" #include "pthread_internal.h" +__BIONIC_WEAK_FOR_NATIVE_BRIDGE int pthread_setschedparam(pthread_t t, int policy, const sched_param* param) { ErrnoRestorer errno_restorer; @@ -42,6 +44,7 @@ int pthread_setschedparam(pthread_t t, int policy, const sched_param* param) { return (sched_setscheduler(tid, policy, param) == -1) ? errno : 0; } +__BIONIC_WEAK_FOR_NATIVE_BRIDGE int pthread_setschedprio(pthread_t t, int priority) { ErrnoRestorer errno_restorer; diff --git a/libc/bionic/threads.cpp b/libc/bionic/threads.cpp new file mode 100644 index 000000000..f59758013 --- /dev/null +++ b/libc/bionic/threads.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +#include <threads.h> + +#define __BIONIC_THREADS_INLINE /* Out of line. */ +#include <bits/threads_inlines.h> diff --git a/libc/include/android/api-level.h b/libc/include/android/api-level.h index a17585768..af7cde1e3 100644 --- a/libc/include/android/api-level.h +++ b/libc/include/android/api-level.h @@ -100,6 +100,9 @@ __BEGIN_DECLS /** Names the "Q" API level (29), for comparisons against __ANDROID_API__. */ #define __ANDROID_API_Q__ 29 +/** Names the "R" API level (30), for comparisons against __ANDROID_API__. */ +#define __ANDROID_API_R__ 30 + /** * Returns the `targetSdkVersion` of the caller, or `__ANDROID_API_FUTURE__` * if there is no known target SDK version (for code not running in the diff --git a/libc/arch-arm/dynamic_function_wrapper.S b/libc/include/android/legacy_threads_inlines.h index 1d2842b5c..e73ef37c7 100644 --- a/libc/arch-arm/dynamic_function_wrapper.S +++ b/libc/include/android/legacy_threads_inlines.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,12 +26,13 @@ * SUCH DAMAGE. */ -#include <private/bionic_asm.h> +#pragma once -#define FUNCTION_DELEGATE(name, impl) \ -ENTRY(name); \ - b impl; \ -END(name) +#include <sys/cdefs.h> -FUNCTION_DELEGATE(strcmp, strcmp_internal) -FUNCTION_DELEGATE(strlen, strlen_internal) +#if __ANDROID_API__ < __ANDROID_API_R__ + +#define __BIONIC_THREADS_INLINE static __inline +#include <bits/threads_inlines.h> + +#endif diff --git a/libc/include/bits/threads_inlines.h b/libc/include/bits/threads_inlines.h new file mode 100644 index 000000000..1130b3a55 --- /dev/null +++ b/libc/include/bits/threads_inlines.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +#pragma once + +#include <threads.h> + +#include <errno.h> +#include <sched.h> +#include <stdlib.h> + +#if !defined(__BIONIC_THREADS_INLINE) +#define __BIONIC_THREADS_INLINE static __inline +#endif + +__BEGIN_DECLS + +static __inline int __bionic_thrd_error(int __pthread_code) { + switch (__pthread_code) { + case 0: return 0; + case ENOMEM: return thrd_nomem; + case ETIMEDOUT: return thrd_timedout; + case EBUSY: return thrd_busy; + default: return thrd_error; + } +} + +__BIONIC_THREADS_INLINE void call_once(once_flag* __flag, + void (*__function)(void)) { + pthread_once(__flag, __function); +} + + + +__BIONIC_THREADS_INLINE int cnd_broadcast(cnd_t* __cnd) { + return __bionic_thrd_error(pthread_cond_broadcast(__cnd)); +} + +__BIONIC_THREADS_INLINE void cnd_destroy(cnd_t* __cnd) { + pthread_cond_destroy(__cnd); +} + +__BIONIC_THREADS_INLINE int cnd_init(cnd_t* __cnd) { + return __bionic_thrd_error(pthread_cond_init(__cnd, NULL)); +} + +__BIONIC_THREADS_INLINE int cnd_signal(cnd_t* __cnd) { + return __bionic_thrd_error(pthread_cond_signal(__cnd)); +} + +__BIONIC_THREADS_INLINE int cnd_timedwait(cnd_t* __cnd, + mtx_t* __mtx, + const struct timespec* __timeout) { + return __bionic_thrd_error(pthread_cond_timedwait(__cnd, __mtx, __timeout)); +} + +__BIONIC_THREADS_INLINE int cnd_wait(cnd_t* __cnd, mtx_t* __mtx) { + return __bionic_thrd_error(pthread_cond_wait(__cnd, __mtx)); +} + + + +__BIONIC_THREADS_INLINE void mtx_destroy(mtx_t* __mtx) { + pthread_mutex_destroy(__mtx); +} + +__BIONIC_THREADS_INLINE int mtx_init(mtx_t* __mtx, int __type) { + int __pthread_type = (__type & mtx_recursive) ? PTHREAD_MUTEX_RECURSIVE + : PTHREAD_MUTEX_NORMAL; + __type &= ~mtx_recursive; + if (__type != mtx_plain && __type != mtx_timed) return thrd_error; + + pthread_mutexattr_t __attr; + pthread_mutexattr_init(&__attr); + pthread_mutexattr_settype(&__attr, __pthread_type); + return __bionic_thrd_error(pthread_mutex_init(__mtx, &__attr)); +} + +__BIONIC_THREADS_INLINE int mtx_lock(mtx_t* __mtx) { + return __bionic_thrd_error(pthread_mutex_lock(__mtx)); +} + +__BIONIC_THREADS_INLINE int mtx_timedlock(mtx_t* __mtx, + const struct timespec* __timeout) { + return __bionic_thrd_error(pthread_mutex_timedlock(__mtx, __timeout)); +} + +__BIONIC_THREADS_INLINE int mtx_trylock(mtx_t* __mtx) { + return __bionic_thrd_error(pthread_mutex_trylock(__mtx)); +} + +__BIONIC_THREADS_INLINE int mtx_unlock(mtx_t* __mtx) { + return __bionic_thrd_error(pthread_mutex_unlock(__mtx)); +} + + + +struct __bionic_thrd_data { + thrd_start_t __func; + void* __arg; +}; + +static inline void* __bionic_thrd_trampoline(void* __arg) { + struct __bionic_thrd_data __data = + *__BIONIC_CAST(static_cast, struct __bionic_thrd_data*, __arg); + free(__arg); + int __result = __data.__func(__data.__arg); + return __BIONIC_CAST(reinterpret_cast, void*, + __BIONIC_CAST(static_cast, uintptr_t, __result)); +} + +__BIONIC_THREADS_INLINE int thrd_create(thrd_t* __thrd, + thrd_start_t __func, + void* __arg) { + struct __bionic_thrd_data* __pthread_arg = + __BIONIC_CAST(static_cast, struct __bionic_thrd_data*, + malloc(sizeof(struct __bionic_thrd_data))); + __pthread_arg->__func = __func; + __pthread_arg->__arg = __arg; + int __result = __bionic_thrd_error(pthread_create(__thrd, NULL, + __bionic_thrd_trampoline, + __pthread_arg)); + if (__result != thrd_success) free(__pthread_arg); + return __result; +} + +__BIONIC_THREADS_INLINE thrd_t thrd_current(void) { + return pthread_self(); +} + +__BIONIC_THREADS_INLINE int thrd_detach(thrd_t __thrd) { + return __bionic_thrd_error(pthread_detach(__thrd)); +} + +__BIONIC_THREADS_INLINE int thrd_equal(thrd_t __lhs, thrd_t __rhs) { + return pthread_equal(__lhs, __rhs); +} + +__BIONIC_THREADS_INLINE void thrd_exit(int __result) { + pthread_exit(__BIONIC_CAST(reinterpret_cast, void*, + __BIONIC_CAST(static_cast, uintptr_t, __result))); +} + +__BIONIC_THREADS_INLINE int thrd_join(thrd_t __thrd, int* __result) { + void* __pthread_result; + if (pthread_join(__thrd, &__pthread_result) != 0) return thrd_error; + if (__result) { + *__result = __BIONIC_CAST(reinterpret_cast, intptr_t, __pthread_result); + } + return thrd_success; +} + +__BIONIC_THREADS_INLINE int thrd_sleep(const struct timespec* __duration, + struct timespec* __remaining) { + int __rc = nanosleep(__duration, __remaining); + if (__rc == 0) return 0; + return (errno == EINTR) ? -1 : -2; +} + +__BIONIC_THREADS_INLINE void thrd_yield(void) { + sched_yield(); +} + + + +__BIONIC_THREADS_INLINE int tss_create(tss_t* __key, tss_dtor_t __dtor) { + return __bionic_thrd_error(pthread_key_create(__key, __dtor)); +} + +__BIONIC_THREADS_INLINE void tss_delete(tss_t __key) { + pthread_key_delete(__key); +} + +__BIONIC_THREADS_INLINE void* tss_get(tss_t __key) { + return pthread_getspecific(__key); +} + +__BIONIC_THREADS_INLINE int tss_set(tss_t __key, void* __value) { + return __bionic_thrd_error(pthread_setspecific(__key, __value)); +} + +__END_DECLS diff --git a/libc/include/threads.h b/libc/include/threads.h new file mode 100644 index 000000000..2c43b0b65 --- /dev/null +++ b/libc/include/threads.h @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +#pragma once + +/** + * @file threads.h + * @brief C11 threads. + */ + +#include <sys/cdefs.h> + +#include <pthread.h> +#include <time.h> + +#define ONCE_FLAG_INIT PTHREAD_ONCE_INIT +#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS + +/** The type for a condition variable. */ +typedef pthread_cond_t cnd_t; +/** The type for a thread. */ +typedef pthread_t thrd_t; +/** The type for a thread-specific storage key. */ +typedef pthread_key_t tss_t; +/** The type for a mutex. */ +typedef pthread_mutex_t mtx_t; + +/** The type for a thread-specific storage destructor. */ +typedef void (*tss_dtor_t)(void*); +/** The type of the function passed to thrd_create() to create a new thread. */ +typedef int (*thrd_start_t)(void*); + +/** The type used by call_once(). */ +typedef pthread_once_t once_flag; + +enum { + mtx_plain = 0x1, + mtx_recursive = 0x2, + mtx_timed = 0x4, +}; + +enum { + thrd_success = 0, + thrd_busy = 1, + thrd_error = 2, + thrd_nomem = 3, + thrd_timedout = 4, +}; + +#if !defined(__cplusplus) +#define thread_local _Thread_local +#endif + +__BEGIN_DECLS + +#if __ANDROID_API__ >= __ANDROID_API_R__ +// This file is implemented as static inlines before API level 30. + +/** Uses `__flag` to ensure that `__function` is called exactly once. */ +void call_once(once_flag* __flag, void (*__function)(void)); + + + +/** + * Unblocks all threads blocked on `__cond`. + */ +int cnd_broadcast(cnd_t* __cond); + +/** + * Destroys a condition variable. + */ +void cnd_destroy(cnd_t* __cond); + +/** + * Creates a condition variable. + */ +int cnd_init(cnd_t* __cond); + +/** + * Unblocks one thread blocked on `__cond`. + */ +int cnd_signal(cnd_t* __cond); + +/** + * Unlocks `__mutex` and blocks until `__cond` is signaled or `__timeout` occurs. + */ +int cnd_timedwait(cnd_t* __cond, mtx_t* __mutex, const struct timespec* __timeout); + +/** + * Unlocks `__mutex` and blocks until `__cond` is signaled. + */ +int cnd_wait(cnd_t* __cond, mtx_t* __mutex); + + + +/** + * Destroys a mutex. + */ +void mtx_destroy(mtx_t* __mutex); + +/** + * Creates a mutex. + */ +int mtx_init(mtx_t* __mutex, int __type); + +/** + * Blocks until `__mutex` is acquired. + */ +int mtx_lock(mtx_t* __mutex); + +/** + * Blocks until `__mutex` is acquired or `__timeout` expires. + */ +int mtx_timedlock(mtx_t* __mutex, const struct timespec* __timeout); + +/** + * Acquires `__mutex` or returns `thrd_busy`. + */ +int mtx_trylock(mtx_t* __mutex); + +/** + * Unlocks `__mutex`. + */ +int mtx_unlock(mtx_t* __mutex); + + + +/** + * Creates a new thread running `__function(__arg)`, and sets `*__thrd` to + * the new thread. + */ +int thrd_create(thrd_t* __thrd, thrd_start_t __function, void* __arg); + +/** + * Returns the `thrd_t` corresponding to the caller. + */ +thrd_t thrd_current(void); + +/** + * Tells the OS to automatically dispose of `__thrd` when it exits. + */ +int thrd_detach(thrd_t __thrd); + +/** + * Tests whether two threads are the same thread. + */ +int thrd_equal(thrd_t __lhs, thrd_t __rhs); + +/** + * Terminates the calling thread, setting its result to `__result`. + */ +void thrd_exit(int __result) __noreturn; + +/** + * Blocks until `__thrd` terminates. If `__result` is not null, `*__result` + * is set to the exiting thread's result. + */ +int thrd_join(thrd_t __thrd, int* __result); + +/** + * Blocks the caller for at least `__duration` unless a signal is delivered. + * If a signal causes the sleep to end early and `__remaining` is not null, + * `*__remaining` is set to the time remaining. + * + * Returns 0 on success, or -1 if a signal was delivered. + */ +int thrd_sleep(const struct timespec* __duration, struct timespec* __remaining); + +/** + * Request that other threads should be scheduled. + */ +void thrd_yield(void); + + + +/** + * Creates a thread-specific storage key with the associated destructor (which + * may be null). + */ +int tss_create(tss_t* __key, tss_dtor_t __dtor); + +/** + * Destroys a thread-specific storage key. + */ +void tss_delete(tss_t __key); + +/** + * Returns the value for the current thread held in the thread-specific storage + * identified by `__key`. + */ +void* tss_get(tss_t __key); + +/** + * Sets the current thread's value for the thread-specific storage identified + * by `__key` to `__value`. + */ +int tss_set(tss_t __key, void* __value); + +#endif + +__END_DECLS + +#include <android/legacy_threads_inlines.h> diff --git a/libc/libc.map.txt b/libc/libc.map.txt index 55ca9dcfb..4a734fc20 100644 --- a/libc/libc.map.txt +++ b/libc/libc.map.txt @@ -1473,18 +1473,42 @@ LIBC_Q { # introduced=Q malloc_enable; # apex malloc_iterate; # apex - # Used by libmediautils - write_malloc_leak_info; # apex - free_malloc_leak_info; # apex - get_malloc_leak_info; # apex - # Used by libandroid_net android_getaddrinfofornet; # apex - # Used by libandroid_runtime and libmedia + # Used by libandroid_runtime, libmedia and libmediautils android_mallopt; # apex } LIBC_P; +LIBC_R { # introduced=R + global: + call_once; + cnd_broadcast; + cnd_destroy; + cnd_init; + cnd_signal; + cnd_timedwait; + cnd_wait; + mtx_destroy; + mtx_init; + mtx_lock; + mtx_timedlock; + mtx_trylock; + mtx_unlock; + thrd_create; + thrd_current; + thrd_detach; + thrd_equal; + thrd_exit; + thrd_join; + thrd_sleep; + thrd_yield; + tss_create; + tss_delete; + tss_get; + tss_set; +} LIBC_Q; + LIBC_PRIVATE { global: ___Unwind_Backtrace; # arm diff --git a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp index 0d23a6a8f..86e20ea90 100644 --- a/libc/malloc_hooks/tests/malloc_hooks_tests.cpp +++ b/libc/malloc_hooks/tests/malloc_hooks_tests.cpp @@ -38,6 +38,7 @@ #include <gtest/gtest.h> +#include <private/bionic_malloc.h> #include <private/bionic_malloc_dispatch.h> #include <tests/utils.h> @@ -197,19 +198,15 @@ TEST_F(MallocHooksTest, extended_functions) { } TEST_F(MallocHooksTest, DISABLED_extended_functions) { - uint8_t* info = nullptr; - size_t overall_size = 100; - size_t info_size = 200; - size_t total_memory = 300; - size_t backtrace_size = 400; - get_malloc_leak_info(&info, &overall_size, &info_size, &total_memory, &backtrace_size); - EXPECT_EQ(nullptr, info); - EXPECT_EQ(0U, overall_size); - EXPECT_EQ(0U, info_size); - EXPECT_EQ(0U, total_memory); - EXPECT_EQ(0U, backtrace_size); - - free_malloc_leak_info(info); + android_mallopt_leak_info_t leak_info; + ASSERT_TRUE(android_mallopt(M_GET_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info))); + EXPECT_EQ(nullptr, leak_info.buffer); + EXPECT_EQ(0U, leak_info.overall_size); + EXPECT_EQ(0U, leak_info.info_size); + EXPECT_EQ(0U, leak_info.total_memory); + EXPECT_EQ(0U, leak_info.backtrace_size); + + ASSERT_TRUE(android_mallopt(M_FREE_MALLOC_LEAK_INFO, &leak_info, sizeof(leak_info))); malloc_enable(); malloc_disable(); diff --git a/libc/private/bionic_malloc.h b/libc/private/bionic_malloc.h index e8a6f6ef5..9c602eadf 100644 --- a/libc/private/bionic_malloc.h +++ b/libc/private/bionic_malloc.h @@ -30,6 +30,22 @@ #include <stdbool.h> +// Structures for android_mallopt. + +typedef struct { + // Pointer to the buffer allocated by a call to M_GET_MALLOC_LEAK_INFO. + uint8_t* buffer; + // The size of the "info" buffer. + size_t overall_size; + // The size of a single entry. + size_t info_size; + // The sum of all allocations that have been tracked. Does not include + // any heap overhead. + size_t total_memory; + // The maximum number of backtrace entries. + size_t backtrace_size; +} android_mallopt_leak_info_t; + // Opcodes for android_mallopt. enum { @@ -48,6 +64,26 @@ enum { // Called after the zygote forks to indicate this is a child. M_SET_ZYGOTE_CHILD = 4, #define M_SET_ZYGOTE_CHILD M_SET_ZYGOTE_CHILD + + // Options to dump backtraces of allocations. These options only + // work when malloc debug has been enabled. + + // Writes the backtrace information of all current allocations to a file. + // NOTE: arg_size has to be sizeof(FILE*) because FILE is an opaque type. + // arg = FILE* + // arg_size = sizeof(FILE*) + M_WRITE_MALLOC_LEAK_INFO_TO_FILE = 5, +#define M_WRITE_MALLOC_LEAK_INFO_TO_FILE M_WRITE_MALLOC_LEAK_INFO_TO_FILE + // Get information about the backtraces of all + // arg = android_mallopt_leak_info_t* + // arg_size = sizeof(android_mallopt_leak_info_t) + M_GET_MALLOC_LEAK_INFO = 6, +#define M_GET_MALLOC_LEAK_INFO M_GET_MALLOC_LEAK_INFO + // Free the memory allocated and returned by M_GET_MALLOC_LEAK_INFO. + // arg = android_mallopt_leak_info_t* + // arg_size = sizeof(android_mallopt_leak_info_t) + M_FREE_MALLOC_LEAK_INFO = 7, +#define M_FREE_MALLOC_LEAK_INFO M_FREE_MALLOC_LEAK_INFO }; // Manipulates bionic-specific handling of memory allocation APIs such as diff --git a/libc/stdlib/atexit.c b/libc/stdlib/atexit.c index bd6ac3db0..692e0c0fc 100644 --- a/libc/stdlib/atexit.c +++ b/libc/stdlib/atexit.c @@ -186,7 +186,10 @@ restart: } _ATEXIT_UNLOCK(); - fflush(NULL); + /* If called via exit(), flush output of all open files. */ + if (dso == NULL) { + fflush(NULL); + } /* BEGIN android-changed: call __unregister_atfork if dso is not null */ if (dso != NULL) { diff --git a/libdl/libdl_static.cpp b/libdl/libdl_static.cpp index 714676258..0a36e6f16 100644 --- a/libdl/libdl_static.cpp +++ b/libdl/libdl_static.cpp @@ -23,7 +23,7 @@ void* dlopen(const char* /*filename*/, int /*flag*/) { } char* dlerror() { - return nullptr; + return const_cast<char*>("libdl.a is a stub --- use libdl.so instead"); } void* dlsym(void* /*handle*/, const char* /*symbol*/) { diff --git a/tests/Android.bp b/tests/Android.bp index 71cf8a67e..85bb29a69 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -184,6 +184,7 @@ cc_test_library { "system_properties_test2.cpp", "termios_test.cpp", "tgmath_test.c", + "threads_test.cpp", "time_test.cpp", "uchar_test.cpp", "unistd_nofortify_test.cpp", diff --git a/tests/headers/posix/threads_h.c b/tests/headers/posix/threads_h.c new file mode 100644 index 000000000..c9329f4e8 --- /dev/null +++ b/tests/headers/posix/threads_h.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +#if __has_include(<threads.h>) + +#include <threads.h> + +#include "header_checks.h" + +thread_local int t; + +static void threads_h() { + MACRO(ONCE_FLAG_INIT); + MACRO(TSS_DTOR_ITERATIONS); + + TYPE(cnd_t); + TYPE(thrd_t); + TYPE(tss_t); + TYPE(mtx_t); + + TYPE(tss_dtor_t); + TYPE(thrd_start_t); + + TYPE(once_flag); + + int enumeration_constants = mtx_plain | mtx_recursive | mtx_timed | + thrd_timedout | thrd_success | thrd_busy | thrd_error | thrd_nomem; + + FUNCTION(call_once, void (*f)(once_flag*, void (*)(void))); + + FUNCTION(cnd_broadcast, int (*f)(cnd_t*)); + FUNCTION(cnd_destroy, void (*f)(cnd_t*)); + FUNCTION(cnd_init, int (*f)(cnd_t*)); + FUNCTION(cnd_signal, int (*f)(cnd_t*)); + FUNCTION(cnd_timedwait, int (*f)(cnd_t*, mtx_t*, const struct timespec*)); + FUNCTION(cnd_wait, int (*f)(cnd_t*, mtx_t*)); + + FUNCTION(mtx_destroy, void (*f)(mtx_t*)); + FUNCTION(mtx_init, int (*f)(mtx_t*, int)); + FUNCTION(mtx_lock, int (*f)(mtx_t*)); + FUNCTION(mtx_timedlock, int (*f)(mtx_t*, const struct timespec*)); + FUNCTION(mtx_trylock, int (*f)(mtx_t*)); + FUNCTION(mtx_unlock, int (*f)(mtx_t*)); + + FUNCTION(thrd_create, int (*f)(thrd_t*, thrd_start_t, void*)); + FUNCTION(thrd_current, thrd_t (*f)(void)); + FUNCTION(thrd_detach, int (*f)(thrd_t)); + FUNCTION(thrd_equal, int (*f)(thrd_t, thrd_t)); + FUNCTION(thrd_exit, void (*f)(int)); + FUNCTION(thrd_join, int (*f)(thrd_t, int*)); + FUNCTION(thrd_sleep, int (*f)(const struct timespec*, struct timespec*)); + FUNCTION(thrd_yield, void (*f)(void)); + + FUNCTION(tss_create, int (*f)(tss_t*, tss_dtor_t)); + FUNCTION(tss_delete, void (*f)(tss_t)); + FUNCTION(tss_get, void* (*f)(tss_t)); + FUNCTION(tss_set, int (*f)(tss_t, void*)); +} + +#define DO_NOT_INCLUDE_TIME_H +#include "time_h.c" + +#endif diff --git a/tests/threads_test.cpp b/tests/threads_test.cpp new file mode 100644 index 000000000..5fafff38d --- /dev/null +++ b/tests/threads_test.cpp @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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 COPYRIGHT HOLDERS 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 + * COPYRIGHT OWNER 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. + */ + +#include <gtest/gtest.h> + +#if __has_include(<threads.h>) + +#define HAVE_THREADS_H +#include <threads.h> + +static int g_call_once_call_count; + +static void increment_call_count() { + ++g_call_once_call_count; +} + +static int g_dtor_call_count; + +static void tss_dtor(void* ptr) { + ++g_dtor_call_count; + free(ptr); +} + +static int return_arg(void* arg) { + return static_cast<int>(reinterpret_cast<uintptr_t>(arg)); +} + +static int exit_arg(void* arg) { + thrd_exit(static_cast<int>(reinterpret_cast<uintptr_t>(arg))); +} + +#endif + +#include <time.h> + +#include <thread> + +#include "BionicDeathTest.h" +#include "SignalUtils.h" + +TEST(threads, call_once) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + once_flag flag = ONCE_FLAG_INIT; + call_once(&flag, increment_call_count); + call_once(&flag, increment_call_count); + std::thread([&flag] { + call_once(&flag, increment_call_count); + }).join(); + ASSERT_EQ(1, g_call_once_call_count); +#endif +} + +TEST(threads, cnd_broadcast__cnd_wait) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain)); + + cnd_t c; + ASSERT_EQ(thrd_success, cnd_init(&c)); + + std::atomic_int i = 0; + + auto waiter = [&c, &i, &m] { + ASSERT_EQ(thrd_success, mtx_lock(&m)); + while (i != 1) ASSERT_EQ(thrd_success, cnd_wait(&c, &m)); + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + }; + std::thread t1(waiter); + std::thread t2(waiter); + std::thread t3(waiter); + + ASSERT_EQ(thrd_success, mtx_lock(&m)); + i = 1; + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + + ASSERT_EQ(thrd_success, cnd_broadcast(&c)); + + t1.join(); + t2.join(); + t3.join(); + + mtx_destroy(&m); + cnd_destroy(&c); +#endif +} + +TEST(threads, cnd_init__cnd_destroy) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + cnd_t c; + ASSERT_EQ(thrd_success, cnd_init(&c)); + cnd_destroy(&c); +#endif +} + +TEST(threads, cnd_signal__cnd_wait) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain)); + cnd_t c; + ASSERT_EQ(thrd_success, cnd_init(&c)); + + std::atomic_int count = 0; + auto waiter = [&c, &m, &count] { + ASSERT_EQ(thrd_success, mtx_lock(&m)); + ASSERT_EQ(thrd_success, cnd_wait(&c, &m)); + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + ++count; + }; + std::thread t1(waiter); + std::thread t2(waiter); + std::thread t3(waiter); + + // This is inherently racy, but attempts to distinguish between cnd_signal and + // cnd_broadcast. + usleep(100000); + ASSERT_EQ(thrd_success, cnd_signal(&c)); + while (count == 0) { + } + usleep(100000); + ASSERT_EQ(1, count); + + ASSERT_EQ(thrd_success, cnd_signal(&c)); + while (count == 1) { + } + usleep(100000); + ASSERT_EQ(2, count); + + ASSERT_EQ(thrd_success, cnd_signal(&c)); + while (count == 2) { + } + usleep(100000); + ASSERT_EQ(3, count); + + t1.join(); + t2.join(); + t3.join(); + + mtx_destroy(&m); + cnd_destroy(&c); +#endif +} + +TEST(threads, cnd_timedwait_timedout) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed)); + ASSERT_EQ(thrd_success, mtx_lock(&m)); + + cnd_t c; + ASSERT_EQ(thrd_success, cnd_init(&c)); + + timespec ts = {}; + ASSERT_EQ(thrd_timedout, cnd_timedwait(&c, &m, &ts)); +#endif +} + +TEST(threads, cnd_timedwait) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed)); + + cnd_t c; + ASSERT_EQ(thrd_success, cnd_init(&c)); + + std::atomic_bool done = false; + std::thread t([&c, &m, &done] { + ASSERT_EQ(thrd_success, mtx_lock(&m)); + + // cnd_timewait's time is *absolute*. + timespec ts; + ASSERT_EQ(TIME_UTC, timespec_get(&ts, TIME_UTC)); + ts.tv_sec += 666; + + ASSERT_EQ(thrd_success, cnd_timedwait(&c, &m, &ts)); + done = true; + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + }); + + while (!done) ASSERT_EQ(thrd_success, cnd_signal(&c)); + + t.join(); +#endif +} + +TEST(threads, mtx_init) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain)); + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed)); + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain | mtx_recursive)); + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed | mtx_recursive)); + ASSERT_EQ(thrd_error, mtx_init(&m, 123)); + ASSERT_EQ(thrd_error, mtx_init(&m, mtx_recursive)); +#endif +} + +TEST(threads, mtx_destroy) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain)); + mtx_destroy(&m); +#endif +} + +TEST(threads, mtx_lock_plain) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain)); + + ASSERT_EQ(thrd_success, mtx_lock(&m)); + ASSERT_EQ(thrd_busy, mtx_trylock(&m)); + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + + mtx_destroy(&m); +#endif +} + +TEST(threads, mtx_lock_recursive) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain | mtx_recursive)); + + ASSERT_EQ(thrd_success, mtx_lock(&m)); + ASSERT_EQ(thrd_success, mtx_trylock(&m)); + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + + mtx_destroy(&m); +#endif +} + +TEST(threads, mtx_timedlock) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_timed)); + + timespec ts = {}; + ASSERT_EQ(thrd_success, mtx_timedlock(&m, &ts)); + + std::thread([&m] { + timespec ts = { .tv_nsec = 500000 }; + ASSERT_EQ(thrd_timedout, mtx_timedlock(&m, &ts)); + }).join(); + + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + mtx_destroy(&m); +#endif +} + + +TEST(threads, mtx_unlock) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + mtx_t m; + ASSERT_EQ(thrd_success, mtx_init(&m, mtx_plain)); + ASSERT_EQ(thrd_success, mtx_lock(&m)); + std::thread([&m] { + ASSERT_EQ(thrd_busy, mtx_trylock(&m)); + }).join(); + ASSERT_EQ(thrd_success, mtx_unlock(&m)); + std::thread([&m] { + ASSERT_EQ(thrd_success, mtx_trylock(&m)); + }).join(); +#endif +} + +TEST(threads, thrd_current__thrd_equal) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + thrd_t t1 = thrd_current(); + // (As a side-effect, this demonstrates interoperability with std::thread.) + std::thread([&t1] { + thrd_t t2 = thrd_current(); + ASSERT_FALSE(thrd_equal(t1, t2)); + thrd_t t2_2 = thrd_current(); + ASSERT_TRUE(thrd_equal(t2, t2_2)); + }).join(); + thrd_t t1_2 = thrd_current(); + ASSERT_TRUE(thrd_equal(t1, t1_2)); +#endif +} + +TEST(threads, thrd_create__thrd_detach) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + thrd_t t; + ASSERT_EQ(thrd_success, thrd_create(&t, exit_arg, reinterpret_cast<void*>(1))); + ASSERT_EQ(thrd_success, thrd_detach(t)); +#endif +} + +TEST(threads, thrd_create__thrd_exit) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + // Similar to the thrd_join test, but with a function that calls thrd_exit + // instead. + thrd_t t; + int result; + ASSERT_EQ(thrd_success, thrd_create(&t, exit_arg, reinterpret_cast<void*>(1))); + ASSERT_EQ(thrd_success, thrd_join(t, &result)); + ASSERT_EQ(1, result); + + ASSERT_EQ(thrd_success, thrd_create(&t, exit_arg, reinterpret_cast<void*>(2))); + ASSERT_EQ(thrd_success, thrd_join(t, &result)); + ASSERT_EQ(2, result); + + // The `result` argument can be null if you don't care... + ASSERT_EQ(thrd_success, thrd_create(&t, exit_arg, reinterpret_cast<void*>(3))); + ASSERT_EQ(thrd_success, thrd_join(t, nullptr)); +#endif +} + +class threads_DeathTest : public BionicDeathTest {}; + +TEST(threads_DeathTest, thrd_exit_main_thread) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + // "The program terminates normally after the last thread has been terminated. + // The behavior is as if the program called the exit function with the status + // EXIT_SUCCESS at thread termination time." (ISO/IEC 9899:2018) + ASSERT_EXIT(thrd_exit(12), ::testing::ExitedWithCode(EXIT_SUCCESS), ""); +#endif +} + +TEST(threads, thrd_create__thrd_join) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + // Similar to the thrd_exit test, but with a function that calls return + // instead. + thrd_t t; + int result; + ASSERT_EQ(thrd_success, thrd_create(&t, return_arg, reinterpret_cast<void*>(1))); + ASSERT_EQ(thrd_success, thrd_join(t, &result)); + ASSERT_EQ(1, result); + + ASSERT_EQ(thrd_success, thrd_create(&t, return_arg, reinterpret_cast<void*>(2))); + ASSERT_EQ(thrd_success, thrd_join(t, &result)); + ASSERT_EQ(2, result); + + // The `result` argument can be null if you don't care... + ASSERT_EQ(thrd_success, thrd_create(&t, return_arg, reinterpret_cast<void*>(3))); + ASSERT_EQ(thrd_success, thrd_join(t, nullptr)); +#endif +} + +TEST(threads, thrd_sleep_signal) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + ScopedSignalHandler ssh{SIGALRM, [](int) {}}; + std::thread t([] { + timespec long_time = { .tv_sec = 666 }; + timespec remaining = {}; + ASSERT_EQ(-1, thrd_sleep(&long_time, &remaining)); + uint64_t t = remaining.tv_sec * 1000000000 + remaining.tv_nsec; + ASSERT_LE(t, 666ULL * 1000000000); + }); + usleep(100000); // 0.1s + pthread_kill(t.native_handle(), SIGALRM); + t.join(); +#endif +} + +TEST(threads, thrd_sleep_signal_nullptr) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + ScopedSignalHandler ssh{SIGALRM, [](int) {}}; + std::thread t([] { + timespec long_time = { .tv_sec = 666 }; + ASSERT_EQ(-1, thrd_sleep(&long_time, nullptr)); + }); + usleep(100000); // 0.1s + pthread_kill(t.native_handle(), SIGALRM); + t.join(); +#endif +} + +TEST(threads, thrd_sleep_error) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + timespec invalid = { .tv_sec = -1 }; + ASSERT_EQ(-2, thrd_sleep(&invalid, nullptr)); +#endif +} + +TEST(threads, thrd_yield) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + thrd_yield(); +#endif +} + +TEST(threads, TSS_DTOR_ITERATIONS_macro) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + ASSERT_EQ(PTHREAD_DESTRUCTOR_ITERATIONS, TSS_DTOR_ITERATIONS); +#endif +} + +TEST(threads, tss_create) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + tss_t key; + ASSERT_EQ(thrd_success, tss_create(&key, nullptr)); + tss_delete(key); +#endif +} + +TEST(threads, tss_create_dtor) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + tss_dtor_t dtor = tss_dtor; + tss_t key; + ASSERT_EQ(thrd_success, tss_create(&key, dtor)); + + ASSERT_EQ(thrd_success, tss_set(key, strdup("hello"))); + std::thread([&key] { + ASSERT_EQ(thrd_success, tss_set(key, strdup("world"))); + }).join(); + // Thread exit calls the destructor... + ASSERT_EQ(1, g_dtor_call_count); + + // "[A call to tss_set] will not invoke the destructor associated with the + // key on the value being replaced" (ISO/IEC 9899:2018). + g_dtor_call_count = 0; + ASSERT_EQ(thrd_success, tss_set(key, strdup("hello"))); + ASSERT_EQ(0, g_dtor_call_count); + + // "Calling tss_delete will not result in the invocation of any + // destructors" (ISO/IEC 9899:2018). + // The destructor for "hello" won't be called until *this* thread exits. + g_dtor_call_count = 0; + tss_delete(key); + ASSERT_EQ(0, g_dtor_call_count); +#endif +} + +TEST(threads, tss_get__tss_set) { +#if !defined(HAVE_THREADS_H) + GTEST_SKIP() << "<threads.h> unavailable"; +#else + tss_t key; + ASSERT_EQ(thrd_success, tss_create(&key, nullptr)); + + ASSERT_EQ(thrd_success, tss_set(key, const_cast<char*>("hello"))); + ASSERT_STREQ("hello", reinterpret_cast<char*>(tss_get(key))); + std::thread([&key] { + ASSERT_EQ(nullptr, tss_get(key)); + ASSERT_EQ(thrd_success, tss_set(key, const_cast<char*>("world"))); + ASSERT_STREQ("world", reinterpret_cast<char*>(tss_get(key))); + }).join(); + ASSERT_STREQ("hello", reinterpret_cast<char*>(tss_get(key))); + + tss_delete(key); +#endif +} |