blob: 00c7671041605f065ced475727e86eeeec53cfc0 [file] [log] [blame]
#pragma once
#include "atomic.h"
#include "libc.h"
#include "pthread_arch.h"
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <pthread.h>
#include <signal.h>
#include <sys/uio.h>
#include <threads.h>
#include <zircon/stack.h>
#include <zircon/tls.h>
#include <runtime/thread.h>
#include <runtime/tls.h>
#define pthread __pthread
// This is what the thread pointer points to directly. On TLS_ABOVE_TP
// machines, the size of this is part of the ABI known to the compiler
// and linker.
typedef struct {
// The position of this pointer is part of the ABI on x86.
// It has the same value as the thread pointer itself.
uintptr_t tp;
void** dtv;
} tcbhead_t;
// The locations of these fields is part of the ABI known to the compiler.
typedef struct {
uintptr_t stack_guard;
uintptr_t unsafe_sp;
} tp_abi_t;
struct tls_dtor;
struct pthread {
#ifndef TLS_ABOVE_TP
// These must be the very first members.
tcbhead_t head;
tp_abi_t abi;
#endif
zxr_thread_t zxr_thread;
// The *_region fields describe whole memory regions reserved,
// including guard pages (for deallocation). safe_stack and
// unsafe_stack describe just the actual stack block between the
// guards.
struct iovec tcb_region;
struct iovec safe_stack, safe_stack_region;
struct iovec unsafe_stack, unsafe_stack_region;
struct tls_dtor* tls_dtors;
void* tsd[PTHREAD_KEYS_MAX];
int tsd_used;
int errno_value;
void* sanitizer_hook;
void* start_arg;
void* (*start)(void*);
void* result;
locale_t locale;
char* dlerror_buf;
int dlerror_flag;
#ifdef TLS_ABOVE_TP
// These must be the very last members.
tp_abi_t abi;
tcbhead_t head;
#endif
};
#ifdef TLS_ABOVE_TP
#define PTHREAD_TP_OFFSET offsetof(struct pthread, head)
#else
#define PTHREAD_TP_OFFSET 0
#endif
#define TP_OFFSETOF(field) \
((ptrdiff_t)offsetof(struct pthread, field) - PTHREAD_TP_OFFSET)
static_assert(TP_OFFSETOF(head) == 0,
"ABI tcbhead_t misplaced in struct pthread");
#ifdef ABI_TCBHEAD_SIZE
static_assert((sizeof(struct pthread) -
offsetof(struct pthread, head)) == ABI_TCBHEAD_SIZE,
"ABI tcbhead_t misplaced in struct pthread");
#endif
#if defined(__x86_64__) || defined(__aarch64__)
// The tlsdesc.s assembly code assumes this, though it's not part of the ABI.
static_assert(TP_OFFSETOF(head.dtv) == 8, "dtv misplaced in struct pthread");
#endif
static_assert(TP_OFFSETOF(abi.stack_guard) == ZX_TLS_STACK_GUARD_OFFSET,
"stack_guard not at ABI-mandated offset from thread pointer");
static_assert(TP_OFFSETOF(abi.unsafe_sp) == ZX_TLS_UNSAFE_SP_OFFSET,
"unsafe_sp not at ABI-mandated offset from thread pointer");
static inline void* pthread_to_tp(struct pthread* thread) {
return (void*)((char*)thread + PTHREAD_TP_OFFSET);
}
static inline struct pthread* tp_to_pthread(void* tp) {
return (struct pthread*)((char*)tp - PTHREAD_TP_OFFSET);
}
#ifndef DTP_OFFSET
#define DTP_OFFSET 0
#endif
#define SIGALL_SET ((sigset_t*)(const unsigned long long[2]){-1, -1})
#define SIGPT_SET \
((sigset_t*)(const unsigned long[_NSIG / 8 / sizeof(long)]){[sizeof(long) == 4] = \
3UL \
<< (32 * (sizeof(long) > 4))})
#define SIGTIMER_SET ((sigset_t*)(const unsigned long[_NSIG / 8 / sizeof(long)]){0x80000000})
#define PTHREAD_MUTEX_MASK (PTHREAD_MUTEX_RECURSIVE | PTHREAD_MUTEX_ERRORCHECK)
// The bit used in the recursive and errorchecking cases, which track thread owners.
#define PTHREAD_MUTEX_OWNED_LOCK_BIT 0x80000000
#define PTHREAD_MUTEX_OWNED_LOCK_MASK 0x7fffffff
extern void* __pthread_tsd_main[];
extern volatile size_t __pthread_tsd_size;
void* __tls_get_new(size_t*) ATTR_LIBC_VISIBILITY;
static inline pthread_t __pthread_self(void) {
return tp_to_pthread(zxr_tp_get());
}
static inline thrd_t __thrd_current(void) {
return (thrd_t)__pthread_self();
}
static inline pid_t __thread_get_tid(void) {
// We rely on the fact that the high bit is not set. For now,
// let's incur the cost of this check, until we consider the
// userspace handle value representation completely baked.
pid_t id = __pthread_self()->zxr_thread.handle;
if (id & PTHREAD_MUTEX_OWNED_LOCK_BIT) {
__builtin_trap();
}
return id;
}
int __pthread_create(pthread_t* __restrict, const pthread_attr_t* __restrict,
void* (*)(void*), void* __restrict) ATTR_LIBC_VISIBILITY;
int __pthread_detach(pthread_t t) ATTR_LIBC_VISIBILITY;
_Noreturn void __pthread_exit(void* result) ATTR_LIBC_VISIBILITY;
int __pthread_join(pthread_t t, void** result) ATTR_LIBC_VISIBILITY;
// Signal n (or all, for -1) threads on a pthread_cond_t or cnd_t.
void __private_cond_signal(void* condvar, int n) ATTR_LIBC_VISIBILITY;
int __pthread_key_create(tss_t*, void (*)(void*)) ATTR_LIBC_VISIBILITY;
int __pthread_key_delete(tss_t k) ATTR_LIBC_VISIBILITY;
// This is guaranteed to only return 0, EINVAL, or ETIMEDOUT.
int __timedwait(atomic_int*, int, clockid_t, const struct timespec*)
ATTR_LIBC_VISIBILITY;
// Loading a library can introduce more thread_local variables. Thread
// allocation bases bookkeeping decisions based on the current state
// of thread_locals in the program, so thread creation needs to be
// inhibited by a concurrent dlopen. This lock implements that
// exclusion.
void __thread_allocation_inhibit(void) ATTR_LIBC_VISIBILITY;
void __thread_allocation_release(void) ATTR_LIBC_VISIBILITY;
void __pthread_tsd_run_dtors(void) ATTR_LIBC_VISIBILITY;
#define DEFAULT_PTHREAD_ATTR \
((pthread_attr_t){ \
._a_stacksize = ZIRCON_DEFAULT_STACK_SIZE, \
._a_guardsize = PAGE_SIZE, \
})
pthread_t __allocate_thread(const pthread_attr_t* attr,
const char* thread_name,
char default_name[ZX_MAX_NAME_LEN])
__attribute__((nonnull(1,2))) ATTR_LIBC_VISIBILITY;
pthread_t __init_main_thread(zx_handle_t thread_self) ATTR_LIBC_VISIBILITY;
int __pthread_once(pthread_once_t*, void (*)(void)) ATTR_LIBC_VISIBILITY;
int __clock_gettime(clockid_t, struct timespec*) ATTR_LIBC_VISIBILITY;