commit 7dc565a2cffeda8e7f5cf2806c056298e7907aba (HEAD, refs/remotes/origin/master) Author: Eli Zaretskii Date: Sat Mar 28 10:36:01 2015 +0300 Fix blocking connections on MS-Windows (Bug#20159) src/w32.c (sys_connect): Fix a mistake in previous commit that broke blocking connections. diff --git a/src/ChangeLog b/src/ChangeLog index db3056c..0cc0a42 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2015-03-28 Eli Zaretskii + + * w32.c (sys_connect): Fix a mistake in previous commit that broke + blocking connections. (Bug#20159) + 2015-03-27 Paul Eggert Avoid some core dumps in X session management diff --git a/src/w32.c b/src/w32.c index 1917fea..6f16704 100644 --- a/src/w32.c +++ b/src/w32.c @@ -7489,8 +7489,8 @@ sys_connect (int s, const struct sockaddr * name, int namelen) errno = EINPROGRESS; /* that's what process.c expects */ fd_info[s].flags |= FILE_CONNECT; } - return rc; } + return rc; } errno = ENOTSOCK; return SOCKET_ERROR; commit 01d1024bec7781066440104ebee0b186382e10f3 Author: Paul Eggert Date: Fri Mar 27 12:10:44 2015 -0700 Avoid some core dumps in X session management Derived from a bug report by Nicolas Richard in: http://bugs.gnu.org/20191#20 * xsmfns.c (smc_save_yourself_CB): Don't dump core if invocation-name is not a string. Initialize user-login-name if it is not already initialized, and don't dump core if it is not a string. (create_client_leader_window): Don't dump core if x-resource-name and x-resource-class are not both strings. (x_session_initialize): Don't dump core if x-session-previous-id, invocation-directory, and invocation-name are not strings. diff --git a/src/ChangeLog b/src/ChangeLog index 61f2a84..db3056c 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,6 +1,20 @@ 2015-03-27 Paul Eggert + Avoid some core dumps in X session management + Derived from a bug report by Nicolas Richard in: + http://bugs.gnu.org/20191#20 + * xsmfns.c (smc_save_yourself_CB): Don't dump core if + invocation-name is not a string. Initialize user-login-name if it + is not already initialized, and don't dump core if it is not a + string. + (create_client_leader_window): Don't dump core if x-resource-name + and x-resource-class are not both strings. + (x_session_initialize): Don't dump core if x-session-previous-id, + invocation-directory, and invocation-name are not strings. + Port user-login-name initialization to Qnil == 0 + Derived from a bug report by Nicolas Richard in: + http://bugs.gnu.org/20191#20 * editfns.c (Fuser_login_name, Fuser_real_login_name) (syms_of_editfns): Don't rely on all-bits-zero being an Elisp integer, as this is no longer true now that Qnil == 0. diff --git a/src/xsmfns.c b/src/xsmfns.c index 0e635d3..375b51c 100644 --- a/src/xsmfns.c +++ b/src/xsmfns.c @@ -168,7 +168,6 @@ smc_save_yourself_CB (SmcConn smcConn, int val_idx = 0, vp_idx = 0; int props_idx = 0; int i; - char *cwd = get_current_dir_name (); char *smid_opt, *chdir_opt = NULL; /* How to start a new instance of Emacs. */ @@ -181,27 +180,34 @@ smc_save_yourself_CB (SmcConn smcConn, props[props_idx]->vals[0].value = emacs_program; ++props_idx; - /* The name of the program. */ - props[props_idx] = &prop_ptr[props_idx]; - props[props_idx]->name = xstrdup (SmProgram); - props[props_idx]->type = xstrdup (SmARRAY8); - props[props_idx]->num_vals = 1; - props[props_idx]->vals = &values[val_idx++]; - props[props_idx]->vals[0].length = SBYTES (Vinvocation_name); - props[props_idx]->vals[0].value = SDATA (Vinvocation_name); - ++props_idx; + if (STRINGP (Vinvocation_name)) + { + /* The name of the program. */ + props[props_idx] = &prop_ptr[props_idx]; + props[props_idx]->name = xstrdup (SmProgram); + props[props_idx]->type = xstrdup (SmARRAY8); + props[props_idx]->num_vals = 1; + props[props_idx]->vals = &values[val_idx++]; + props[props_idx]->vals[0].length = SBYTES (Vinvocation_name); + props[props_idx]->vals[0].value = SDATA (Vinvocation_name); + ++props_idx; + } /* User id. */ - props[props_idx] = &prop_ptr[props_idx]; - props[props_idx]->name = xstrdup (SmUserID); - props[props_idx]->type = xstrdup (SmARRAY8); - props[props_idx]->num_vals = 1; - props[props_idx]->vals = &values[val_idx++]; - props[props_idx]->vals[0].length = SBYTES (Vuser_login_name); - props[props_idx]->vals[0].value = SDATA (Vuser_login_name); - ++props_idx; - + Lisp_Object user_login_name = Fuser_login_name (Qnil); + if (STRINGP (user_login_name)) + { + props[props_idx] = &prop_ptr[props_idx]; + props[props_idx]->name = xstrdup (SmUserID); + props[props_idx]->type = xstrdup (SmARRAY8); + props[props_idx]->num_vals = 1; + props[props_idx]->vals = &values[val_idx++]; + props[props_idx]->vals[0].length = SBYTES (user_login_name); + props[props_idx]->vals[0].value = SDATA (user_login_name); + ++props_idx; + } + char *cwd = get_current_dir_name (); if (cwd) { props[props_idx] = &prop_ptr[props_idx]; @@ -372,6 +378,7 @@ create_client_leader_window (struct x_display_info *dpyinfo, char *client_ID) -1, -1, 1, 1, CopyFromParent, CopyFromParent, CopyFromParent); + validate_x_resource_name (); class_hints.res_name = SSDATA (Vx_resource_name); class_hints.res_class = SSDATA (Vx_resource_class); XSetClassHint (dpyinfo->display, w, &class_hints); @@ -402,22 +409,24 @@ x_session_initialize (struct x_display_info *dpyinfo) /* Check if we where started by the session manager. If so, we will have a previous id. */ - if (! NILP (Vx_session_previous_id) && STRINGP (Vx_session_previous_id)) + if (STRINGP (Vx_session_previous_id)) previous_id = SSDATA (Vx_session_previous_id); /* Construct the path to the Emacs program. */ - if (! NILP (Vinvocation_directory)) + if (STRINGP (Vinvocation_directory)) name_len += SBYTES (Vinvocation_directory); - name_len += SBYTES (Vinvocation_name); + if (STRINGP (Vinvocation_name)) + name_len += SBYTES (Vinvocation_name); /* This malloc will not be freed, but it is only done once, and hopefully not very large */ emacs_program = xmalloc (name_len + 1); char *z = emacs_program; - if (! NILP (Vinvocation_directory)) + if (STRINGP (Vinvocation_directory)) z = lispstpcpy (z, Vinvocation_directory); - lispstpcpy (z, Vinvocation_name); + if (STRINGP (Vinvocation_name)) + lispstpcpy (z, Vinvocation_name); /* The SM protocol says all callbacks are mandatory, so set up all here and in the mask passed to SmcOpenConnection. */ commit 589a4034b7af522c5b8107d5089fb1aec523a1e4 Author: Paul Eggert Date: Fri Mar 27 11:57:44 2015 -0700 Port user-login-name initialization to Qnil == 0 * editfns.c (Fuser_login_name, Fuser_real_login_name) (syms_of_editfns): Don't rely on all-bits-zero being an Elisp integer, as this is no longer true now that Qnil == 0. diff --git a/src/ChangeLog b/src/ChangeLog index 3f9ab4f..61f2a84 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,10 @@ 2015-03-27 Paul Eggert + Port user-login-name initialization to Qnil == 0 + * editfns.c (Fuser_login_name, Fuser_real_login_name) + (syms_of_editfns): Don't rely on all-bits-zero being an Elisp integer, + as this is no longer true now that Qnil == 0. + Assume !BROKEN_NON_BLOCKING_CONNECT From a suggestion by Eli Zaretskii in: http://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00824.html diff --git a/src/editfns.c b/src/editfns.c index f463890..7d3e462 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -1191,7 +1191,7 @@ of the user with that uid, or nil if there is no such user. */) /* Set up the user name info if we didn't do it before. (That can happen if Emacs is dumpable but you decide to run `temacs -l loadup' and not dump. */ - if (INTEGERP (Vuser_login_name)) + if (NILP (Vuser_login_name)) init_editfns (); if (NILP (uid)) @@ -1214,7 +1214,7 @@ This ignores the environment variables LOGNAME and USER, so it differs from /* Set up the user name info if we didn't do it before. (That can happen if Emacs is dumpable but you decide to run `temacs -l loadup' and not dump. */ - if (INTEGERP (Vuser_login_name)) + if (NILP (Vuser_login_name)) init_editfns (); return Vuser_real_login_name; } @@ -4955,6 +4955,7 @@ functions if all the text being accessed has this property. */); DEFVAR_LISP ("user-login-name", Vuser_login_name, doc: /* The user's name, taken from environment variables if possible. */); + Vuser_login_name = Qnil; DEFVAR_LISP ("user-real-login-name", Vuser_real_login_name, doc: /* The user's name, based upon the real uid only. */); commit 84c7c6fd2b9604fa28e0b834caa46423114b9c5b Author: Paul Eggert Date: Fri Mar 27 11:14:14 2015 -0700 Port etags to -DDEBUG * etags.c (xnew, xrnew) [DEBUG]: Don't include chkmalloc.h, which is not part of Emacs and is typically not installed. Instead, just invoke xmalloc and xrealloc as usual. Problem reported by Nicolas Richard in: http://bugs.gnu.org/20191#20 (xrnew): Avoid no-longer-needed cast to 'char *'. (xrealloc): First arg is now void *, not char *. diff --git a/lib-src/ChangeLog b/lib-src/ChangeLog index 9786809..0bb24c3 100644 --- a/lib-src/ChangeLog +++ b/lib-src/ChangeLog @@ -1,3 +1,14 @@ +2015-03-27 Paul Eggert + + Port etags to -DDEBUG + * etags.c (xnew, xrnew) [DEBUG]: Don't include chkmalloc.h, which + is not part of Emacs and is typically not installed. + Instead, just invoke xmalloc and xrealloc as usual. + Problem reported by Nicolas Richard in: + http://bugs.gnu.org/20191#20 + (xrnew): Avoid no-longer-needed cast to 'char *'. + (xrealloc): First arg is now void *, not char *. + 2015-03-06 Paul Eggert Random minor fixes for movemail diff --git a/lib-src/etags.c b/lib-src/etags.c index 7f18755..b1361db 100644 --- a/lib-src/etags.c +++ b/lib-src/etags.c @@ -176,17 +176,8 @@ char pot_etags_version[] = "@(#) pot revision number is 17.38.1.4"; * SYNOPSIS: Type *xnew (int n, Type); * void xrnew (OldPointer, int n, Type); */ -#if DEBUG -# include "chkmalloc.h" -# define xnew(n,Type) ((Type *) trace_malloc (__FILE__, __LINE__, \ - (n) * sizeof (Type))) -# define xrnew(op,n,Type) ((op) = (Type *) trace_realloc (__FILE__, __LINE__, \ - (char *) (op), (n) * sizeof (Type))) -#else -# define xnew(n,Type) ((Type *) xmalloc ((n) * sizeof (Type))) -# define xrnew(op,n,Type) ((op) = (Type *) xrealloc ( \ - (char *) (op), (n) * sizeof (Type))) -#endif +#define xnew(n, Type) ((Type *) xmalloc ((n) * sizeof (Type))) +#define xrnew(op, n, Type) ((op) = (Type *) xrealloc (op, (n) * sizeof (Type))) typedef void Lang_function (FILE *); @@ -348,7 +339,7 @@ static void canonicalize_filename (char *); static void linebuffer_init (linebuffer *); static void linebuffer_setlen (linebuffer *, int); static void *xmalloc (size_t); -static void *xrealloc (char *, size_t); +static void *xrealloc (void *, size_t); static char searchar = '/'; /* use /.../ searches */ @@ -6533,7 +6524,7 @@ xmalloc (size_t size) } static void * -xrealloc (char *ptr, size_t size) +xrealloc (void *ptr, size_t size) { void *result = realloc (ptr, size); if (result == NULL) commit 087fdee723da5968679d0392302043f10a5934bb Author: Paul Eggert Date: Fri Mar 27 10:36:15 2015 -0700 Assume !BROKEN_NON_BLOCKING_CONNECT From a suggestion by Eli Zaretskii in: http://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00824.html * process.c (NON_BLOCKING_CONNECT): Simplify by assuming that BROKEN_NON_BLOCKING_CONNECT is not defined. (SELECT_CAN_DO_WRITE_MASK): Remove, and assume it's now true. diff --git a/admin/CPP-DEFINES b/admin/CPP-DEFINES index 18423c2..796b57d 100644 --- a/admin/CPP-DEFINES +++ b/admin/CPP-DEFINES @@ -85,7 +85,6 @@ AMPERSAND_FULL_NAME BROKEN_DATAGRAM_SOCKETS BROKEN_FIONREAD BROKEN_GET_CURRENT_DIR_NAME -BROKEN_NON_BLOCKING_CONNECT BROKEN_PTY_READ_AFTER_EAGAIN DEFAULT_SOUND_DEVICE DEVICE_SEP diff --git a/src/ChangeLog b/src/ChangeLog index 98037e8..3f9ab4f 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,12 @@ +2015-03-27 Paul Eggert + + Assume !BROKEN_NON_BLOCKING_CONNECT + From a suggestion by Eli Zaretskii in: + http://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00824.html + * process.c (NON_BLOCKING_CONNECT): Simplify by assuming that + BROKEN_NON_BLOCKING_CONNECT is not defined. + (SELECT_CAN_DO_WRITE_MASK): Remove, and assume it's now true. + 2015-03-27 Eli Zaretskii * lread.c (substitute_object_recurse): For sub-char-tables, start diff --git a/src/process.c b/src/process.c index 3fe8644..2800fa5 100644 --- a/src/process.c +++ b/src/process.c @@ -195,24 +195,15 @@ static EMACS_INT process_tick; /* Number of events for which the user or sentinel has been notified. */ static EMACS_INT update_tick; -/* Define NON_BLOCKING_CONNECT if we can support non-blocking connects. */ +/* Define NON_BLOCKING_CONNECT if we can support non-blocking connects. + The code can be simplified by assuming NON_BLOCKING_CONNECT once + Emacs starts assuming POSIX 1003.1-2001 or later. */ -/* Only W32 has this, it really means that select can't take write mask. */ -#ifdef BROKEN_NON_BLOCKING_CONNECT -#undef NON_BLOCKING_CONNECT -enum { SELECT_CAN_DO_WRITE_MASK = false }; -#else -enum { SELECT_CAN_DO_WRITE_MASK = true }; -#ifndef NON_BLOCKING_CONNECT -#ifdef HAVE_SELECT -#if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX) -#if defined (EWOULDBLOCK) || defined (EINPROGRESS) -#define NON_BLOCKING_CONNECT -#endif /* EWOULDBLOCK || EINPROGRESS */ -#endif /* HAVE_GETPEERNAME || GNU_LINUX */ -#endif /* HAVE_SELECT */ -#endif /* NON_BLOCKING_CONNECT */ -#endif /* BROKEN_NON_BLOCKING_CONNECT */ +#if (defined HAVE_SELECT \ + && (defined GNU_LINUX || defined HAVE_GETPEERNAME) \ + && (defined EWOULDBLOCK || defined EINPROGRESS)) +# define NON_BLOCKING_CONNECT +#endif /* Define DATAGRAM_SOCKETS if datagrams can be used safely on this system. We need to read full packets, so we need a @@ -4606,7 +4597,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, Available = input_wait_mask; Writeok = write_mask; check_delay = wait_proc ? 0 : process_output_delay_count; - check_write = SELECT_CAN_DO_WRITE_MASK; + check_write = true; } /* If frame size has changed or the window is newly mapped, commit 9552a65f3187ad13f22392e17e3faf25defd259e Author: Eli Zaretskii Date: Fri Mar 27 16:16:36 2015 +0300 Fix crashes when restoring sub-char-tables from desktop file src/lread.c (substitute_object_recurse): For sub-char-tables, start the recursive SUBSTITUTE loop from index of 2, to skip the non-Lisp members of the sub-char-table. See the discussion at http://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00520.html for the details. diff --git a/src/ChangeLog b/src/ChangeLog index c003e44..98037e8 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,5 +1,11 @@ 2015-03-27 Eli Zaretskii + * lread.c (substitute_object_recurse): For sub-char-tables, start + the recursive SUBSTITUTE loop from index of 2, to skip the + non-Lisp members of the sub-char-table. See the discussion at + http://lists.gnu.org/archive/html/emacs-devel/2015-03/msg00520.html + for the details. + Support non-blocking connect on MS-Windows. Based on ideas from Kim F. Storm , see http://lists.gnu.org/archive/html/emacs-devel/2006-12/msg00873.html. diff --git a/src/lread.c b/src/lread.c index ae17529..050e43e 100644 --- a/src/lread.c +++ b/src/lread.c @@ -3280,7 +3280,7 @@ substitute_object_recurse (Lisp_Object object, Lisp_Object placeholder, Lisp_Obj { case Lisp_Vectorlike: { - ptrdiff_t i, length = 0; + ptrdiff_t i = 0, length = 0; if (BOOL_VECTOR_P (subtree)) return subtree; /* No sub-objects anyway. */ else if (CHAR_TABLE_P (subtree) || SUB_CHAR_TABLE_P (subtree) @@ -3295,7 +3295,9 @@ substitute_object_recurse (Lisp_Object object, Lisp_Object placeholder, Lisp_Obj behavior. */ wrong_type_argument (Qsequencep, subtree); - for (i = 0; i < length; i++) + if (SUB_CHAR_TABLE_P (subtree)) + i = 2; + for ( ; i < length; i++) SUBSTITUTE (AREF (subtree, i), ASET (subtree, i, true_value)); return subtree; commit 5ba79b03f3ca5f0d9fab833e78517faeb06c4c5f Author: Eli Zaretskii Date: Fri Mar 27 12:47:04 2015 +0300 Fix the bug number of previous commit: should be bug #20159. diff --git a/nt/ChangeLog b/nt/ChangeLog index da79681..b44988f 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog @@ -1,7 +1,7 @@ 2015-03-27 Eli Zaretskii * inc/ms-w32.h (BROKEN_NON_BLOCKING_CONNECT): Don't define. - (Bug#20207) + (Bug#20159) 2015-03-09 Eli Zaretskii diff --git a/src/ChangeLog b/src/ChangeLog index 67e04f6..c003e44 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -12,7 +12,7 @@ fd_info member doesn't have its FILE_CONNECT flag set, ignore the descriptor. Otherwise, acknowledge a successful non-blocking connect by resetting the FILE_CONNECT flag and setting cp->status - to STATUS_READ_ACKNOWLEDGED. (Bug#20207) + to STATUS_READ_ACKNOWLEDGED. (Bug#20159) * w32.h (STATUS_CONNECT_FAILED): New enumeration value. (struct _child_process): New member 'errcode'. commit d133cf839421462280ac0bfd9bd84c591f0e0249 Author: Eli Zaretskii Date: Fri Mar 27 12:44:31 2015 +0300 Support non-blocking connect on MS-Windows (Bug#20207) Based on ideas from Kim F. Storm , see http://lists.gnu.org/archive/html/emacs-devel/2006-12/msg00873.html. src/w32proc.c (reader_thread): If the FILE_CONNECT flag is set, call '_sys_wait_connect'. If it returns STATUS_CONNECT_FAILED, exit the thread with code 2. (sys_select): Support 'wfds' in addition to 'rfds'. If a descriptor in 'wfds' has its bit set, but the corresponding fd_info member doesn't have its FILE_CONNECT flag set, ignore the descriptor. Otherwise, acknowledge a successful non-blocking connect by resetting the FILE_CONNECT flag and setting cp->status to STATUS_READ_ACKNOWLEDGED. src/w32.h (STATUS_CONNECT_FAILED): New enumeration value. (struct _child_process): New member 'errcode'. (FILE_CONNECT): New flag. (_sys_wait_connect): Add prototype. src/w32.c (pfn_WSAEnumNetworkEvents): New function pointer. (init_winsock): Load WSAEnumNetworkEvents from winsock DLL. (set_errno): Map WSAEWOULDBLOCK and WSAENOTCONN. (sys_connect): Support non-blocking 'connect' calls by setting the FILE_CONNECT flag in the fd_info member and returning EINPROGRESS. (_sys_read_ahead): Add debug message if this function is called for a descriptor that waits for a non-blocking connect to complete. (_sys_wait_connect): New function. (sys_read): Support STATUS_CONNECT_FAILED. Return the error code recorded by _sys_wait_connect when the non-blocking connect failed. Don't call WSAGetLastError before a call to set_errno had a chance to use its value, since WSAGetLastError clears the last error. nt/inc/ms-w32.h (BROKEN_NON_BLOCKING_CONNECT): Don't define. diff --git a/nt/ChangeLog b/nt/ChangeLog index 9d954d5..da79681 100644 --- a/nt/ChangeLog +++ b/nt/ChangeLog @@ -1,3 +1,8 @@ +2015-03-27 Eli Zaretskii + + * inc/ms-w32.h (BROKEN_NON_BLOCKING_CONNECT): Don't define. + (Bug#20207) + 2015-03-09 Eli Zaretskii * INSTALL: Add some more installation instructions for mingw-get diff --git a/nt/inc/ms-w32.h b/nt/inc/ms-w32.h index c06ed58..da77290 100644 --- a/nt/inc/ms-w32.h +++ b/nt/inc/ms-w32.h @@ -58,10 +58,6 @@ along with GNU Emacs. If not, see . */ Look in for a timeval structure. */ #define HAVE_TIMEVAL 1 -/* But our select implementation doesn't allow us to make non-blocking - connects. So until that is fixed, this is necessary: */ -#define BROKEN_NON_BLOCKING_CONNECT 1 - /* And the select implementation does 1-byte read-ahead waiting for received packets, so datagrams are broken too. */ #define BROKEN_DATAGRAM_SOCKETS 1 diff --git a/src/ChangeLog b/src/ChangeLog index 632b798..67e04f6 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,38 @@ +2015-03-27 Eli Zaretskii + + Support non-blocking connect on MS-Windows. + Based on ideas from Kim F. Storm , see + http://lists.gnu.org/archive/html/emacs-devel/2006-12/msg00873.html. + + * w32proc.c (reader_thread): If the FILE_CONNECT flag is set, call + '_sys_wait_connect'. If it returns STATUS_CONNECT_FAILED, exit + the thread with code 2. + (sys_select): Support 'wfds' in addition to 'rfds'. If a + descriptor in 'wfds' has its bit set, but the corresponding + fd_info member doesn't have its FILE_CONNECT flag set, ignore the + descriptor. Otherwise, acknowledge a successful non-blocking + connect by resetting the FILE_CONNECT flag and setting cp->status + to STATUS_READ_ACKNOWLEDGED. (Bug#20207) + + * w32.h (STATUS_CONNECT_FAILED): New enumeration value. + (struct _child_process): New member 'errcode'. + (FILE_CONNECT): New flag. + (_sys_wait_connect): Add prototype. + + * w32.c (pfn_WSAEnumNetworkEvents): New function pointer. + (init_winsock): Load WSAEnumNetworkEvents from winsock DLL. + (set_errno): Map WSAEWOULDBLOCK and WSAENOTCONN. + (sys_connect): Support non-blocking 'connect' calls by setting the + FILE_CONNECT flag in the fd_info member and returning EINPROGRESS. + (_sys_read_ahead): Add debug message if this function is called + for a descriptor that waits for a non-blocking connect to complete. + (_sys_wait_connect): New function. + (sys_read): Support STATUS_CONNECT_FAILED. Return the error code + recorded by _sys_wait_connect when the non-blocking connect + failed. Don't call WSAGetLastError before a call to set_errno had + a chance to use its value, since WSAGetLastError clears the last + error. + 2015-03-25 Stefan Monnier * editfns.c (save_excursion_save): Don't save the mark. diff --git a/src/w32.c b/src/w32.c index 547db0f..1917fea 100644 --- a/src/w32.c +++ b/src/w32.c @@ -7038,6 +7038,9 @@ int (PASCAL *pfn_WSAStartup) (WORD wVersionRequired, LPWSADATA lpWSAData); void (PASCAL *pfn_WSASetLastError) (int iError); int (PASCAL *pfn_WSAGetLastError) (void); int (PASCAL *pfn_WSAEventSelect) (SOCKET s, HANDLE hEventObject, long lNetworkEvents); +int (PASCAL *pfn_WSAEnumNetworkEvents) (SOCKET s, HANDLE hEventObject, + WSANETWORKEVENTS *NetworkEvents); + HANDLE (PASCAL *pfn_WSACreateEvent) (void); int (PASCAL *pfn_WSACloseEvent) (HANDLE hEvent); int (PASCAL *pfn_socket) (int af, int type, int protocol); @@ -7123,6 +7126,7 @@ init_winsock (int load_now) LOAD_PROC (WSASetLastError); LOAD_PROC (WSAGetLastError); LOAD_PROC (WSAEventSelect); + LOAD_PROC (WSAEnumNetworkEvents); LOAD_PROC (WSACreateEvent); LOAD_PROC (WSACloseEvent); LOAD_PROC (socket); @@ -7206,6 +7210,8 @@ set_errno (void) case WSAEMFILE: errno = EMFILE; break; case WSAENAMETOOLONG: errno = ENAMETOOLONG; break; case WSAENOTEMPTY: errno = ENOTEMPTY; break; + case WSAEWOULDBLOCK: errno = EWOULDBLOCK; break; + case WSAENOTCONN: errno = ENOTCONN; break; default: errno = wsa_err; break; } } @@ -7473,8 +7479,18 @@ sys_connect (int s, const struct sockaddr * name, int namelen) { int rc = pfn_connect (SOCK_HANDLE (s), name, namelen); if (rc == SOCKET_ERROR) - set_errno (); - return rc; + { + set_errno (); + /* If this is a non-blocking 'connect', set the bit in flags + that will tell reader_thread to wait for connection + before trying to read. */ + if (errno == EWOULDBLOCK && (fd_info[s].flags & FILE_NDELAY) != 0) + { + errno = EINPROGRESS; /* that's what process.c expects */ + fd_info[s].flags |= FILE_CONNECT; + } + return rc; + } } errno = ENOTSOCK; return SOCKET_ERROR; @@ -7984,6 +8000,8 @@ _sys_read_ahead (int fd) emacs_abort (); } + if ((fd_info[fd].flags & FILE_CONNECT) != 0) + DebPrint (("_sys_read_ahead: read requested from fd %d, which waits for async connect!\n", fd)); cp->status = STATUS_READ_IN_PROGRESS; if (fd_info[fd].flags & FILE_PIPE) @@ -8106,6 +8124,60 @@ _sys_wait_accept (int fd) } int +_sys_wait_connect (int fd) +{ + HANDLE hEv; + child_process * cp; + int rc; + + if (fd < 0 || fd >= MAXDESC) + return STATUS_READ_ERROR; + + cp = fd_info[fd].cp; + if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY) + return STATUS_READ_ERROR; + + cp->status = STATUS_READ_FAILED; + + hEv = pfn_WSACreateEvent (); + rc = pfn_WSAEventSelect (SOCK_HANDLE (fd), hEv, FD_CONNECT); + if (rc != SOCKET_ERROR) + { + do { + rc = WaitForSingleObject (hEv, 500); + Sleep (5); + } while (rc == WAIT_TIMEOUT + && cp->status != STATUS_READ_ERROR + && cp->char_avail); + if (rc == WAIT_OBJECT_0) + { + /* We've got an event, but it could be a successful + connection, or it could be a failure. Find out + which one is it. */ + WSANETWORKEVENTS events; + + pfn_WSAEnumNetworkEvents (SOCK_HANDLE (fd), hEv, &events); + if ((events.lNetworkEvents & FD_CONNECT) != 0 + && events.iErrorCode[FD_CONNECT_BIT]) + { + cp->status = STATUS_CONNECT_FAILED; + cp->errcode = events.iErrorCode[FD_CONNECT_BIT]; + } + else + { + cp->status = STATUS_READ_SUCCEEDED; + cp->errcode = 0; + } + } + pfn_WSAEventSelect (SOCK_HANDLE (fd), NULL, 0); + } + else + pfn_WSACloseEvent (hEv); + + return cp->status; +} + +int sys_read (int fd, char * buffer, unsigned int count) { int nchars; @@ -8174,6 +8246,7 @@ sys_read (int fd, char * buffer, unsigned int count) ResetEvent (cp->char_avail); case STATUS_READ_ACKNOWLEDGED: + case STATUS_CONNECT_FAILED: break; default: @@ -8239,7 +8312,29 @@ sys_read (int fd, char * buffer, unsigned int count) { if (winsock_lib == NULL) emacs_abort (); - /* do the equivalent of a non-blocking read */ + /* When a non-blocking 'connect' call fails, + wait_reading_process_output detects this by calling + 'getpeername', and then attempts to obtain the connection + error code by trying to read 1 byte from the socket. If + we try to serve that read by calling 'recv' below, the + error we get is a generic WSAENOTCONN, not the actual + connection error. So instead, we use the actual error + code stashed by '_sys_wait_connect' in cp->errcode. + Alternatively, we could have used 'getsockopt', like on + GNU/Linux, but: (a) I have no idea whether the winsock + version could hang, as it does "on some systems" (see the + comment in process.c); and (b) 'getsockopt' on Windows is + documented to clear the socket error for the entire + process, which I'm not sure is TRT; FIXME. */ + if (current_status == STATUS_CONNECT_FAILED + && (fd_info[fd].flags & FILE_CONNECT) != 0 + && cp->errcode != 0) + { + pfn_WSASetLastError (cp->errcode); + set_errno (); + return -1; + } + /* Do the equivalent of a non-blocking read. */ pfn_ioctlsocket (SOCK_HANDLE (fd), FIONREAD, &waiting); if (waiting == 0 && nchars == 0) { @@ -8253,9 +8348,9 @@ sys_read (int fd, char * buffer, unsigned int count) int res = pfn_recv (SOCK_HANDLE (fd), buffer, count, 0); if (res == SOCKET_ERROR) { - DebPrint (("sys_read.recv failed with error %d on socket %ld\n", - pfn_WSAGetLastError (), SOCK_HANDLE (fd))); set_errno (); + DebPrint (("sys_read.recv failed with error %d on socket %ld\n", + errno, SOCK_HANDLE (fd))); return -1; } nchars += res; diff --git a/src/w32.h b/src/w32.h index 835557d..9b3521d 100644 --- a/src/w32.h +++ b/src/w32.h @@ -61,7 +61,8 @@ enum { STATUS_READ_IN_PROGRESS, STATUS_READ_FAILED, STATUS_READ_SUCCEEDED, - STATUS_READ_ACKNOWLEDGED + STATUS_READ_ACKNOWLEDGED, + STATUS_CONNECT_FAILED }; /* This structure is used for both pipes and sockets; for @@ -96,6 +97,8 @@ typedef struct _child_process /* Status of subprocess/connection and of reading its output. For values, see the enumeration above. */ volatile int status; + /* Used to store errno value of failed async 'connect' calls. */ + volatile int errcode; /* Holds a single character read by _sys_read_ahead, when a subprocess has some output ready. */ char chr; @@ -122,7 +125,8 @@ extern filedesc fd_info [ MAXDESC ]; /* fd_info flag definitions */ #define FILE_READ 0x0001 #define FILE_WRITE 0x0002 -#define FILE_LISTEN 0x0004 +#define FILE_LISTEN 0x0004 +#define FILE_CONNECT 0x0008 #define FILE_BINARY 0x0010 #define FILE_LAST_CR 0x0020 #define FILE_AT_EOF 0x0040 @@ -171,6 +175,7 @@ extern void init_timers (void); extern int _sys_read_ahead (int fd); extern int _sys_wait_accept (int fd); +extern int _sys_wait_connect (int fd); extern HMODULE w32_delayed_load (Lisp_Object); diff --git a/src/w32proc.c b/src/w32proc.c index 74731db..2d10534 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -1011,7 +1011,9 @@ reader_thread (void *arg) { int rc; - if (cp->fd >= 0 && fd_info[cp->fd].flags & FILE_LISTEN) + if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_CONNECT) != 0) + rc = _sys_wait_connect (cp->fd); + else if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_LISTEN) != 0) rc = _sys_wait_accept (cp->fd); else rc = _sys_read_ahead (cp->fd); @@ -1031,8 +1033,8 @@ reader_thread (void *arg) return 1; } - if (rc == STATUS_READ_ERROR) - return 1; + if (rc == STATUS_READ_ERROR || rc == STATUS_CONNECT_FAILED) + return 2; /* If the read died, the child has died so let the thread die */ if (rc == STATUS_READ_FAILED) @@ -1929,7 +1931,7 @@ int sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, struct timespec *timeout, void *ignored) { - SELECT_TYPE orfds; + SELECT_TYPE orfds, owfds; DWORD timeout_ms, start_time; int i, nh, nc, nr; DWORD active; @@ -1947,15 +1949,27 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, return 0; } - /* Otherwise, we only handle rfds, so fail otherwise. */ - if (rfds == NULL || wfds != NULL || efds != NULL) + /* Otherwise, we only handle rfds and wfds, so fail otherwise. */ + if ((rfds == NULL && wfds == NULL) || efds != NULL) { errno = EINVAL; return -1; } - orfds = *rfds; - FD_ZERO (rfds); + if (rfds) + { + orfds = *rfds; + FD_ZERO (rfds); + } + else + FD_ZERO (&orfds); + if (wfds) + { + owfds = *wfds; + FD_ZERO (wfds); + } + else + FD_ZERO (&owfds); nr = 0; /* If interrupt_handle is available and valid, always wait on it, to @@ -1970,7 +1984,7 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, /* Build a list of pipe handles to wait on. */ for (i = 0; i < nfds; i++) - if (FD_ISSET (i, &orfds)) + if (FD_ISSET (i, &orfds) || FD_ISSET (i, &owfds)) { if (i == 0) { @@ -1984,7 +1998,7 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, /* Check for any emacs-generated input in the queue since it won't be detected in the wait */ - if (detect_input_pending ()) + if (rfds && detect_input_pending ()) { FD_SET (i, rfds); return 1; @@ -1999,6 +2013,13 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, { /* Child process and socket/comm port input. */ cp = fd_info[i].cp; + if (FD_ISSET (i, &owfds) + && cp + && (fd_info[i].flags && FILE_CONNECT) == 0) + { + DebPrint (("sys_select: fd %d is in wfds, but FILE_CONNECT is reset!\n", i)); + cp = NULL; + } if (cp) { int current_status = cp->status; @@ -2007,6 +2028,8 @@ sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds, { /* Tell reader thread which file handle to use. */ cp->fd = i; + /* Zero out the error code. */ + cp->errcode = 0; /* Wake up the reader thread for this process */ cp->status = STATUS_READ_READY; if (!SetEvent (cp->char_consumed)) @@ -2197,7 +2220,7 @@ count_children: if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_AT_EOF) == 0) fd_info[cp->fd].flags |= FILE_SEND_SIGCHLD; - /* SIG_DFL for SIGCHLD is ignore */ + /* SIG_DFL for SIGCHLD is ignored */ else if (sig_handlers[SIGCHLD] != SIG_DFL && sig_handlers[SIGCHLD] != SIG_IGN) { @@ -2214,7 +2237,7 @@ count_children: errno = EINTR; return -1; } - else if (fdindex[active] == 0) + else if (rfds && fdindex[active] == 0) { /* Keyboard input available */ FD_SET (0, rfds); @@ -2222,9 +2245,33 @@ count_children: } else { - /* must be a socket or pipe - read ahead should have - completed, either succeeding or failing. */ - FD_SET (fdindex[active], rfds); + /* Must be a socket or pipe - read ahead should have + completed, either succeeding or failing. If this handle + was waiting for an async 'connect', reset the connect + flag, so it could read from now on. */ + if (wfds && (fd_info[fdindex[active]].flags & FILE_CONNECT) != 0) + { + cp = fd_info[fdindex[active]].cp; + if (cp) + { + /* Don't reset the FILE_CONNECT bit and don't + acknowledge the read if the status is + STATUS_CONNECT_FAILED or some other + failure. That's because the thread exits in those + cases, so it doesn't need the ACK, and we want to + keep the FILE_CONNECT bit as evidence that the + connect failed, to be checked in sys_read. */ + if (cp->status == STATUS_READ_SUCCEEDED) + { + fd_info[cp->fd].flags &= ~FILE_CONNECT; + cp->status = STATUS_READ_ACKNOWLEDGED; + } + ResetEvent (cp->char_avail); + } + FD_SET (fdindex[active], wfds); + } + else if (rfds) + FD_SET (fdindex[active], rfds); nr++; }