musl/src/internal/pthread_impl.h

121 lines
2.8 KiB
C
Raw Normal View History

2011-02-12 00:22:29 -05:00
#ifndef _PTHREAD_IMPL_H
#define _PTHREAD_IMPL_H
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <limits.h>
#include <inttypes.h>
#include <setjmp.h>
#include <string.h>
#include <time.h>
2011-05-30 01:41:23 -04:00
#include <locale.h>
2011-02-12 00:22:29 -05:00
#include "libc.h"
#include "syscall.h"
#include "atomic.h"
#include "futex.h"
#define pthread __pthread
struct pthread {
struct pthread *self;
void **dtv, *unused1, *unused2;
uintptr_t sysinfo;
uintptr_t canary;
2011-02-12 00:22:29 -05:00
pid_t tid, pid;
int tsd_used, errno_val, *errno_ptr;
volatile int cancel, canceldisable, cancelasync;
int detached;
2011-02-12 00:22:29 -05:00
unsigned char *map_base;
size_t map_size;
void *start_arg;
void *(*start)(void *);
void *result;
struct __ptcb *cancelbuf;
void **tsd;
pthread_attr_t attr;
volatile int dead;
struct {
void **head;
long off;
void *pending;
} robust_list;
int unblock_cancel;
int delete_timer;
2011-05-30 01:41:23 -04:00
locale_t locale;
int killlock[2];
int exitlock[2];
2011-02-12 00:22:29 -05:00
};
struct __timer {
int timerid;
pthread_t thread;
};
#define __SU (sizeof(size_t)/sizeof(int))
#define _a_stacksize __u.__s[0]
#define _a_guardsize __u.__s[1]
#define _a_stackaddr __u.__s[2]
#define _a_detach __u.__i[3*__SU+0]
#define _m_type __u.__i[0]
#define _m_lock __u.__i[1]
#define _m_waiters __u.__i[2]
#define _m_prev __u.__p[3]
#define _m_next __u.__p[4]
#define _m_count __u.__i[5]
#define _c_mutex __u.__p[0]
#define _c_seq __u.__i[2]
#define _c_waiters __u.__i[3]
#define _c_clock __u.__i[4]
#define _c_lock __u.__i[5]
#define _c_lockwait __u.__i[6]
#define _c_waiters2 __u.__i[7]
#define _c_destroy __u.__i[8]
overhaul rwlocks to address several issues like mutexes and semaphores, rwlocks suffered from a race condition where the unlock operation could access the lock memory after another thread successfully obtained the lock (and possibly destroyed or unmapped the object). this has been fixed in the same way it was fixed for other lock types. in addition, the previous implementation favored writers over readers. in the absence of other considerations, that is the best behavior for rwlocks, and posix explicitly allows it. however posix also requires read locks to be recursive. if writers are favored, any attempt to obtain a read lock while a writer is waiting for the lock will fail, causing "recursive" read locks to deadlock. this can be avoided by keeping track of which threads already hold read locks, but doing so requires unbounded memory usage, and there must be a fallback case that favors readers in case memory allocation failed. and all of this must be synchronized. the cost, complexity, and risk of errors in getting it right is too great, so we simply favor readers. tracking of the owner of write locks has been removed, as it was not useful for anything. it could allow deadlock detection, but it's not clear to me that returning EDEADLK (which a buggy program is likely to ignore) is better than deadlocking; at least the latter behavior prevents further data corruption. a correct program cannot invoke this situation anyway. the reader count and write lock state, as well as the "last minute" waiter flag have all been combined into a single atomic lock. this means all state transitions for the lock are atomic compare-and-swap operations. this makes establishing correctness much easier and may improve performance. finally, some code duplication has been cleaned up. more is called for, especially the standard __timedwait idiom repeated in all locks.
2011-08-03 10:21:32 -04:00
#define _rw_lock __u.__i[0]
#define _rw_waiters __u.__i[1]
#define _b_lock __u.__i[0]
#define _b_waiters __u.__i[1]
#define _b_limit __u.__i[2]
#define _b_count __u.__i[3]
#define _b_waiters2 __u.__i[4]
#define _b_inst __u.__p[3]
#include "pthread_arch.h"
2011-02-12 00:22:29 -05:00
#define SIGTIMER 32
#define SIGCANCEL 33
new attempt at making set*id() safe and robust changing credentials in a multi-threaded program is extremely difficult on linux because it requires synchronizing the change between all threads, which have their own thread-local credentials on the kernel side. this is further complicated by the fact that changing the real uid can fail due to exceeding RLIMIT_NPROC, making it possible that the syscall will succeed in some threads but fail in others. the old __rsyscall approach being replaced was robust in that it would report failure if any one thread failed, but in this case, the program would be left in an inconsistent state where individual threads might have different uid. (this was not as bad as glibc, which would sometimes even fail to report the failure entirely!) the new approach being committed refuses to change real user id when it cannot temporarily set the rlimit to infinity. this is completely POSIX conformant since POSIX does not require an implementation to allow real-user-id changes for non-privileged processes whatsoever. still, setting the real uid can fail due to memory allocation in the kernel, but this can only happen if there is not already a cached object for the target user. thus, we forcibly serialize the syscalls attempts, and fail the entire operation on the first failure. this *should* lead to an all-or-nothing success/failure result, but it's still fragile and highly dependent on kernel developers not breaking things worse than they're already broken. ideally linux will eventually add a CLONE_USERCRED flag that would give POSIX conformant credential changes without any hacks from userspace, and all of this code would become redundant and could be removed ~10 years down the line when everyone has abandoned the old broken kernels. i'm not holding my breath...
2011-07-29 22:59:44 -04:00
#define SIGSYNCCALL 34
#define SIGALL_SET ((sigset_t *)(const unsigned long long [2]){ -1,-1 })
#define SIGPT_SET \
((sigset_t *)(const unsigned long [__SYSCALL_SSLEN/sizeof(long)]){ \
[sizeof(long)==4] = 3UL<<(32*(sizeof(long)>4)) })
#define SIGTIMER_SET \
((sigset_t *)(const unsigned long [__SYSCALL_SSLEN/sizeof(long)]){ \
0x80000000 })
2011-02-12 00:22:29 -05:00
pthread_t __pthread_self_init(void);
int __clone(int (*)(void *), void *, int, void *, ...);
int __set_thread_area(void *);
2011-02-12 00:22:29 -05:00
int __libc_sigaction(int, const struct sigaction *, struct sigaction *);
int __libc_sigprocmask(int, const sigset_t *, sigset_t *);
void __lock(volatile int *);
void __unmapself(void *, size_t);
int __timedwait(volatile int *, int, clockid_t, const struct timespec *, void (*)(void *), void *, int);
2011-02-12 00:22:29 -05:00
void __wait(volatile int *, volatile int *, int, int);
void __wake(volatile int *, int, int);
new attempt at making set*id() safe and robust changing credentials in a multi-threaded program is extremely difficult on linux because it requires synchronizing the change between all threads, which have their own thread-local credentials on the kernel side. this is further complicated by the fact that changing the real uid can fail due to exceeding RLIMIT_NPROC, making it possible that the syscall will succeed in some threads but fail in others. the old __rsyscall approach being replaced was robust in that it would report failure if any one thread failed, but in this case, the program would be left in an inconsistent state where individual threads might have different uid. (this was not as bad as glibc, which would sometimes even fail to report the failure entirely!) the new approach being committed refuses to change real user id when it cannot temporarily set the rlimit to infinity. this is completely POSIX conformant since POSIX does not require an implementation to allow real-user-id changes for non-privileged processes whatsoever. still, setting the real uid can fail due to memory allocation in the kernel, but this can only happen if there is not already a cached object for the target user. thus, we forcibly serialize the syscalls attempts, and fail the entire operation on the first failure. this *should* lead to an all-or-nothing success/failure result, but it's still fragile and highly dependent on kernel developers not breaking things worse than they're already broken. ideally linux will eventually add a CLONE_USERCRED flag that would give POSIX conformant credential changes without any hacks from userspace, and all of this code would become redundant and could be removed ~10 years down the line when everyone has abandoned the old broken kernels. i'm not holding my breath...
2011-07-29 22:59:44 -04:00
void __synccall_lock();
void __synccall_unlock();
#define DEFAULT_STACK_SIZE 81920
2011-02-12 00:22:29 -05:00
#define DEFAULT_GUARD_SIZE PAGE_SIZE
#endif