diff options
Diffstat (limited to 'src/thread')
45 files changed, 386 insertions, 95 deletions
diff --git a/src/thread/aarch64/__set_thread_area.c b/src/thread/aarch64/__set_thread_area.c new file mode 100644 index 00000000..2ec788e8 --- /dev/null +++ b/src/thread/aarch64/__set_thread_area.c @@ -0,0 +1,27 @@ +#include <elf.h> +#include "libc.h" + +#define BITRANGE(a,b) (2*(1UL<<(b))-(1UL<<(a))) + +int __set_thread_area(void *p) +{ +	__asm__ __volatile__ ("msr tpidr_el0,%0" : : "r"(p) : "memory"); + +	/* Mask off hwcap bits for SME and unknown future features. This is +	 * necessary because SME is not safe to use without libc support for +	 * it, and we do not (yet) have such support. */ +	for (size_t *v = libc.auxv; *v; v+=2) { +		if (v[0]==AT_HWCAP) { +			v[1] &= ~BITRANGE(42,63); /* 42-47 are SME */ +		} else if (v[0]==AT_HWCAP2) { +			v[1] &= ~(BITRANGE(23,30) +			        | BITRANGE(37,42) +			        | BITRANGE(57,62)); +		} else if (v[0]==AT_HWCAP3 || v[0]==AT_HWCAP4) { +			v[0] = AT_IGNORE; +			v[1] = 0; +		} +	} + +	return 0; +} diff --git a/src/thread/aarch64/clone.s b/src/thread/aarch64/clone.s index e3c83395..aff8155b 100644 --- a/src/thread/aarch64/clone.s +++ b/src/thread/aarch64/clone.s @@ -24,7 +24,8 @@ __clone:  	// parent  	ret  	// child -1:	ldp x1,x0,[sp],#16 +1:	mov x29, 0 +	ldp x1,x0,[sp],#16  	blr x1  	mov x8,#93 // SYS_exit  	svc #0 diff --git a/src/thread/arm/clone.s b/src/thread/arm/clone.s index bb0965da..4ff0c0e8 100644 --- a/src/thread/arm/clone.s +++ b/src/thread/arm/clone.s @@ -19,7 +19,8 @@ __clone:  	ldmfd sp!,{r4,r5,r6,r7}  	bx lr -1:	mov r0,r6 +1:	mov fp,#0 +	mov r0,r6  	bl 3f  2:	mov r7,#1  	svc 0 diff --git a/src/thread/i386/__set_thread_area.s b/src/thread/i386/__set_thread_area.s index c2c21dd5..aa6852be 100644 --- a/src/thread/i386/__set_thread_area.s +++ b/src/thread/i386/__set_thread_area.s @@ -28,6 +28,7 @@ __set_thread_area:  	ret  2:  	mov %ebx,%ecx +	xor %eax,%eax  	xor %ebx,%ebx  	xor %edx,%edx  	mov %ebx,(%esp) diff --git a/src/thread/aarch64/__set_thread_area.s b/src/thread/loongarch64/__set_thread_area.s index fd0df34b..021307fc 100644 --- a/src/thread/aarch64/__set_thread_area.s +++ b/src/thread/loongarch64/__set_thread_area.s @@ -2,6 +2,6 @@  .hidden __set_thread_area  .type   __set_thread_area,@function  __set_thread_area: -	msr tpidr_el0,x0 -	mov w0,#0 -	ret +	move $tp, $a0 +	move $a0, $zero +	jr   $ra diff --git a/src/thread/loongarch64/__unmapself.s b/src/thread/loongarch64/__unmapself.s new file mode 100644 index 00000000..719ad056 --- /dev/null +++ b/src/thread/loongarch64/__unmapself.s @@ -0,0 +1,7 @@ +.global __unmapself +.type   __unmapself, @function +__unmapself: +	li.d    $a7, 215   # call munmap +	syscall 0 +	li.d    $a7, 93    # call exit +	syscall 0 diff --git a/src/thread/loongarch64/clone.s b/src/thread/loongarch64/clone.s new file mode 100644 index 00000000..cb4aacfc --- /dev/null +++ b/src/thread/loongarch64/clone.s @@ -0,0 +1,30 @@ +#__clone(func, stack, flags, arg, ptid, tls, ctid) +#         a0,    a1,   a2,    a3,  a4,  a5,   a6 +# sys_clone(flags, stack, ptid, ctid, tls) +#            a0,    a1,   a2,    a3,  a4 + +.global __clone +.hidden __clone +.type __clone,@function +__clone: +	bstrins.d $a1, $zero, 3, 0   #stack to 16 align +	# Save function pointer and argument pointer on new thread stack +	addi.d  $a1, $a1, -16 +	st.d    $a0, $a1, 0     # save function pointer +	st.d    $a3, $a1, 8     # save argument pointer +	or      $a0, $a2, $zero +	or      $a2, $a4, $zero +	or      $a3, $a6, $zero +	or      $a4, $a5, $zero +	ori     $a7, $zero, 220 +	syscall 0               # call clone + +	beqz    $a0, 1f         # whether child process +	jirl    $zero, $ra, 0   # parent process return +1: +	move    $fp, $zero +	ld.d    $t8, $sp, 0     # function pointer +	ld.d    $a0, $sp, 8     # argument pointer +	jirl    $ra, $t8, 0     # call the user's function +	ori     $a7, $zero, 93 +	syscall 0               # child process exit diff --git a/src/thread/loongarch64/syscall_cp.s b/src/thread/loongarch64/syscall_cp.s new file mode 100644 index 00000000..c057a97b --- /dev/null +++ b/src/thread/loongarch64/syscall_cp.s @@ -0,0 +1,29 @@ +.global __cp_begin +.hidden __cp_begin +.global __cp_end +.hidden __cp_end +.global __cp_cancel +.hidden __cp_cancel +.hidden __cancel +.global __syscall_cp_asm +.hidden __syscall_cp_asm +.type   __syscall_cp_asm,@function + +__syscall_cp_asm: +__cp_begin: +	ld.w $a0, $a0, 0 +	bnez $a0, __cp_cancel +	move $t8, $a1    # reserve system call number +	move $a0, $a2 +	move $a1, $a3 +	move $a2, $a4 +	move $a3, $a5 +	move $a4, $a6 +	move $a5, $a7 +	move $a7, $t8 +	syscall 0 +__cp_end: +	jr $ra +__cp_cancel: +	la.local $t8, __cancel +	jr $t8 diff --git a/src/thread/m68k/clone.s b/src/thread/m68k/clone.s index f6dfa06f..0134cf4e 100644 --- a/src/thread/m68k/clone.s +++ b/src/thread/m68k/clone.s @@ -18,7 +18,8 @@ __clone:  	beq 1f  	movem.l (%sp)+,%d2-%d5  	rts -1:	move.l %a1,-(%sp) +1:	suba.l %fp,%fp +	move.l %a1,-(%sp)  	jsr (%a0)  	move.l #1,%d0  	trap #0 diff --git a/src/thread/microblaze/clone.s b/src/thread/microblaze/clone.s index b68cc5fc..64e3f074 100644 --- a/src/thread/microblaze/clone.s +++ b/src/thread/microblaze/clone.s @@ -22,7 +22,8 @@ __clone:  	rtsd    r15, 8  	nop -1:	lwi     r3, r1, 0 +1:	add     r19, r0, r0 +	lwi     r3, r1, 0  	lwi     r5, r1, 4  	brald   r15, r3  	nop diff --git a/src/thread/mips/clone.s b/src/thread/mips/clone.s index 04463385..229b987e 100644 --- a/src/thread/mips/clone.s +++ b/src/thread/mips/clone.s @@ -27,7 +27,8 @@ __clone:  	addu $sp, $sp, 16  	jr $ra  	nop -1:	lw $25, 0($sp) +1:	move $fp, $0 +	lw $25, 0($sp)  	lw $4, 4($sp)  	jalr $25  	nop diff --git a/src/thread/mips64/clone.s b/src/thread/mips64/clone.s index 2d86899a..8de3db6c 100644 --- a/src/thread/mips64/clone.s +++ b/src/thread/mips64/clone.s @@ -25,7 +25,8 @@ __clone:  	nop  	jr	$ra  	nop -1:	ld	$25, 0($sp)	# function pointer +1:	move	$fp, $0 +	ld	$25, 0($sp)	# function pointer  	ld	$4, 8($sp)	# argument pointer  	jalr	$25		# call the user's function  	nop diff --git a/src/thread/mipsn32/clone.s b/src/thread/mipsn32/clone.s index 4d3c8c7a..9571231a 100644 --- a/src/thread/mipsn32/clone.s +++ b/src/thread/mipsn32/clone.s @@ -25,7 +25,8 @@ __clone:  	nop  	jr	$ra  	nop -1:	lw	$25, 0($sp)	# function pointer +1:	move	$fp, $0 +	lw	$25, 0($sp)	# function pointer  	lw	$4, 4($sp)	# argument pointer  	jalr	$25		# call the user's function  	nop diff --git a/src/thread/or1k/clone.s b/src/thread/or1k/clone.s index 2473ac20..b41488a2 100644 --- a/src/thread/or1k/clone.s +++ b/src/thread/or1k/clone.s @@ -6,6 +6,8 @@  .hidden __clone  .type   __clone,@function  __clone: +	l.xori	r11, r0, -4 +	l.and	r4, r4, r11  	l.addi	r4, r4, -8  	l.sw	0(r4), r3  	l.sw	4(r4), r6 @@ -23,7 +25,8 @@ __clone:  	l.jr	r9  	 l.nop -1:	l.lwz	r11, 0(r1) +1:	l.ori	r2, r0, 0 +	l.lwz	r11, 0(r1)  	l.jalr	r11  	 l.lwz	r3, 4(r1) diff --git a/src/thread/pthread_atfork.c b/src/thread/pthread_atfork.c index 76497401..26d32543 100644 --- a/src/thread/pthread_atfork.c +++ b/src/thread/pthread_atfork.c @@ -1,7 +1,13 @@  #include <pthread.h> +#include <errno.h>  #include "libc.h"  #include "lock.h" +#define malloc __libc_malloc +#define calloc undef +#define realloc undef +#define free undef +  static struct atfork_funcs {  	void (*prepare)(void);  	void (*parent)(void); @@ -34,7 +40,7 @@ void __fork_handler(int who)  int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))  {  	struct atfork_funcs *new = malloc(sizeof *new); -	if (!new) return -1; +	if (!new) return ENOMEM;  	LOCK(lock);  	new->next = funcs; diff --git a/src/thread/pthread_attr_get.c b/src/thread/pthread_attr_get.c index 4aa5afdb..f12ff442 100644 --- a/src/thread/pthread_attr_get.c +++ b/src/thread/pthread_attr_get.c @@ -70,7 +70,7 @@ int pthread_condattr_getpshared(const pthread_condattr_t *restrict a, int *restr  int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *restrict a, int *restrict protocol)  { -	*protocol = PTHREAD_PRIO_NONE; +	*protocol = a->__attr / 8U % 2;  	return 0;  }  int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict a, int *restrict pshared) diff --git a/src/thread/pthread_cancel.c b/src/thread/pthread_cancel.c index 2f9d5e97..139a6fc8 100644 --- a/src/thread/pthread_cancel.c +++ b/src/thread/pthread_cancel.c @@ -56,7 +56,12 @@ static void cancel_handler(int sig, siginfo_t *si, void *ctx)  	_sigaddset(&uc->uc_sigmask, SIGCANCEL); -	if (self->cancelasync || pc >= (uintptr_t)__cp_begin && pc < (uintptr_t)__cp_end) { +	if (self->cancelasync) { +		pthread_sigmask(SIG_SETMASK, &uc->uc_sigmask, 0); +		__cancel(); +	} + +	if (pc >= (uintptr_t)__cp_begin && pc < (uintptr_t)__cp_end) {  		uc->uc_mcontext.MC_PC = (uintptr_t)__cp_cancel;  #ifdef CANCEL_GOT  		uc->uc_mcontext.MC_GOT = CANCEL_GOT; @@ -77,7 +82,7 @@ void __testcancel()  static void init_cancellation()  {  	struct sigaction sa = { -		.sa_flags = SA_SIGINFO | SA_RESTART, +		.sa_flags = SA_SIGINFO | SA_RESTART | SA_ONSTACK,  		.sa_sigaction = cancel_handler  	};  	memset(&sa.sa_mask, -1, _NSIG/8); diff --git a/src/thread/pthread_cond_timedwait.c b/src/thread/pthread_cond_timedwait.c index d1501240..6b761455 100644 --- a/src/thread/pthread_cond_timedwait.c +++ b/src/thread/pthread_cond_timedwait.c @@ -146,14 +146,18 @@ relock:  	if (oldstate == WAITING) goto done; -	if (!node.next) a_inc(&m->_m_waiters); +	if (!node.next && !(m->_m_type & 8)) +		a_inc(&m->_m_waiters);  	/* Unlock the barrier that's holding back the next waiter, and  	 * either wake it or requeue it to the mutex. */ -	if (node.prev) -		unlock_requeue(&node.prev->barrier, &m->_m_lock, m->_m_type & 128); -	else -		a_dec(&m->_m_waiters); +	if (node.prev) { +		int val = m->_m_lock; +		if (val>0) a_cas(&m->_m_lock, val, val|0x80000000); +		unlock_requeue(&node.prev->barrier, &m->_m_lock, m->_m_type & (8|128)); +	} else if (!(m->_m_type & 8)) { +		a_dec(&m->_m_waiters);		 +	}  	/* Since a signal was consumed, cancellation is not permitted. */  	if (e == ECANCELED) e = 0; diff --git a/src/thread/pthread_create.c b/src/thread/pthread_create.c index 6bdfb44f..087f6206 100644 --- a/src/thread/pthread_create.c +++ b/src/thread/pthread_create.c @@ -69,15 +69,29 @@ _Noreturn void __pthread_exit(void *result)  	__pthread_tsd_run_dtors(); +	__block_app_sigs(&set); + +	/* This atomic potentially competes with a concurrent pthread_detach +	 * call; the loser is responsible for freeing thread resources. */ +	int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING); + +	if (state==DT_DETACHED && self->map_base) { +		/* Since __unmapself bypasses the normal munmap code path, +		 * explicitly wait for vmlock holders first. This must be +		 * done before any locks are taken, to avoid lock ordering +		 * issues that could lead to deadlock. */ +		__vm_wait(); +	} +  	/* Access to target the exiting thread with syscalls that use  	 * its kernel tid is controlled by killlock. For detached threads,  	 * any use past this point would have undefined behavior, but for -	 * joinable threads it's a valid usage that must be handled. */ +	 * joinable threads it's a valid usage that must be handled. +	 * Signals must be blocked since pthread_kill must be AS-safe. */  	LOCK(self->killlock); -	/* The thread list lock must be AS-safe, and thus requires -	 * application signals to be blocked before it can be taken. */ -	__block_app_sigs(&set); +	/* The thread list lock must be AS-safe, and thus depends on +	 * application signals being blocked above. */  	__tl_lock();  	/* If this is the only thread in the list, don't proceed with @@ -85,13 +99,24 @@ _Noreturn void __pthread_exit(void *result)  	 * signal state to prepare for exit to call atexit handlers. */  	if (self->next == self) {  		__tl_unlock(); -		__restore_sigs(&set);  		UNLOCK(self->killlock); +		self->detach_state = state; +		__restore_sigs(&set);  		exit(0);  	}  	/* At this point we are committed to thread termination. */ +	/* After the kernel thread exits, its tid may be reused. Clear it +	 * to prevent inadvertent use and inform functions that would use +	 * it that it's no longer available. At this point the killlock +	 * may be released, since functions that use it will consistently +	 * see the thread as having exited. Release it now so that no +	 * remaining locks (except thread list) are held if we end up +	 * resetting need_locks below. */ +	self->tid = 0; +	UNLOCK(self->killlock); +  	/* Process robust list in userspace to handle non-pshared mutexes  	 * and the detached thread case where the robust list head will  	 * be invalid when the kernel would process it. */ @@ -124,10 +149,6 @@ _Noreturn void __pthread_exit(void *result)  	self->prev->next = self->next;  	self->prev = self->next = self; -	/* This atomic potentially competes with a concurrent pthread_detach -	 * call; the loser is responsible for freeing thread resources. */ -	int state = a_cas(&self->detach_state, DT_JOINABLE, DT_EXITING); -  	if (state==DT_DETACHED && self->map_base) {  		/* Detached threads must block even implementation-internal  		 * signals, since they will not have a stack in their last @@ -139,24 +160,15 @@ _Noreturn void __pthread_exit(void *result)  		if (self->robust_list.off)  			__syscall(SYS_set_robust_list, 0, 3*sizeof(long)); -		/* Since __unmapself bypasses the normal munmap code path, -		 * explicitly wait for vmlock holders first. */ -		__vm_wait(); -  		/* The following call unmaps the thread's stack mapping  		 * and then exits without touching the stack. */  		__unmapself(self->map_base, self->map_size);  	}  	/* Wake any joiner. */ +	a_store(&self->detach_state, DT_EXITED);  	__wake(&self->detach_state, 1, 1); -	/* After the kernel thread exits, its tid may be reused. Clear it -	 * to prevent inadvertent use and inform functions that would use -	 * it that it's no longer available. */ -	self->tid = 0; -	UNLOCK(self->killlock); -  	for (;;) __syscall(SYS_exit, 0);  } @@ -313,7 +325,7 @@ int __pthread_create(pthread_t *restrict res, const pthread_attr_t *restrict att  		new->detach_state = DT_JOINABLE;  	}  	new->robust_list.head = &new->robust_list.head; -	new->CANARY = self->CANARY; +	new->canary = self->canary;  	new->sysinfo = self->sysinfo;  	/* Setup argument structure for the new thread on its stack. diff --git a/src/thread/pthread_detach.c b/src/thread/pthread_detach.c index 77772af2..d73a500e 100644 --- a/src/thread/pthread_detach.c +++ b/src/thread/pthread_detach.c @@ -5,8 +5,12 @@ static int __pthread_detach(pthread_t t)  {  	/* If the cas fails, detach state is either already-detached  	 * or exiting/exited, and pthread_join will trap or cleanup. */ -	if (a_cas(&t->detach_state, DT_JOINABLE, DT_DETACHED) != DT_JOINABLE) -		return __pthread_join(t, 0); +	if (a_cas(&t->detach_state, DT_JOINABLE, DT_DETACHED) != DT_JOINABLE) { +		int cs; +		__pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); +		__pthread_join(t, 0); +		__pthread_setcancelstate(cs, 0); +	}  	return 0;  } diff --git a/src/thread/pthread_getname_np.c b/src/thread/pthread_getname_np.c new file mode 100644 index 00000000..85504e45 --- /dev/null +++ b/src/thread/pthread_getname_np.c @@ -0,0 +1,25 @@ +#define _GNU_SOURCE +#include <fcntl.h> +#include <unistd.h> +#include <sys/prctl.h> + +#include "pthread_impl.h" + +int pthread_getname_np(pthread_t thread, char *name, size_t len) +{ +	int fd, cs, status = 0; +	char f[sizeof "/proc/self/task//comm" + 3*sizeof(int)]; + +	if (len < 16) return ERANGE; + +	if (thread == pthread_self()) +		return prctl(PR_GET_NAME, (unsigned long)name, 0UL, 0UL, 0UL) ? errno : 0; + +	snprintf(f, sizeof f, "/proc/self/task/%d/comm", thread->tid); +	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); +	if ((fd = open(f, O_RDONLY|O_CLOEXEC)) < 0 || (len = read(fd, name, len)) == -1) status = errno; +	else name[len-1] = 0; /* remove trailing new line only if successful */ +	if (fd >= 0) close(fd); +	pthread_setcancelstate(cs, 0); +	return status; +} diff --git a/src/thread/pthread_getschedparam.c b/src/thread/pthread_getschedparam.c index 1cba073d..c098befb 100644 --- a/src/thread/pthread_getschedparam.c +++ b/src/thread/pthread_getschedparam.c @@ -4,6 +4,8 @@  int pthread_getschedparam(pthread_t t, int *restrict policy, struct sched_param *restrict param)  {  	int r; +	sigset_t set; +	__block_app_sigs(&set);  	LOCK(t->killlock);  	if (!t->tid) {  		r = ESRCH; @@ -14,5 +16,6 @@ int pthread_getschedparam(pthread_t t, int *restrict policy, struct sched_param  		}  	}  	UNLOCK(t->killlock); +	__restore_sigs(&set);  	return r;  } diff --git a/src/thread/pthread_key_create.c b/src/thread/pthread_key_create.c index d1120941..39770c7a 100644 --- a/src/thread/pthread_key_create.c +++ b/src/thread/pthread_key_create.c @@ -1,4 +1,5 @@  #include "pthread_impl.h" +#include "fork_impl.h"  volatile size_t __pthread_tsd_size = sizeof(void *) * PTHREAD_KEYS_MAX;  void *__pthread_tsd_main[PTHREAD_KEYS_MAX] = { 0 }; @@ -20,6 +21,13 @@ static void dummy_0(void)  weak_alias(dummy_0, __tl_lock);  weak_alias(dummy_0, __tl_unlock); +void __pthread_key_atfork(int who) +{ +	if (who<0) __pthread_rwlock_rdlock(&key_lock); +	else if (!who) __pthread_rwlock_unlock(&key_lock); +	else key_lock = (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER; +} +  int __pthread_key_create(pthread_key_t *k, void (*dtor)(void *))  {  	pthread_t self = __pthread_self(); diff --git a/src/thread/pthread_kill.c b/src/thread/pthread_kill.c index 3d9395cb..79ddb209 100644 --- a/src/thread/pthread_kill.c +++ b/src/thread/pthread_kill.c @@ -4,9 +4,15 @@  int pthread_kill(pthread_t t, int sig)  {  	int r; +	sigset_t set; +	/* Block not just app signals, but internal ones too, since +	 * pthread_kill is used to implement pthread_cancel, which +	 * must be async-cancel-safe. */ +	__block_all_sigs(&set);  	LOCK(t->killlock);  	r = t->tid ? -__syscall(SYS_tkill, t->tid, sig)  		: (sig+0U >= _NSIG ? EINVAL : 0);  	UNLOCK(t->killlock); +	__restore_sigs(&set);  	return r;  } diff --git a/src/thread/pthread_mutex_destroy.c b/src/thread/pthread_mutex_destroy.c index 6d49e689..8d1bf77b 100644 --- a/src/thread/pthread_mutex_destroy.c +++ b/src/thread/pthread_mutex_destroy.c @@ -1,6 +1,10 @@ -#include <pthread.h> +#include "pthread_impl.h"  int pthread_mutex_destroy(pthread_mutex_t *mutex)  { +	/* If the mutex being destroyed is process-shared and has nontrivial +	 * type (tracking ownership), it might be in the pending slot of a +	 * robust_list; wait for quiescence. */ +	if (mutex->_m_type > 128) __vm_wait();  	return 0;  } diff --git a/src/thread/pthread_mutexattr_setprotocol.c b/src/thread/pthread_mutexattr_setprotocol.c index 511cc32d..8b80c1ce 100644 --- a/src/thread/pthread_mutexattr_setprotocol.c +++ b/src/thread/pthread_mutexattr_setprotocol.c @@ -1,24 +1,23 @@  #include "pthread_impl.h"  #include "syscall.h" -static pthread_once_t check_pi_once; -static int check_pi_result; - -static void check_pi() -{ -	volatile int lk = 0; -	check_pi_result = -__syscall(SYS_futex, &lk, FUTEX_LOCK_PI, 0, 0); -} +static volatile int check_pi_result = -1;  int pthread_mutexattr_setprotocol(pthread_mutexattr_t *a, int protocol)  { +	int r;  	switch (protocol) {  	case PTHREAD_PRIO_NONE:  		a->__attr &= ~8;  		return 0;  	case PTHREAD_PRIO_INHERIT: -		pthread_once(&check_pi_once, check_pi); -		if (check_pi_result) return check_pi_result; +		r = check_pi_result; +		if (r < 0) { +			volatile int lk = 0; +			r = -__syscall(SYS_futex, &lk, FUTEX_LOCK_PI, 0, 0); +			a_store(&check_pi_result, r); +		} +		if (r) return r;  		a->__attr |= 8;  		return 0;  	case PTHREAD_PRIO_PROTECT: diff --git a/src/thread/pthread_mutexattr_setrobust.c b/src/thread/pthread_mutexattr_setrobust.c index 04db92a6..30a9ac3b 100644 --- a/src/thread/pthread_mutexattr_setrobust.c +++ b/src/thread/pthread_mutexattr_setrobust.c @@ -1,22 +1,20 @@  #include "pthread_impl.h"  #include "syscall.h" -static pthread_once_t check_robust_once; -static int check_robust_result; - -static void check_robust() -{ -	void *p; -	size_t l; -	check_robust_result = -__syscall(SYS_get_robust_list, 0, &p, &l); -} +static volatile int check_robust_result = -1;  int pthread_mutexattr_setrobust(pthread_mutexattr_t *a, int robust)  {  	if (robust > 1U) return EINVAL;  	if (robust) { -		pthread_once(&check_robust_once, check_robust); -		if (check_robust_result) return check_robust_result; +		int r = check_robust_result; +		if (r < 0) { +			void *p; +			size_t l; +			r = -__syscall(SYS_get_robust_list, 0, &p, &l); +			a_store(&check_robust_result, r); +		} +		if (r) return r;  		a->__attr |= 4;  		return 0;  	} diff --git a/src/thread/pthread_setname_np.c b/src/thread/pthread_setname_np.c index 82d35e17..fc2d2306 100644 --- a/src/thread/pthread_setname_np.c +++ b/src/thread/pthread_setname_np.c @@ -19,7 +19,7 @@ int pthread_setname_np(pthread_t thread, const char *name)  	snprintf(f, sizeof f, "/proc/self/task/%d/comm", thread->tid);  	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); -	if ((fd = open(f, O_WRONLY)) < 0 || write(fd, name, len) < 0) status = errno; +	if ((fd = open(f, O_WRONLY|O_CLOEXEC)) < 0 || write(fd, name, len) < 0) status = errno;  	if (fd >= 0) close(fd);  	pthread_setcancelstate(cs, 0);  	return status; diff --git a/src/thread/pthread_setschedparam.c b/src/thread/pthread_setschedparam.c index 038d13d8..76d4d45a 100644 --- a/src/thread/pthread_setschedparam.c +++ b/src/thread/pthread_setschedparam.c @@ -4,8 +4,11 @@  int pthread_setschedparam(pthread_t t, int policy, const struct sched_param *param)  {  	int r; +	sigset_t set; +	__block_app_sigs(&set);  	LOCK(t->killlock);  	r = !t->tid ? ESRCH : -__syscall(SYS_sched_setscheduler, t->tid, policy, param);  	UNLOCK(t->killlock); +	__restore_sigs(&set);  	return r;  } diff --git a/src/thread/pthread_setschedprio.c b/src/thread/pthread_setschedprio.c index 5bf4a019..fc2e13dd 100644 --- a/src/thread/pthread_setschedprio.c +++ b/src/thread/pthread_setschedprio.c @@ -4,8 +4,11 @@  int pthread_setschedprio(pthread_t t, int prio)  {  	int r; +	sigset_t set; +	__block_app_sigs(&set);  	LOCK(t->killlock);  	r = !t->tid ? ESRCH : -__syscall(SYS_sched_setparam, t->tid, &prio);  	UNLOCK(t->killlock); +	__restore_sigs(&set);  	return r;  } diff --git a/src/thread/riscv32/__set_thread_area.s b/src/thread/riscv32/__set_thread_area.s new file mode 100644 index 00000000..828154d2 --- /dev/null +++ b/src/thread/riscv32/__set_thread_area.s @@ -0,0 +1,6 @@ +.global __set_thread_area +.type   __set_thread_area, %function +__set_thread_area: +	mv tp, a0 +	li a0, 0 +	ret diff --git a/src/thread/riscv32/__unmapself.s b/src/thread/riscv32/__unmapself.s new file mode 100644 index 00000000..2849119c --- /dev/null +++ b/src/thread/riscv32/__unmapself.s @@ -0,0 +1,7 @@ +.global __unmapself +.type __unmapself, %function +__unmapself: +	li a7, 215 # SYS_munmap +	ecall +	li a7, 93  # SYS_exit +	ecall diff --git a/src/thread/riscv32/clone.s b/src/thread/riscv32/clone.s new file mode 100644 index 00000000..e2116e33 --- /dev/null +++ b/src/thread/riscv32/clone.s @@ -0,0 +1,35 @@ +# __clone(func, stack, flags, arg, ptid, tls, ctid) +#           a0,    a1,    a2,  a3,   a4,  a5,   a6 + +# syscall(SYS_clone, flags, stack, ptid, tls, ctid) +#                a7     a0,    a1,   a2,  a3,   a4 + +.global __clone +.type  __clone, %function +__clone: +	# Save func and arg to stack +	andi a1, a1, -16 +	addi a1, a1, -16 +	sw a0, 0(a1) +	sw a3, 4(a1) + +	# Call SYS_clone +	mv a0, a2 +	mv a2, a4 +	mv a3, a5 +	mv a4, a6 +	li a7, 220 # SYS_clone +	ecall + +	beqz a0, 1f +	# Parent +	ret + +	# Child +1:      lw a1, 0(sp) +	lw a0, 4(sp) +	jalr a1 + +	# Exit +	li a7, 93 # SYS_exit +	ecall diff --git a/src/thread/riscv32/syscall_cp.s b/src/thread/riscv32/syscall_cp.s new file mode 100644 index 00000000..079d1ba0 --- /dev/null +++ b/src/thread/riscv32/syscall_cp.s @@ -0,0 +1,29 @@ +.global __cp_begin +.hidden __cp_begin +.global __cp_end +.hidden __cp_end +.global __cp_cancel +.hidden __cp_cancel +.hidden __cancel +.global __syscall_cp_asm +.hidden __syscall_cp_asm +.type __syscall_cp_asm, %function +__syscall_cp_asm: +__cp_begin: +	lw t0, 0(a0) +	bnez t0, __cp_cancel + +	mv t0, a1 +	mv a0, a2 +	mv a1, a3 +	mv a2, a4 +	mv a3, a5 +	mv a4, a6 +	mv a5, a7 +	lw a6, 0(sp) +	mv a7, t0 +	ecall +__cp_end: +	ret +__cp_cancel: +	tail __cancel diff --git a/src/thread/riscv64/clone.s b/src/thread/riscv64/clone.s index db908248..0e6f41a8 100644 --- a/src/thread/riscv64/clone.s +++ b/src/thread/riscv64/clone.s @@ -8,6 +8,7 @@  .type  __clone, %function  __clone:  	# Save func and arg to stack +	andi a1, a1, -16  	addi a1, a1, -16  	sd a0, 0(a1)  	sd a3, 8(a1) diff --git a/src/thread/s390x/__tls_get_offset.s b/src/thread/s390x/__tls_get_offset.s index 8ee92de8..056c9110 100644 --- a/src/thread/s390x/__tls_get_offset.s +++ b/src/thread/s390x/__tls_get_offset.s @@ -1,17 +1,17 @@  	.global __tls_get_offset  	.type __tls_get_offset,%function  __tls_get_offset: -	stmg  %r14, %r15, 112(%r15) -	aghi  %r15, -160 +	ear   %r3, %a0 +	sllg  %r3, %r3, 32 +	ear   %r3, %a1 -	la    %r2, 0(%r2, %r12) -	brasl %r14, __tls_get_addr +	la    %r1, 0(%r2, %r12) -	ear   %r1, %a0 -	sllg  %r1, %r1, 32 -	ear   %r1, %a1 +	lg    %r0, 0(%r1) +	sllg  %r4, %r0, 3 +	lg    %r5, 8(%r3) +	lg    %r2, 0(%r4, %r5) +	ag    %r2, 8(%r1) +	sgr   %r2, %r3 -	sgr   %r2, %r1 - -	lmg   %r14, %r15, 272(%r15)  	br    %r14 diff --git a/src/thread/s390x/clone.s b/src/thread/s390x/clone.s index 577748ea..2125f20b 100644 --- a/src/thread/s390x/clone.s +++ b/src/thread/s390x/clone.s @@ -17,6 +17,9 @@ __clone:  	# if (!tid) syscall(SYS_exit, a(d));  	# return tid; +	# preserve call-saved register used as syscall arg +	stg  %r6, 48(%r15) +  	# create initial stack frame for new thread  	nill %r3, 0xfff8  	aghi %r3, -160 @@ -35,6 +38,9 @@ __clone:  	lg   %r6, 160(%r15)  	svc  120 +	# restore call-saved register +	lg   %r6, 48(%r15) +  	# if error or if we're the parent, return  	ltgr %r2, %r2  	bnzr %r14 diff --git a/src/thread/s390x/syscall_cp.s b/src/thread/s390x/syscall_cp.s index c1da40de..d094cbf5 100644 --- a/src/thread/s390x/syscall_cp.s +++ b/src/thread/s390x/syscall_cp.s @@ -14,6 +14,7 @@ __cp_begin:  	icm %r2, 15, 0(%r2)  	jne __cp_cancel +	stg %r6, 48(%r15)  	stg %r7, 56(%r15)  	lgr %r1, %r3  	lgr %r2, %r4 @@ -26,6 +27,7 @@ __cp_begin:  __cp_end:  	lg  %r7, 56(%r15) +	lg  %r6, 48(%r15)  	br  %r14  __cp_cancel: diff --git a/src/thread/sem_getvalue.c b/src/thread/sem_getvalue.c index d9d83071..c0b7762d 100644 --- a/src/thread/sem_getvalue.c +++ b/src/thread/sem_getvalue.c @@ -1,8 +1,9 @@  #include <semaphore.h> +#include <limits.h>  int sem_getvalue(sem_t *restrict sem, int *restrict valp)  {  	int val = sem->__val[0]; -	*valp = val < 0 ? 0 : val; +	*valp = val & SEM_VALUE_MAX;  	return 0;  } diff --git a/src/thread/sem_open.c b/src/thread/sem_open.c index de8555c5..0ad29de9 100644 --- a/src/thread/sem_open.c +++ b/src/thread/sem_open.c @@ -12,6 +12,12 @@  #include <stdlib.h>  #include <pthread.h>  #include "lock.h" +#include "fork_impl.h" + +#define malloc __libc_malloc +#define calloc __libc_calloc +#define realloc undef +#define free undef  static struct {  	ino_t ino; @@ -19,6 +25,7 @@ static struct {  	int refcnt;  } *semtab;  static volatile int lock[1]; +volatile int *const __sem_open_lockptr = lock;  #define FLAGS (O_RDWR|O_NOFOLLOW|O_CLOEXEC|O_NONBLOCK) @@ -163,10 +170,12 @@ int sem_close(sem_t *sem)  	int i;  	LOCK(lock);  	for (i=0; i<SEM_NSEMS_MAX && semtab[i].sem != sem; i++); -	if (!--semtab[i].refcnt) { -		semtab[i].sem = 0; -		semtab[i].ino = 0; +	if (--semtab[i].refcnt) { +		UNLOCK(lock); +		return 0;  	} +	semtab[i].sem = 0; +	semtab[i].ino = 0;  	UNLOCK(lock);  	munmap(sem, sizeof *sem);  	return 0; diff --git a/src/thread/sem_post.c b/src/thread/sem_post.c index 31e3293d..1c8f763c 100644 --- a/src/thread/sem_post.c +++ b/src/thread/sem_post.c @@ -1,17 +1,21 @@  #include <semaphore.h> +#include <limits.h>  #include "pthread_impl.h"  int sem_post(sem_t *sem)  { -	int val, waiters, priv = sem->__val[2]; +	int val, new, waiters, priv = sem->__val[2];  	do {  		val = sem->__val[0];  		waiters = sem->__val[1]; -		if (val == SEM_VALUE_MAX) { +		if ((val & SEM_VALUE_MAX) == SEM_VALUE_MAX) {  			errno = EOVERFLOW;  			return -1;  		} -	} while (a_cas(sem->__val, val, val+1+(val<0)) != val); -	if (val<0 || waiters) __wake(sem->__val, 1, priv); +		new = val + 1; +		if (waiters <= 1) +			new &= ~0x80000000; +	} while (a_cas(sem->__val, val, new) != val); +	if (val<0 || waiters) __wake(sem->__val, waiters>1 ? 1 : -1, priv);  	return 0;  } diff --git a/src/thread/sem_timedwait.c b/src/thread/sem_timedwait.c index 58d3ebfe..aa67376c 100644 --- a/src/thread/sem_timedwait.c +++ b/src/thread/sem_timedwait.c @@ -1,4 +1,5 @@  #include <semaphore.h> +#include <limits.h>  #include "pthread_impl.h"  static void cleanup(void *p) @@ -13,14 +14,15 @@ int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict at)  	if (!sem_trywait(sem)) return 0;  	int spins = 100; -	while (spins-- && sem->__val[0] <= 0 && !sem->__val[1]) a_spin(); +	while (spins-- && !(sem->__val[0] & SEM_VALUE_MAX) && !sem->__val[1]) +		a_spin();  	while (sem_trywait(sem)) { -		int r; +		int r, priv = sem->__val[2];  		a_inc(sem->__val+1); -		a_cas(sem->__val, 0, -1); +		a_cas(sem->__val, 0, 0x80000000);  		pthread_cleanup_push(cleanup, (void *)(sem->__val+1)); -		r = __timedwait_cp(sem->__val, -1, CLOCK_REALTIME, at, sem->__val[2]); +		r = __timedwait_cp(sem->__val, 0x80000000, CLOCK_REALTIME, at, priv);  		pthread_cleanup_pop(1);  		if (r) {  			errno = r; diff --git a/src/thread/sem_trywait.c b/src/thread/sem_trywait.c index 04edf46b..beb435da 100644 --- a/src/thread/sem_trywait.c +++ b/src/thread/sem_trywait.c @@ -1,12 +1,12 @@  #include <semaphore.h> +#include <limits.h>  #include "pthread_impl.h"  int sem_trywait(sem_t *sem)  {  	int val; -	while ((val=sem->__val[0]) > 0) { -		int new = val-1-(val==1 && sem->__val[1]); -		if (a_cas(sem->__val, val, new)==val) return 0; +	while ((val=sem->__val[0]) & SEM_VALUE_MAX) { +		if (a_cas(sem->__val, val, val-1)==val) return 0;  	}  	errno = EAGAIN;  	return -1; diff --git a/src/thread/synccall.c b/src/thread/synccall.c index 648a6ad4..38597254 100644 --- a/src/thread/synccall.c +++ b/src/thread/synccall.c @@ -11,7 +11,7 @@ weak_alias(dummy_0, __tl_unlock);  static int target_tid;  static void (*callback)(void *), *context; -static sem_t target_sem, caller_sem; +static sem_t target_sem, caller_sem, exit_sem;  static void dummy(void *p)  { @@ -33,7 +33,7 @@ static void handler(int sig)  	/* Inform caller we've complered the callback and wait  	 * for the caller to release us to return. */  	sem_post(&caller_sem); -	sem_wait(&target_sem); +	sem_wait(&exit_sem);  	/* Inform caller we are returning and state is destroyable. */  	sem_post(&caller_sem); @@ -45,7 +45,7 @@ void __synccall(void (*func)(void *), void *ctx)  {  	sigset_t oldmask;  	int cs, i, r; -	struct sigaction sa = { .sa_flags = SA_RESTART, .sa_handler = handler }; +	struct sigaction sa = { .sa_flags = SA_RESTART | SA_ONSTACK, .sa_handler = handler };  	pthread_t self = __pthread_self(), td;  	int count = 0; @@ -62,8 +62,10 @@ void __synccall(void (*func)(void *), void *ctx)  	sem_init(&target_sem, 0, 0);  	sem_init(&caller_sem, 0, 0); +	sem_init(&exit_sem, 0, 0); -	if (!libc.threads_minus_1) goto single_threaded; +	if (!libc.threads_minus_1 || __syscall(SYS_gettid) != self->tid) +		goto single_threaded;  	callback = func;  	context = ctx; @@ -106,12 +108,13 @@ single_threaded:  	/* Only release the caught threads once all threads, including the  	 * caller, have returned from the callback function. */  	for (i=0; i<count; i++) -		sem_post(&target_sem); +		sem_post(&exit_sem);  	for (i=0; i<count; i++)  		sem_wait(&caller_sem);  	sem_destroy(&caller_sem);  	sem_destroy(&target_sem); +	sem_destroy(&exit_sem);  	pthread_setcancelstate(cs, 0);  	__tl_unlock(); diff --git a/src/thread/vmlock.c b/src/thread/vmlock.c index 75f3cb76..fa0a8e3c 100644 --- a/src/thread/vmlock.c +++ b/src/thread/vmlock.c @@ -1,6 +1,8 @@  #include "pthread_impl.h" +#include "fork_impl.h"  static volatile int vmlock[2]; +volatile int *const __vmlock_lockptr = vmlock;  void __vm_wait()  {  | 
