From mboxrd@z Thu Jan 1 00:00:00 1970 Path: quimby.gnus.org!not-for-mail From: storm@cua.dk (Kim F. Storm) Newsgroups: gmane.emacs.devel Subject: Re: Non-blocking open-network-stream Date: 25 Feb 2002 23:38:52 +0100 Message-ID: <5xg03pyyo3.fsf@kfs2.cua.dk> References: <5xwux64cxe.fsf@kfs2.cua.dk> NNTP-Posting-Host: quimby2.netfonds.no Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: quimby2.netfonds.no 1014676995 1662 195.204.10.66 (25 Feb 2002 22:43:15 GMT) X-Complaints-To: usenet@quimby2.netfonds.no NNTP-Posting-Date: 25 Feb 2002 22:43:15 GMT Cc: Helmut Eller Original-Received: from fencepost.gnu.org ([199.232.76.164]) by quimby2.netfonds.no with esmtp (Exim 3.12 #1 (Debian)) id 16fTpy-0000Qi-00 for ; Mon, 25 Feb 2002 23:43:14 +0100 Original-Received: from localhost ([127.0.0.1] helo=fencepost.gnu.org) by fencepost.gnu.org with esmtp (Exim 3.33 #1 (Debian)) id 16fTmW-0003O6-00; Mon, 25 Feb 2002 17:39:40 -0500 Original-Received: from mail.filanet.dk ([195.215.206.179]) by fencepost.gnu.org with smtp (Exim 3.33 #1 (Debian)) id 16fTkm-0003Gt-00 for ; Mon, 25 Feb 2002 17:37:53 -0500 Original-Received: from kfs2.cua.dk.cua.dk (unknown [10.1.82.3]) by mail.filanet.dk (Postfix) with SMTP id D36697C035; Mon, 25 Feb 2002 22:37:45 +0000 (GMT) Original-To: emacs-devel@gnu.org In-Reply-To: <5xwux64cxe.fsf@kfs2.cua.dk> Original-Lines: 740 User-Agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.2.50 Errors-To: emacs-devel-admin@gnu.org X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.0.5 Precedence: bulk List-Help: List-Post: List-Subscribe: , List-Id: Emacs development discussions. List-Unsubscribe: , List-Archive: Xref: quimby.gnus.org gmane.emacs.devel:1540 X-Report-Spam: http://spam.gmane.org/gmane.emacs.devel:1540 Here is my second attempt at a patch to support non-blocking open-network-stream. If I don't receive any comments on this, I'll install it later this week. Index: process.c =================================================================== RCS file: /cvs/emacs/src/process.c,v retrieving revision 1.351 diff -c -r1.351 process.c *** process.c 7 Jan 2002 21:16:38 -0000 1.351 --- process.c 25 Feb 2002 22:33:22 -0000 *************** *** 1,6 **** /* Asynchronous subprocess control for GNU Emacs. ! Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 96, 98, 1999, 2001 ! Free Software Foundation, Inc. This file is part of GNU Emacs. --- 1,6 ---- /* Asynchronous subprocess control for GNU Emacs. ! Copyright (C) 1985, 86, 87, 88, 93, 94, 95, 96, 98, 1999, ! 2001, 2002 Free Software Foundation, Inc. This file is part of GNU Emacs. *************** *** 112,118 **** #include "atimer.h" Lisp_Object Qprocessp; ! Lisp_Object Qrun, Qstop, Qsignal, Qopen, Qclosed; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ --- 112,119 ---- #include "atimer.h" Lisp_Object Qprocessp; ! Lisp_Object Qrun, Qstop, Qsignal; ! Lisp_Object Qopen, Qclosed, Qconnect, Qfailed; Lisp_Object Qlast_nonmenu_event; /* Qexit is declared and initialized in eval.c. */ *************** *** 173,178 **** --- 174,199 ---- /* Number of events for which the user or sentinel has been notified. */ int update_tick; + /* Define NON_BLOCKING_CONNECT if we can support non-blocking connects. */ + + #ifdef BROKEN_NON_BLOCKING_CONNECT + #undef NON_BLOCKING_CONNECT + #else + #ifndef NON_BLOCKING_CONNECT + #ifdef HAVE_SOCKETS + #ifdef HAVE_SELECT + #if defined (HAVE_GETPEERNAME) || defined (GNU_LINUX) + #if defined (O_NONBLOCK) || defined (O_NDELAY) + #if defined (EWOULDBLOCK) || defined (EINPROGRESS) + #define NON_BLOCKING_CONNECT + #endif /* EWOULDBLOCK || EINPROGRESS */ + #endif /* O_NONBLOCK || O_NDELAY */ + #endif /* HAVE_GETPEERNAME || GNU_LINUX */ + #endif /* HAVE_SELECT */ + #endif /* HAVE_SOCKETS */ + #endif /* NON_BLOCKING_CONNECT */ + #endif /* BROKEN_NON_BLOCKING_CONNECT */ + #include "sysselect.h" extern int keyboard_bit_set P_ ((SELECT_TYPE *)); *************** *** 195,200 **** --- 216,230 ---- static SELECT_TYPE non_process_wait_mask; + /* Mask of bits indicating the descriptors that we wait for connect to + complete on. Once they complete, they are removed from this mask + and added to the input_wait_mask and non_keyboard_wait_mask. */ + + static SELECT_TYPE connect_wait_mask; + + /* Number of bits set in connect_wait_mask. */ + static int num_pending_connects; + /* The largest descriptor currently in use for a process object. */ static int max_process_desc; *************** *** 335,340 **** --- 365,377 ---- return concat2 (build_string ("exited abnormally with code "), concat2 (string, string2)); } + else if (EQ (symbol, Qfailed)) + { + string = Fnumber_to_string (make_number (code)); + string2 = build_string ("\n"); + return concat2 (build_string ("failed with code "), + concat2 (string, string2)); + } else return Fcopy_sequence (Fsymbol_name (symbol)); } *************** *** 1741,1762 **** deactivate and close it via delete-process */ DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream, ! 4, 4, 0, doc: /* Open a TCP connection for a service to a host. Returns a subprocess-object to represent the connection. Input and output work as for subprocesses; `delete-process' closes it. ! Args are NAME BUFFER HOST SERVICE. NAME is name for process. It is modified if necessary to make it unique. BUFFER is the buffer (or buffer-name) to associate with the process. Process output goes at end of that buffer, unless you specify an output stream or filter function to handle the output. BUFFER may be also nil, meaning that this process is not associated ! with any buffer ! Third arg is name of the host to connect to, or its IP address. ! Fourth arg SERVICE is name of the service desired, or an integer ! specifying a port number to connect to. */) ! (name, buffer, host, service) ! Lisp_Object name, buffer, host, service; { Lisp_Object proc; #ifdef HAVE_GETADDRINFO --- 1778,1809 ---- deactivate and close it via delete-process */ DEFUN ("open-network-stream", Fopen_network_stream, Sopen_network_stream, ! 4, 7, 0, doc: /* Open a TCP connection for a service to a host. Returns a subprocess-object to represent the connection. Input and output work as for subprocesses; `delete-process' closes it. ! Args are NAME BUFFER HOST SERVICE FILTER SENTINEL NON-BLOCKING. NAME is name for process. It is modified if necessary to make it unique. BUFFER is the buffer (or buffer-name) to associate with the process. Process output goes at end of that buffer, unless you specify an output stream or filter function to handle the output. BUFFER may be also nil, meaning that this process is not associated ! with any buffer. ! HOST is name of the host to connect to, or its IP address. ! SERVICE is name of the service desired, or an integer specifying a ! port number to connect to. ! FILTER and SENTINEL are optional args specifying the filter and ! sentinel functions associated with the network stream. ! NON-BLOCKING is optional arg requesting an non-blocking connect. ! When non-nil, open-network-stream will return immediately without ! waiting for the connection to be made. Instead, the sentinel function ! will be called with second arg equal to "run" (if successful) or ! "failed" when the connect completes. ! On systems without non-blocking connect, this function waits ! for the connect to complete and then proceeds emulating a ! non-blocking connect. */) ! (name, buffer, host, service, filter, sentinel, non_blocking) ! Lisp_Object name, buffer, host, service, filter, sentinel, non_blocking; { Lisp_Object proc; #ifdef HAVE_GETADDRINFO *************** *** 1773,1789 **** int port; #endif /* HAVE_GETADDRINFO */ int s = -1, outch, inch; ! struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; int retry = 0; int count = specpdl_ptr - specpdl; int count1; #ifdef WINDOWSNT /* Ensure socket support is loaded if available. */ init_winsock (TRUE); #endif ! GCPRO4 (name, buffer, host, service); CHECK_STRING (name); CHECK_STRING (host); --- 1820,1846 ---- int port; #endif /* HAVE_GETADDRINFO */ int s = -1, outch, inch; ! struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5; int retry = 0; int count = specpdl_ptr - specpdl; int count1; + int is_non_blocking; #ifdef WINDOWSNT /* Ensure socket support is loaded if available. */ init_winsock (TRUE); #endif ! non_blocking = (NILP (non_blocking) ? Qnil : Qt); ! #ifdef NON_BLOCKING_CONNECT ! is_non_blocking = !NILP (non_blocking); ! #else ! is_non_blocking = 0; ! #endif ! ! /* Can only GCPRO 5 variables */ ! sentinel = Fcons (sentinel, filter); ! GCPRO5 (name, buffer, host, service, sentinel); CHECK_STRING (name); CHECK_STRING (host); *************** *** 1867,1872 **** --- 1924,1942 ---- count1 = specpdl_ptr - specpdl; record_unwind_protect (close_file_unwind, make_number (s)); + #ifdef NON_BLOCKING_CONNECT + if (is_non_blocking) + { + #ifdef O_NONBLOCK + ret = fcntl (s, F_SETFL, O_NONBLOCK); + #else + ret = fcntl (s, F_SETFL, O_NDELAY); + #endif + if (ret < 0) + is_non_blocking = 0; + } + #endif + loop: immediate_quit = 1; *************** *** 1885,1893 **** turn_on_atimers (1); if (ret == 0 || xerrno == EISCONN) ! /* The unwind-protect will be discarded afterwards. ! Likewise for immediate_quit. */ break; immediate_quit = 0; --- 1955,1978 ---- turn_on_atimers (1); if (ret == 0 || xerrno == EISCONN) ! { ! is_non_blocking = 0; ! /* The unwind-protect will be discarded afterwards. ! Likewise for immediate_quit. */ ! break; ! } ! ! #ifdef NON_BLOCKING_CONNECT ! #ifdef EINPROGRESS ! if (is_non_blocking && xerrno == EINPROGRESS) ! break; ! #else ! #ifdef EWOULDBLOCK ! if (is_non_blocking && xerrno == EWOULDBLOCK) break; + #endif + #endif + #endif immediate_quit = 0; *************** *** 1989,2001 **** if (interrupt_input) unrequest_sigio (); loop: immediate_quit = 1; QUIT; ! if (connect (s, (struct sockaddr *) &address, sizeof address) == -1 ! && errno != EISCONN) { int xerrno = errno; --- 2074,2115 ---- if (interrupt_input) unrequest_sigio (); + #ifdef NON_BLOCKING_CONNECT + if (is_non_blocking) + { + #ifdef O_NONBLOCK + ret = fcntl (s, F_SETFL, O_NONBLOCK); + #else + ret = fcntl (s, F_SETFL, O_NDELAY); + #endif + if (ret < 0) + is_non_blocking = 0; + } + #endif /* NON_BLOCKING_CONNECT */ + loop: immediate_quit = 1; QUIT; ! ret = connect (s, (struct sockaddr *) &address, sizeof address); ! ! if (ret == 0 || errno == EISCONN) ! { ! is_non_blocking = 0; ! } ! #ifdef NON_BLOCKING_CONNECT ! #ifdef EINPROGRESS ! else if (is_non_blocking && ret == -1 && errno == EINPROGRESS) ! ; ! #else ! #ifdef EWOULDBLOCK ! else if (is_non_blocking && ret == -1 && errno == EWOULDBLOCK) ! ; ! #endif ! #endif ! #endif ! else { int xerrno = errno; *************** *** 2041,2046 **** --- 2155,2161 ---- request_sigio (); #else /* TERM */ + is_non_blocking = 0; s = connect_server (0); if (s < 0) report_file_error ("error creating socket", Fcons (name, Qnil)); *************** *** 2068,2082 **** XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil)); XPROCESS (proc)->command_channel_p = Qnil; XPROCESS (proc)->buffer = buffer; ! XPROCESS (proc)->sentinel = Qnil; ! XPROCESS (proc)->filter = Qnil; XPROCESS (proc)->command = Qnil; XPROCESS (proc)->pid = Qnil; XSETINT (XPROCESS (proc)->infd, inch); XSETINT (XPROCESS (proc)->outfd, outch); XPROCESS (proc)->status = Qrun; ! FD_SET (inch, &input_wait_mask); ! FD_SET (inch, &non_keyboard_wait_mask); if (inch > max_process_desc) max_process_desc = inch; --- 2183,2209 ---- XPROCESS (proc)->childp = Fcons (host, Fcons (service, Qnil)); XPROCESS (proc)->command_channel_p = Qnil; XPROCESS (proc)->buffer = buffer; ! XPROCESS (proc)->sentinel = XCAR (sentinel); ! XPROCESS (proc)->filter = XCDR (sentinel); XPROCESS (proc)->command = Qnil; XPROCESS (proc)->pid = Qnil; XSETINT (XPROCESS (proc)->infd, inch); XSETINT (XPROCESS (proc)->outfd, outch); XPROCESS (proc)->status = Qrun; ! if (!NILP (non_blocking)) ! { ! XPROCESS (proc)->status = Qconnect; ! if (!FD_ISSET (inch, &connect_wait_mask)) ! { ! FD_SET (inch, &connect_wait_mask); ! num_pending_connects++; ! } ! } ! else if (!EQ (XPROCESS (proc)->filter, Qt)) ! { ! FD_SET (inch, &input_wait_mask); ! FD_SET (inch, &non_keyboard_wait_mask); ! } if (inch > max_process_desc) max_process_desc = inch; *************** *** 2194,2199 **** --- 2321,2332 ---- chan_process[inchannel] = Qnil; FD_CLR (inchannel, &input_wait_mask); FD_CLR (inchannel, &non_keyboard_wait_mask); + if (FD_ISSET (inchannel, &connect_wait_mask)) + { + FD_CLR (inchannel, &connect_wait_mask); + if (--num_pending_connects < 0) + abort (); + } if (inchannel == max_process_desc) { int i; *************** *** 2358,2367 **** { register int channel, nfds; static SELECT_TYPE Available; int xerrno; Lisp_Object proc; EMACS_TIME timeout, end_time; - SELECT_TYPE Atemp; int wait_channel = -1; struct Lisp_Process *wait_proc = 0; int got_some_input = 0; --- 2491,2501 ---- { register int channel, nfds; static SELECT_TYPE Available; + static SELECT_TYPE Connecting; + int check_connect, no_avail; int xerrno; Lisp_Object proc; EMACS_TIME timeout, end_time; int wait_channel = -1; struct Lisp_Process *wait_proc = 0; int got_some_input = 0; *************** *** 2370,2375 **** --- 2504,2510 ---- Lisp_Object wait_for_cell = Qnil; FD_ZERO (&Available); + FD_ZERO (&Connecting); /* If read_kbd is a process to watch, set wait_proc and wait_channel accordingly. */ *************** *** 2511,2521 **** timeout to get our attention. */ if (update_tick != process_tick && do_display) { Atemp = input_wait_mask; EMACS_SET_SECS_USECS (timeout, 0, 0); if ((select (max (max_process_desc, max_keyboard_desc) + 1, ! &Atemp, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ! &timeout) <= 0)) { /* It's okay for us to do this and then continue with --- 2646,2660 ---- timeout to get our attention. */ if (update_tick != process_tick && do_display) { + SELECT_TYPE Atemp, Ctemp; + Atemp = input_wait_mask; + Ctemp = connect_wait_mask; EMACS_SET_SECS_USECS (timeout, 0, 0); if ((select (max (max_process_desc, max_keyboard_desc) + 1, ! &Atemp, ! (num_pending_connects > 0 ? &Ctemp : (SELECT_TYPE *)0), ! (SELECT_TYPE *)0, &timeout) <= 0)) { /* It's okay for us to do this and then continue with *************** *** 2525,2535 **** } } ! /* Don't wait for output from a non-running process. */ if (wait_proc != 0 && !NILP (wait_proc->raw_status_low)) update_status (wait_proc); if (wait_proc != 0 ! && ! EQ (wait_proc->status, Qrun)) { int nread, total_nread = 0; --- 2664,2676 ---- } } ! /* Don't wait for output from a non-running process. Just ! read whatever data has already been received. */ if (wait_proc != 0 && !NILP (wait_proc->raw_status_low)) update_status (wait_proc); if (wait_proc != 0 ! && ! EQ (wait_proc->status, Qrun) ! && ! EQ (wait_proc->status, Qconnect)) { int nread, total_nread = 0; *************** *** 2568,2578 **** /* Wait till there is something to do */ if (!NILP (wait_for_cell)) ! Available = non_process_wait_mask; ! else if (! XINT (read_kbd)) ! Available = non_keyboard_wait_mask; else ! Available = input_wait_mask; /* If frame size has changed or the window is newly mapped, redisplay now, before we start to wait. There is a race --- 2709,2726 ---- /* Wait till there is something to do */ if (!NILP (wait_for_cell)) ! { ! Available = non_process_wait_mask; ! check_connect = 0; ! } else ! { ! if (! XINT (read_kbd)) ! Available = non_keyboard_wait_mask; ! else ! Available = input_wait_mask; ! check_connect = (num_pending_connects > 0); ! } /* If frame size has changed or the window is newly mapped, redisplay now, before we start to wait. There is a race *************** *** 2587,2601 **** set_waiting_for_input (&timeout); } if (XINT (read_kbd) && detect_input_pending ()) { nfds = 0; ! FD_ZERO (&Available); } else ! nfds = select (max (max_process_desc, max_keyboard_desc) + 1, ! &Available, (SELECT_TYPE *)0, (SELECT_TYPE *)0, ! &timeout); xerrno = errno; --- 2735,2755 ---- set_waiting_for_input (&timeout); } + no_avail = 0; if (XINT (read_kbd) && detect_input_pending ()) { nfds = 0; ! no_avail = 1; } else ! { ! if (check_connect) ! Connecting = connect_wait_mask; ! nfds = select (max (max_process_desc, max_keyboard_desc) + 1, ! &Available, ! (check_connect ? &Connecting : (SELECT_TYPE *)0), ! (SELECT_TYPE *)0, &timeout); ! } xerrno = errno; *************** *** 2611,2617 **** if (nfds < 0) { if (xerrno == EINTR) ! FD_ZERO (&Available); #ifdef ultrix /* Ultrix select seems to return ENOMEM when it is interrupted. Treat it just like EINTR. Bleah. Note --- 2765,2771 ---- if (nfds < 0) { if (xerrno == EINTR) ! no_avail = 1; #ifdef ultrix /* Ultrix select seems to return ENOMEM when it is interrupted. Treat it just like EINTR. Bleah. Note *************** *** 2619,2631 **** "__ultrix__"; the latter is only defined under GCC, but not by DEC's bundled CC. -JimB */ else if (xerrno == ENOMEM) ! FD_ZERO (&Available); #endif #ifdef ALLIANT /* This happens for no known reason on ALLIANT. I am guessing that this is the right response. -- RMS. */ else if (xerrno == EFAULT) ! FD_ZERO (&Available); #endif else if (xerrno == EBADF) { --- 2773,2785 ---- "__ultrix__"; the latter is only defined under GCC, but not by DEC's bundled CC. -JimB */ else if (xerrno == ENOMEM) ! no_avail = 1; #endif #ifdef ALLIANT /* This happens for no known reason on ALLIANT. I am guessing that this is the right response. -- RMS. */ else if (xerrno == EFAULT) ! no_avail = 1; #endif else if (xerrno == EBADF) { *************** *** 2637,2643 **** So, SIGHUP is ignored (see def of PTY_TTY_NAME_SPRINTF in m/ibmrt-aix.h), and here we just ignore the select error. Cleanup occurs c/o status_notify after SIGCLD. */ ! FD_ZERO (&Available); /* Cannot depend on values returned */ #else abort (); #endif --- 2791,2797 ---- So, SIGHUP is ignored (see def of PTY_TTY_NAME_SPRINTF in m/ibmrt-aix.h), and here we just ignore the select error. Cleanup occurs c/o status_notify after SIGCLD. */ ! no_avail = 1; /* Cannot depend on values returned */ #else abort (); #endif *************** *** 2645,2653 **** else error ("select error: %s", emacs_strerror (xerrno)); } #if defined(sun) && !defined(USG5_4) ! else if (nfds > 0 && keyboard_bit_set (&Available) ! && interrupt_input) /* System sometimes fails to deliver SIGIO. David J. Mackenzie says that Emacs doesn't compile under --- 2799,2814 ---- else error ("select error: %s", emacs_strerror (xerrno)); } + + if (no_avail) + { + FD_ZERO (&Available); + check_connect = 0; + } + #if defined(sun) && !defined(USG5_4) ! if (nfds > 0 && keyboard_bit_set (&Available) ! && interrupt_input) /* System sometimes fails to deliver SIGIO. David J. Mackenzie says that Emacs doesn't compile under *************** *** 2746,2751 **** --- 2907,2915 ---- do_pending_window_change (0); /* Check for data from a process. */ + if (no_avail || nfds == 0) + continue; + /* Really FIRST_PROC_DESC should be 0 on Unix, but this is safer in the short run. */ for (channel = 0; channel <= max_process_desc; channel++) *************** *** 2837,2842 **** --- 3001,3047 ---- = Fcons (Qexit, Fcons (make_number (256), Qnil)); } } + #ifdef NON_BLOCKING_CONNECT + if (check_connect && FD_ISSET (channel, &Connecting)) + { + struct Lisp_Process *p; + struct sockaddr pname; + socklen_t pnamelen = sizeof(pname); + + FD_CLR (channel, &connect_wait_mask); + if (--num_pending_connects < 0) + abort (); + + proc = chan_process[channel]; + if (NILP (proc)) + continue; + + p = XPROCESS (proc); + XSETINT (p->tick, ++process_tick); + + /* If connection failed, getpeername fails */ + if (getpeername(channel, &pname, &pnamelen) < 0) + { + char dummy; + + xerrno = errno; + /* Obtain connect failure code through error slippage. */ + if (errno == ENOTCONN && read(channel, &dummy, 1) < 0) + xerrno = errno; + p->status = Fcons (Qfailed, Fcons (make_number (xerrno), Qnil)); + deactivate_process (proc); + } + else + { + p->status = Qrun; + if (!EQ (p->filter, Qt)) + { + FD_SET (XINT (p->infd), &input_wait_mask); + FD_SET (XINT (p->infd), &non_keyboard_wait_mask); + } + } + } + #endif /* NON_BLOCKING_CONNECT */ } /* end for each file descriptor */ } /* end while exit conditions not met */ *************** *** 4419,4424 **** --- 4624,4630 ---- /* If process is still active, read any output that remains. */ while (! EQ (p->filter, Qt) + && ! EQ (p->status, Qconnect) && XINT (p->infd) >= 0 && read_process_output (proc, XINT (p->infd)) > 0); *************** *** 4653,4658 **** --- 4859,4868 ---- staticpro (&Qopen); Qclosed = intern ("closed"); staticpro (&Qclosed); + Qconnect = intern ("connect"); + staticpro (&Qconnect); + Qfailed = intern ("failed"); + staticpro (&Qfailed); Qlast_nonmenu_event = intern ("last-nonmenu-event"); staticpro (&Qlast_nonmenu_event); -- Kim F. Storm http://www.cua.dk _______________________________________________ Emacs-devel mailing list Emacs-devel@gnu.org http://mail.gnu.org/mailman/listinfo/emacs-devel