diff options
| author | John Birrell <jb@FreeBSD.org> | 1998-04-29 09:59:34 +0000 | 
|---|---|---|
| committer | John Birrell <jb@FreeBSD.org> | 1998-04-29 09:59:34 +0000 | 
| commit | 4a027d50c7f3f30178a89b3159ba9e4b44f06885 (patch) | |
| tree | b7a4ea836e97e11d436f9a6657ea6a9ec8036c33 /lib/libpthread/thread/thr_sig.c | |
| parent | ccf47cfcedf9f3db1780bc3b52ca0adb4480d3f6 (diff) | |
Notes
Diffstat (limited to 'lib/libpthread/thread/thr_sig.c')
| -rw-r--r-- | lib/libpthread/thread/thr_sig.c | 299 | 
1 files changed, 227 insertions, 72 deletions
| diff --git a/lib/libpthread/thread/thr_sig.c b/lib/libpthread/thread/thr_sig.c index e73d5a21e3af..1e5ee630ce4b 100644 --- a/lib/libpthread/thread/thr_sig.c +++ b/lib/libpthread/thread/thr_sig.c @@ -1,5 +1,5 @@  /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>   * All rights reserved.   *   * Redistribution and use in source and binary forms, with or without @@ -38,16 +38,79 @@  #include <pthread.h>  #include "pthread_private.h" +/* Static variables: */ +static int	volatile yield_on_unlock_dead		= 0; +static int	volatile yield_on_unlock_thread	= 0; +static long	volatile thread_dead_lock		= 0; +static long	volatile thread_link_list_lock		= 0; + +/* Lock the thread list: */ +void +_lock_thread_list() +{ +	/* Lock the thread list: */ +	_spinlock(&thread_link_list_lock); +} + +/* Lock the dead thread list: */ +void +_lock_dead_thread_list() +{ +	/* Lock the dead thread list: */ +	_spinlock(&thread_dead_lock); +} + +/* Lock the thread list: */ +void +_unlock_thread_list() +{ +	/* Unlock the thread list: */ +	_atomic_unlock(&thread_link_list_lock); + +	/* +	 * Check if a scheduler interrupt occurred while the thread +	 * list was locked: +	 */ +	if (yield_on_unlock_thread) { +		/* Reset the interrupt flag: */ +		yield_on_unlock_thread = 0; + +		/* This thread has overstayed it's welcome: */ +		sched_yield(); +	} +} + +/* Lock the dead thread list: */ +void +_unlock_dead_thread_list() +{ +	/* Unlock the dead thread list: */ +	_atomic_unlock(&thread_dead_lock); + +	/* +	 * Check if a scheduler interrupt occurred while the dead +	 * thread list was locked: +	 */ +	if (yield_on_unlock_dead) { +		/* Reset the interrupt flag: */ +		yield_on_unlock_dead = 0; + +		/* This thread has overstayed it's welcome: */ +		sched_yield(); +	} +} +  void  _thread_sig_handler(int sig, int code, struct sigcontext * scp)  {  	char            c;  	int             i; +	int		dispatch = 0;  	pthread_t       pthread;  	/*  	 * Check if the pthread kernel has unblocked signals (or is about to) -	 * and was on its way into a _thread_sys_select when the current +	 * and was on its way into a _select when the current  	 * signal interrupted it:   	 */  	if (_thread_kern_in_select) { @@ -57,51 +120,65 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)  		/*  		 * Write the signal number to the kernel pipe so that it will  		 * be ready to read when this signal handler returns. This -		 * means that the _thread_sys_select call will complete +		 * means that the _select call will complete  		 * immediately.   		 */ -		if (_thread_sys_write(_thread_kern_pipe[1], &c, 1) != 1) { -		} +		_thread_sys_write(_thread_kern_pipe[1], &c, 1);  	} +  	/* Check if the signal requires a dump of thread information: */ -	if (sig == SIGINFO) { +	if (sig == SIGINFO)  		/* Dump thread information to file: */  		_thread_dump_info(); -	} else { -		/* Handle depending on signal type: */ -		switch (sig) { -		/* Interval timer used for timeslicing: */ -		case SIGVTALRM: + +	/* Check if an interval timer signal: */ +	else if (sig == SIGVTALRM) { +		/* Check if the scheduler interrupt has come at an +		 * unfortunate time which one of the threads is +		 * modifying the thread list: +		 */ +		if (thread_link_list_lock)  			/* -			 * Don't add the signal to any thread.  Just want to -			 * call the scheduler: +			 * Set a flag so that the thread that has +			 * the lock yields when it unlocks the +			 * thread list:  			 */ -			break; +			yield_on_unlock_thread = 1; -		/* Child termination: */ -		case SIGCHLD: +		/* Check if the scheduler interrupt has come at an +		 * unfortunate time which one of the threads is +		 * modifying the dead thread list: +		 */ +		if (thread_dead_lock)  			/* -			 * Enter a loop to process each thread in the linked -			 * list: +			 * Set a flag so that the thread that has +			 * the lock yields when it unlocks the +			 * dead thread list:  			 */ -			for (pthread = _thread_link_list; pthread != NULL; -			     pthread = pthread->nxt) { -				/* -				 * Add the signal to the set of pending -				 * signals: -				 */ -				pthread->sigpend[sig] += 1; -				if (pthread->state == PS_WAIT_WAIT) { -					/* Reset the error: */ -					/* There should be another flag so that this is not required! ### */ -					_thread_seterrno(pthread, 0); - -					/* Change the state of the thread to run: */ -					PTHREAD_NEW_STATE(pthread,PS_RUNNING); -				} -			} +			yield_on_unlock_dead = 1; + +		/* +		 * Check if the kernel has not been interrupted while +		 * executing scheduler code: +		 */ +		else if (!_thread_kern_in_sched) { +			/* +			 * Schedule the next thread. This function is not +			 * expected to return because it will do a longjmp +			 * instead.  +			 */ +			_thread_kern_sched(scp);  			/* +			 * This point should not be reached, so abort the +			 * process:  +			 */ +			PANIC("Returned to signal function from scheduler"); +		} +	} else { +		/* Check if a child has terminated: */ +		if (sig == SIGCHLD) { +			/*  			 * Go through the file list and set all files  			 * to non-blocking again in case the child  			 * set some of them to block. Sigh. @@ -109,59 +186,137 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp)  			for (i = 0; i < _thread_dtablesize; i++) {  				/* Check if this file is used: */  				if (_thread_fd_table[i] != NULL) { -					/* Set the file descriptor to non-blocking: */ -					_thread_sys_fcntl(i, F_SETFL, _thread_fd_table[i]->flags | O_NONBLOCK); +					/* +					 * Set the file descriptor to +					 * non-blocking: +					 */ +					_thread_sys_fcntl(i, F_SETFL, +					    _thread_fd_table[i]->flags | +					    O_NONBLOCK);  				}  			} -			break; +		} -		/* Signals specific to the running thread: */ -		case SIGBUS: -		case SIGEMT: -		case SIGFPE: -		case SIGILL: -		case SIGPIPE: -		case SIGSEGV: -		case SIGSYS: -			/* Add the signal to the set of pending signals: */ -			_thread_run->sigpend[sig] += 1; -			break; +		/* +		 * POSIX says that pending SIGCONT signals are +		 * discarded when one of there signals occurs. +		 */ +		if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) { +			/* +			 * Enter a loop to discard pending SIGCONT +			 * signals: +			 */ +			for (pthread = _thread_link_list; +			    pthread != NULL; +			    pthread = pthread->nxt) +				sigdelset(&pthread->sigpend,SIGCONT); +		} -		/* Signals to send to all threads: */ -		default: +		/* Check if the signal is not being ignored: */ +		if (_thread_sigact[sig - 1].sa_handler != SIG_IGN)  			/*  			 * Enter a loop to process each thread in the linked  			 * list:   			 */  			for (pthread = _thread_link_list; pthread != NULL; -			     pthread = pthread->nxt) { -				/* -				 * Add the signal to the set of pending -				 * signals:  -				 */ -				pthread->sigpend[sig] += 1; -			} +			     pthread = pthread->nxt) +				_thread_signal(pthread,sig); + +		/* Dispatch pending signals to the running thread: */ +		_dispatch_signals(); +	} + +	/* Returns nothing. */ +	return; +} + +/* Perform thread specific actions in response to a signal: */ +void +_thread_signal(pthread_t pthread, int sig) +{ +	pthread_t saved; +	struct sigaction act; + +	/* +	 * Flag the signal as pending. It will be dispatched later. +	 */ +	sigaddset(&pthread->sigpend,sig); + +	/* Check if system calls are not restarted: */ +	if ((_thread_sigact[sig - 1].sa_flags & SA_RESTART) == 0) { +		/* +		 * Process according to thread state: +		 */ +		switch (pthread->state) { +		/* +		 * States which do not change when a signal is trapped: +		 */ +		case PS_COND_WAIT: +		case PS_DEAD: +		case PS_FDLR_WAIT: +		case PS_FDLW_WAIT: +		case PS_FILE_WAIT: +		case PS_JOIN: +		case PS_MUTEX_WAIT: +		case PS_RUNNING: +		case PS_STATE_MAX: +		case PS_SIGTHREAD: +		case PS_SUSPENDED: +			/* Nothing to do here. */ +			break; + +		/* +		 * States that are interrupted by the occurrence of a signal +		 * other than the scheduling alarm:  +		 */ +		case PS_FDR_WAIT: +		case PS_FDW_WAIT: +		case PS_SLEEP_WAIT: +		case PS_SIGWAIT: +		case PS_WAIT_WAIT: +		case PS_SELECT_WAIT: +			/* Flag the operation as interrupted: */ +			pthread->interrupted = 1; + +			/* Change the state of the thread to run: */ +			PTHREAD_NEW_STATE(pthread,PS_RUNNING); + +			/* Return the signal number: */ +			pthread->signo = sig;  			break;  		} +	} +} -		/* Check if the kernel is not locked: */ -		if (_thread_run != &_thread_kern_thread) { -			/* -			 * Schedule the next thread. This function is not -			 * expected to return because it will do a longjmp -			 * instead.  -			 */ -			_thread_kern_sched(scp); +/* Dispatch pending signals to the running thread: */ +void +_dispatch_signals() +{ +	int i; +	/* +	 * Check if there are pending signals for the running +	 * thread that aren't blocked: +	 */ +	if ((_thread_run->sigpend & ~_thread_run->sigmask) != 0) +		/* Look for all possible pending signals: */ +		for (i = 1; i < NSIG; i++)  			/* -			 * This point should not be reached, so abort the -			 * process:  +			 * Check that a custom handler is installed +			 * and if the signal is not blocked:  			 */ -			PANIC("Returned to signal function from scheduler"); -		} -	} +			if (_thread_sigact[i - 1].sa_handler != SIG_DFL && +			    _thread_sigact[i - 1].sa_handler != SIG_IGN && +			    sigismember(&_thread_run->sigpend,i) && +			    !sigismember(&_thread_run->sigmask,i)) { +				/* Clear the pending signal: */ +				sigdelset(&_thread_run->sigpend,i); -	/* Returns nothing. */ -	return; +				/* +				 * Dispatch the signal via the custom signal +				 * handler: +				 */ +				(*(_thread_sigact[i - 1].sa_handler))(i); +			}  }  #endif | 
