mirror of
				https://github.com/python/cpython.git
				synced 2025-11-01 06:01:29 +00:00 
			
		
		
		
	This closes patch:
[ 960406 ] unblock signals in threads although the changes do not correspond exactly to any patch attached to that report. Non-main threads no longer have all signals masked. A different interface to readline is used. The handling of signals inside calls to PyOS_Readline is now rather different. These changes are all a bit scary! Review and cross-platform testing much appreciated.
This commit is contained in:
		
							parent
							
								
									e3c330b42a
								
							
						
					
					
						commit
						30ea2f223f
					
				
					 10 changed files with 1576 additions and 3095 deletions
				
			
		|  | @ -327,6 +327,7 @@ Hannu Krosing | |||
| Andrew Kuchling | ||||
| Vladimir Kushnir | ||||
| Cameron Laird | ||||
| Andrew Langmead | ||||
| Detlef Lannert | ||||
| Soren Larsen | ||||
| Piers Lauder | ||||
|  |  | |||
|  | @ -656,6 +656,66 @@ setup_readline(void) | |||
| #endif | ||||
| } | ||||
| 
 | ||||
| /* Wrapper around GNU readline that handles signals differently. */ | ||||
| 
 | ||||
| 
 | ||||
| #if defined(HAVE_RL_CALLBACK) && defined(HAVE_SELECT) | ||||
| 
 | ||||
| static	char *completed_input_string; | ||||
| static void | ||||
| rlhandler(char *text) | ||||
| { | ||||
| 	completed_input_string = text; | ||||
| 	rl_callback_handler_remove(); | ||||
| } | ||||
| 
 | ||||
| extern PyThreadState* _PyOS_ReadlineTState; | ||||
| 
 | ||||
| static char * | ||||
| readline_until_enter_or_signal(char *prompt, int *signal) | ||||
| { | ||||
| 	char * not_done_reading = ""; | ||||
| 	fd_set selectset; | ||||
| 
 | ||||
| 	*signal = 0; | ||||
| #ifdef HAVE_RL_CATCH_SIGNAL | ||||
| 	rl_catch_signals = 0; | ||||
| #endif | ||||
| 
 | ||||
| 	rl_callback_handler_install (prompt, rlhandler); | ||||
| 	FD_ZERO(&selectset); | ||||
| 	FD_SET(fileno(rl_instream), &selectset); | ||||
| 	 | ||||
| 	completed_input_string = not_done_reading; | ||||
| 
 | ||||
| 	while(completed_input_string == not_done_reading) { | ||||
| 		int has_input; | ||||
| 
 | ||||
| 		has_input = select(fileno(rl_instream) + 1, &selectset, | ||||
| 				   NULL, NULL, NULL); | ||||
| 		if(has_input > 0) { | ||||
| 			rl_callback_read_char(); | ||||
| 		} | ||||
| 		else if (errno == EINTR) { | ||||
| 			int s; | ||||
| 			PyEval_RestoreThread(_PyOS_ReadlineTState); | ||||
| 			s = PyErr_CheckSignals(); | ||||
| 			PyThreadState_Swap(NULL);	 | ||||
| 			if (s < 0) { | ||||
| 				rl_free_line_state(); | ||||
| 				rl_cleanup_after_signal(); | ||||
| 				rl_callback_handler_remove(); | ||||
| 				*signal = 1; | ||||
| 				completed_input_string = NULL; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return completed_input_string; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
| /* Interrupt handler */ | ||||
| 
 | ||||
|  | @ -669,14 +729,13 @@ onintr(int sig) | |||
| } | ||||
| 
 | ||||
| 
 | ||||
| /* Wrapper around GNU readline that handles signals differently. */ | ||||
| 
 | ||||
| static char * | ||||
| call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) | ||||
| readline_until_enter_or_signal(char *prompt, int *signal) | ||||
| { | ||||
| 	size_t n; | ||||
| 	char *p, *q; | ||||
| 	PyOS_sighandler_t old_inthandler; | ||||
| 	char *p; | ||||
|      | ||||
| 	*signal = 0; | ||||
| 
 | ||||
| 	old_inthandler = PyOS_setsig(SIGINT, onintr); | ||||
| 	if (setjmp(jbuf)) { | ||||
|  | @ -685,8 +744,24 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) | |||
| 		sigrelse(SIGINT); | ||||
| #endif | ||||
| 		PyOS_setsig(SIGINT, old_inthandler); | ||||
| 		*signal = 1; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	p = readline(prompt); | ||||
| 	PyOS_setsig(SIGINT, old_inthandler); | ||||
| 
 | ||||
|     return p; | ||||
| } | ||||
| #endif /*defined(HAVE_RL_CALLBACK) && defined(HAVE_SELECT) */ | ||||
| 
 | ||||
| 
 | ||||
| static char * | ||||
| call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) | ||||
| { | ||||
| 	size_t n; | ||||
| 	char *p, *q; | ||||
| 	int signal; | ||||
| 
 | ||||
| 	rl_event_hook = PyOS_InputHook; | ||||
| 
 | ||||
| 	if (sys_stdin != rl_instream || sys_stdout != rl_outstream) { | ||||
|  | @ -697,16 +772,22 @@ call_readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) | |||
| #endif | ||||
| 	} | ||||
| 
 | ||||
| 	p = readline(prompt); | ||||
| 	PyOS_setsig(SIGINT, old_inthandler); | ||||
| 	p = readline_until_enter_or_signal(prompt, &signal); | ||||
| 	 | ||||
| 	/* We must return a buffer allocated with PyMem_Malloc. */ | ||||
| 	/* we got an interrupt signal */ | ||||
| 	if(signal) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	/* We got an EOF, return a empty string. */ | ||||
| 	if (p == NULL) { | ||||
| 		p = PyMem_Malloc(1); | ||||
| 		if (p != NULL) | ||||
| 			*p = '\0'; | ||||
| 		return p; | ||||
| 	} | ||||
| 
 | ||||
| 	/* we have a valid line */ | ||||
| 	n = strlen(p); | ||||
| 	if (n > 0) { | ||||
| 		char *line; | ||||
|  |  | |||
|  | @ -19,6 +19,14 @@ | |||
| extern char* vms__StdioReadline(FILE *sys_stdin, FILE *sys_stdout, char *prompt); | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| PyThreadState* _PyOS_ReadlineTState; | ||||
| 
 | ||||
| #if WITH_THREAD | ||||
| #include "pythread.h" | ||||
| static PyThread_type_lock _PyOS_ReadlineLock = NULL; | ||||
| #endif | ||||
| 
 | ||||
| int (*PyOS_InputHook)(void) = NULL; | ||||
| 
 | ||||
| #ifdef RISCOS | ||||
|  | @ -73,10 +81,13 @@ my_fgets(char *buf, int len, FILE *fp) | |||
| 		} | ||||
| #ifdef EINTR | ||||
| 		if (errno == EINTR) { | ||||
| 			if (PyOS_InterruptOccurred()) { | ||||
| 				return 1; /* Interrupt */ | ||||
| 			int s; | ||||
| 			PyEval_RestoreThread(_PyOS_ReadlineTState); | ||||
| 			s = PyErr_CheckSignals(); | ||||
| 			PyThreadState_Swap(NULL); | ||||
| 			if (s < 0) { | ||||
| 				return 1; | ||||
| 			} | ||||
| 			continue; | ||||
| 		} | ||||
| #endif | ||||
| 		if (PyOS_InterruptOccurred()) { | ||||
|  | @ -155,6 +166,13 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) | |||
| { | ||||
| 	char *rv; | ||||
| 
 | ||||
| 	if (_PyOS_ReadlineTState == PyThreadState_GET()) { | ||||
| 		PyErr_SetString(PyExc_RuntimeError, | ||||
| 				"can't re-enter readline"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	 | ||||
| 
 | ||||
| 	if (PyOS_ReadlineFunctionPointer == NULL) { | ||||
| #ifdef __VMS | ||||
|                 PyOS_ReadlineFunctionPointer = vms__StdioReadline; | ||||
|  | @ -163,7 +181,17 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) | |||
| #endif | ||||
| 	} | ||||
| 	 | ||||
| #if WITH_THREAD | ||||
| 	if (_PyOS_ReadlineLock == NULL) { | ||||
| 		_PyOS_ReadlineLock = PyThread_allocate_lock();		 | ||||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	_PyOS_ReadlineTState = PyThreadState_GET(); | ||||
| 	Py_BEGIN_ALLOW_THREADS | ||||
| #if WITH_THREAD | ||||
| 	PyThread_acquire_lock(_PyOS_ReadlineLock, 1); | ||||
| #endif | ||||
| 
 | ||||
|         /* This is needed to handle the unlikely case that the
 | ||||
|          * interpreter is in interactive mode *and* stdin/out are not | ||||
|  | @ -176,5 +204,12 @@ PyOS_Readline(FILE *sys_stdin, FILE *sys_stdout, char *prompt) | |||
|                 rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout, | ||||
|                                                      prompt); | ||||
| 	Py_END_ALLOW_THREADS | ||||
| 
 | ||||
| #if WITH_THREAD | ||||
| 	PyThread_release_lock(_PyOS_ReadlineLock); | ||||
| #endif | ||||
| 
 | ||||
| 	_PyOS_ReadlineTState = NULL; | ||||
| 
 | ||||
| 	return rv; | ||||
| } | ||||
|  |  | |||
|  | @ -1589,6 +1589,7 @@ builtin_raw_input(PyObject *self, PyObject *args) | |||
|                                   prompt); | ||||
| 		Py_XDECREF(po); | ||||
| 		if (s == NULL) { | ||||
| 			if (!PyErr_Occurred()) | ||||
| 				PyErr_SetNone(PyExc_KeyboardInterrupt); | ||||
| 			return NULL; | ||||
| 		} | ||||
|  |  | |||
|  | @ -318,7 +318,7 @@ static volatile int things_to_do = 0; | |||
| int | ||||
| Py_AddPendingCall(int (*func)(void *), void *arg) | ||||
| { | ||||
| 	static int busy = 0; | ||||
| 	static volatile int busy = 0; | ||||
| 	int i, j; | ||||
| 	/* XXX Begin critical section */ | ||||
| 	/* XXX If you want this to be safe against nested
 | ||||
|  |  | |||
|  | @ -1435,6 +1435,7 @@ err_input(perrdetail *err) | |||
| 		msg = "EOL while scanning single-quoted string"; | ||||
| 		break; | ||||
| 	case E_INTR: | ||||
| 		if (!PyErr_Occurred()) | ||||
| 			PyErr_SetNone(PyExc_KeyboardInterrupt); | ||||
| 		Py_XDECREF(v); | ||||
| 		return; | ||||
|  |  | |||
|  | @ -119,7 +119,6 @@ PyThread_start_new_thread(void (*func)(void *), void *arg) | |||
| { | ||||
| 	pthread_t th; | ||||
| 	int status; | ||||
|  	sigset_t oldmask, newmask; | ||||
| #if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) | ||||
| 	pthread_attr_t attrs; | ||||
| #endif | ||||
|  | @ -137,13 +136,6 @@ PyThread_start_new_thread(void (*func)(void *), void *arg) | |||
|         pthread_attr_setscope(&attrs, PTHREAD_SCOPE_SYSTEM); | ||||
| #endif | ||||
| 
 | ||||
| 	/* Mask all signals in the current thread before creating the new
 | ||||
| 	 * thread.  This causes the new thread to start with all signals | ||||
| 	 * blocked. | ||||
| 	 */ | ||||
| 	sigfillset(&newmask); | ||||
| 	SET_THREAD_SIGMASK(SIG_BLOCK, &newmask, &oldmask); | ||||
| 
 | ||||
| 	status = pthread_create(&th,  | ||||
| #if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) | ||||
| 				 &attrs, | ||||
|  | @ -154,9 +146,6 @@ PyThread_start_new_thread(void (*func)(void *), void *arg) | |||
| 				 (void *)arg | ||||
| 				 ); | ||||
| 
 | ||||
| 	/* Restore signal mask for original thread */ | ||||
| 	SET_THREAD_SIGMASK(SIG_SETMASK, &oldmask, NULL); | ||||
| 
 | ||||
| #if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED) | ||||
| 	pthread_attr_destroy(&attrs); | ||||
| #endif | ||||
|  |  | |||
							
								
								
									
										16
									
								
								configure.in
									
										
									
									
									
								
							
							
						
						
									
										16
									
								
								configure.in
									
										
									
									
									
								
							|  | @ -2783,6 +2783,11 @@ then | |||
|   [Define this if you have flockfile(), getc_unlocked(), and funlockfile()]) | ||||
| fi | ||||
| 
 | ||||
| # check for readline 2.1 | ||||
| AC_CHECK_LIB(readline, rl_callback_handler_install, | ||||
| 	AC_DEFINE(HAVE_RL_CALLBACK, 1, | ||||
|         [Define if you have readline 2.1]), , -ltermcap) | ||||
| 
 | ||||
| # check for readline 2.2 | ||||
| AC_TRY_CPP([#include <readline/readline.h>], | ||||
| have_readline=yes, have_readline=no) | ||||
|  | @ -2804,6 +2809,17 @@ AC_CHECK_LIB(readline, rl_completion_matches, | |||
| 	AC_DEFINE(HAVE_RL_COMPLETION_MATCHES, 1, | ||||
|         [Define if you have readline 4.2]), , -ltermcap) | ||||
| 
 | ||||
| # also in readline 4.2 | ||||
| AC_TRY_CPP([#include <readline/readline.h>], | ||||
| have_readline=yes, have_readline=no) | ||||
| if test $have_readline = yes | ||||
| then | ||||
|   AC_EGREP_HEADER([extern int rl_catch_signals;], | ||||
|   [readline/readline.h], | ||||
|   AC_DEFINE(HAVE_RL_CATCH_SIGNAL, 1, | ||||
|   [Define if you can turn off readline's signal handling.]), ) | ||||
| fi | ||||
| 
 | ||||
| AC_MSG_CHECKING(for broken nice()) | ||||
| AC_CACHE_VAL(ac_cv_broken_nice, [ | ||||
| AC_TRY_RUN([ | ||||
|  |  | |||
|  | @ -359,6 +359,12 @@ | |||
| /* Define to 1 if you have the `realpath' function. */ | ||||
| #undef HAVE_REALPATH | ||||
| 
 | ||||
| /* Define if you have readline 2.1 */ | ||||
| #undef HAVE_RL_CALLBACK | ||||
| 
 | ||||
| /* Define if you can turn off readline's signal handling. */ | ||||
| #undef HAVE_RL_CATCH_SIGNAL | ||||
| 
 | ||||
| /* Define if you have readline 2.2 */ | ||||
| #undef HAVE_RL_COMPLETION_APPEND_CHARACTER | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Michael W. Hudson
						Michael W. Hudson