unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Re: Non-blocking open-network-stream
       [not found] <m2u1sa7819.fsf@xaital.online-marketwatch.com>
@ 2002-02-21 23:45 ` Kim F. Storm
  2002-02-22 16:04   ` Stefan Monnier
  2002-02-25 22:38   ` Kim F. Storm
  0 siblings, 2 replies; 17+ messages in thread
From: Kim F. Storm @ 2002-02-21 23:45 UTC (permalink / raw)
  Cc: emacs-devel

Helmut Eller <helmut@xaital.km4u.net> writes:

> Hi,
> 
> I read your post about non-blocking open-network-stream in the web
> archive of the emacs-devel list and would like to make some comments.
> I write to you directly because I'm not subscribed and I have the
> impression the list is "For Developers Only".
> 
Thanks a lot for your feedback.  I've copied my response to the list.

> I tried the code a bit and I noticed that the sentinel is sometimes
> not invoked with "run" when the connections completes successfully but
> is closed shortly afterwards by the peer.  The sentinel was sometimes
> invoked sometimes not.  The scenario was very simple: a server
> accepted the connection and wrote "hello" to the socket and closed the
> connection.  Emacs executed this:
> 
>     (open-network-stream "nb" "x.x" "localhost" 34567
> 			 nil 
> 			 (lambda (s msg)
> 			   (with-current-buffer (process-buffer s)
> 			     (insert msg "\n")
> 			     (when (equal "failed" msg)
> 			       (message "deleting %S" s)
> 			       (delete-process s))))
> 			 t)
> 

This seems to be related to the problem you describe below: 
reading the process output before checking for the completion of the
connect.  I'll look into it.

> You wrote:
> 
> [...]
> > The initial process-state is  `connecting'  which changes to `open'
> > or `failed' depending on whether the connect succeeded or failed.
> > Notice that the sentinel string for `open' is "run".
> 
> You could modify status_message to return something more meaningful.
> Preferably the symbol 'open.
> 

Yes, I considered doing that.  However, I didn't know whether existing
code may depend on the current behaviour, and although I couldn't find any
in CVS, I decided against changing it.

> [...]  
> > + /* Number of bits set in connect_wait_mask.  */
> > + int num_pending_connects;
> > + 
> 
> Any reason to make this non-static?
No.  I just forgot it.

> 
> [...]  
> > !   non_blocking = (NILP (non_blocking) ? Qnil : Qt);
> > ! #ifdef NON_BLOCKING_CONNECT
> > !   is_non_blocking = !NILP (non_blocking);
> > ! #else
> > !   is_non_blocking = 0;
> > ! #endif
> 
> Wouldn't non_blocking_p be a more lispy name? :-)
Yes - but is_non_blocking is not a lisp object :-)

> 
> [...]
> > --- 1948,1971 ----
> > 	  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;
> > !       }
> 
> Why do you set is_non_blocking to 0?  I can see no later use.  For
> documentation?

It is defensive programming -- in case it ever becomes necessary to test on
it later in the code, it contains the proper value.

> 
> > ! 
> > ! #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
> 
> What does it mean when connect returns EWOULDBLOCK?  My man page
> doesn't mention it.
> 

Neither does mine -- but I saw some references on the Web which
mentions this as one of the possible error codes from a non-blocking
connect.  So I included the test in case EINPROGRESS is not defined.

> [...] 
> > --- 2176,2202 ----
> [...]
> > !   if (!NILP (non_blocking))
> > !     {
> > !       XPROCESS (proc)->status = Qconnecting;
> > !       if (!FD_ISSET (inch, &connect_wait_mask))
> > !       {
> > !         FD_SET (inch, &connect_wait_mask);
> > !         num_pending_connects++;
> > !       }
> > !     }
> 
> Why is  if(!FD_ISET...) necessary?  

Because num_pending_connects would be updated incorrectly if
it is already set.  Defensive programming...

> 
> [...]
> > + #ifdef NON_BLOCKING_CONNECT     
> > +         if (check_connect && FD_ISSET (channel, &Connecting))
> > +           {
> 
> Isn't it possible that channel becomes readable and writable at the
> same time?  If yes, wouldn't that mean that read_process_output (and
> hence the filter) was already called before we get here?

It seems you are right.  Maybe the easiest fix would be for
read_process_output to just return 0 if the process state is
Qconnecting.  I'll look into that.

> 
> > +             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;
> 
> Is it safe to decrement num_pending_connects even if proc is nil?  
> 
> [...]

Yes, because we only get here if channel is in the Connecting mask
which is a subset of connect_wait_mask.  Whether there really is a
corresponding process doesn't matter.  (there should be one, but I'm
playing safe here -- cleaning up the connect_wait_mask in any case).

> > + 
> > +             p = XPROCESS (proc);
> > +             XSETINT (p->tick, ++process_tick);
> > + 
> > +             /* If connection failed, getpeername fails */
> > +             if (getpeername(channel, &pname, &pnamelen) < 0)
> > +               {
> > +                 /* Preserve status of processes already terminated.  */
> > +                 p->status = Qfailed;
> > +                 deactivate_process (proc);
> > +               }
> > +             else
> 
> It would be nice if the error message (obtained with getsockopt
> (channel, SOL_SCOKET, SO_ERROR ...)) would be passed to the sentinel.
> 
That would be reasonable.  I'll look into that.

> A minor note: open-network-stream contains a large chunk of duplicated
> code (starting from "/* Kernel bugs (on Ultrix..." to
> "report_file_error ("connection failed...))).  This are about 70 lines;
> should they be in a separate function?

Maybe, or the #ifdefs could be rearranged to avoid the duplication.
I'll take a look.

-- 
Kim F. Storm <storm@cua.dk> http://www.cua.dk


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-02-21 23:45 ` Kim F. Storm
@ 2002-02-22 16:04   ` Stefan Monnier
  2002-02-25 22:38   ` Kim F. Storm
  1 sibling, 0 replies; 17+ messages in thread
From: Stefan Monnier @ 2002-02-22 16:04 UTC (permalink / raw)
  Cc: Helmut Eller, emacs-devel

> > > !   non_blocking = (NILP (non_blocking) ? Qnil : Qt);
> > > ! #ifdef NON_BLOCKING_CONNECT
> > > !   is_non_blocking = !NILP (non_blocking);
> > > ! #else
> > > !   is_non_blocking = 0;
> > > ! #endif
> > 
> > Wouldn't non_blocking_p be a more lispy name? :-)
> Yes - but is_non_blocking is not a lisp object :-)

Actually the answer was `no'.  The `p' postfix stands for `predicate'.
A predicate is not a mere boolean value but a function that returns a boolean.


	Stefan


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-02-21 23:45 ` Kim F. Storm
  2002-02-22 16:04   ` Stefan Monnier
@ 2002-02-25 22:38   ` Kim F. Storm
  2002-02-26 22:46     ` Helmut Eller
  1 sibling, 1 reply; 17+ messages in thread
From: Kim F. Storm @ 2002-02-25 22:38 UTC (permalink / raw)
  Cc: Helmut Eller


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 <storm@cua.dk> http://www.cua.dk


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-02-25 22:38   ` Kim F. Storm
@ 2002-02-26 22:46     ` Helmut Eller
  2002-02-27 11:59       ` Kim F. Storm
  0 siblings, 1 reply; 17+ messages in thread
From: Helmut Eller @ 2002-02-26 22:46 UTC (permalink / raw)
  Cc: emacs-devel

storm@cua.dk (Kim F. Storm) writes:

> Here is my second attempt at a patch to support non-blocking
> open-network-stream.

I think the problem I described last time is still present.  The
problem is that the filter is invoked before the sentinel.  This
happens when the stream is readable immediately after the transition
from connect state to open state.

Consider wait_reading_process_input:

> --- 3001,3047 ----
[...]
> + 	      XSETINT (p->tick, ++process_tick);
> + 	      if (getpeername(channel, &pname, &pnamelen) < 0)
[...]
> + 	      else
> + 		{
> + 		  p->status = Qrun;

Tick is incremented and status is set to Qrun.  Incrementing tick
causes status_notify to be invoked during the next iteration.

But status_notify ...

> --- 4624,4630 ----
[...]
>   	  while (! EQ (p->filter, Qt)
> + 		 && ! EQ (p->status, Qconnect)
>   		 && XINT (p->infd) >= 0
>   		 && read_process_output (proc, XINT (p->infd)) > 0);

... calls read_process_output (and the filter) before the sentinel.

A simple solution is to call the sentinel in
wait_reading_process_input without incrementing tick, e.g.:

--- 3001,3047 ----
...       
+ 	      if (getpeername(channel, &pname, &pnamelen) < 0)
+ 	          XSETINT (p->tick, ++process_tick);
...           
+ 	      else
+ 		{
+ 		  p->status = Qrun;
+                 exec_sentinel (proc, Qopen);
            
Another point: is it a problem to pass the error message and not just
the error number to the sentinel?  

Helmut.








_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-02-26 22:46     ` Helmut Eller
@ 2002-02-27 11:59       ` Kim F. Storm
  2002-02-28  4:08         ` Richard Stallman
  0 siblings, 1 reply; 17+ messages in thread
From: Kim F. Storm @ 2002-02-27 11:59 UTC (permalink / raw)
  Cc: emacs-devel

Helmut Eller <helmut@xaital.km4u.net> writes:

> storm@cua.dk (Kim F. Storm) writes:
> 
> > Here is my second attempt at a patch to support non-blocking
> > open-network-stream.
> 
> I think the problem I described last time is still present.  The
> problem is that the filter is invoked before the sentinel.  This
> happens when the stream is readable immediately after the transition
> from connect state to open state.

I've made the chage you suggested.  Thanks.

There is a general problem with handling a request for non-blocking connect
on systems which does not support this.

As I have specified this now, open-network-stream will proceed to
emulate a non-blocking connect -- which means that it will block.  And
when the connect completes, it continues to pretend that the connect
was non-blocking, so it setup the sentinel to be called after return,
and all that jazz.

I think it would be much cleaner if (open-network-stream ... t) simply
returns nil if it doesn't support non-blocking connect.

Then the code can do some smarter things (like delaying the connect),
and I don't have to mess up the C code with all sorts of hacks to delay
the delivery of a "failed to connect" message on a process which would
not otherwise have to exist.

Does this sound acceptable (since there is currently no code supporting
non-blocking connect, this cannot break anything :-)

-- 
Kim F. Storm <storm@cua.dk> http://www.cua.dk


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
@ 2002-02-27 17:49 Helmut Eller
  0 siblings, 0 replies; 17+ messages in thread
From: Helmut Eller @ 2002-02-27 17:49 UTC (permalink / raw)
  Cc: emacs-devel

storm@cua.dk (Kim F. Storm) writes:

> Does this sound acceptable (since there is currently no code supporting
> non-blocking connect, this cannot break anything :-)

This sounds much cleaner, at least to me. 

Helmut

_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-02-27 11:59       ` Kim F. Storm
@ 2002-02-28  4:08         ` Richard Stallman
  2002-03-01  0:21           ` Kim F. Storm
  0 siblings, 1 reply; 17+ messages in thread
From: Richard Stallman @ 2002-02-28  4:08 UTC (permalink / raw)
  Cc: helmut, emacs-devel

    I think it would be much cleaner if (open-network-stream ... t) simply
    returns nil if it doesn't support non-blocking connect.

I think that is ok.  The program could immediately try a blocking
connect if that is the right thing to do.


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-02-28  4:08         ` Richard Stallman
@ 2002-03-01  0:21           ` Kim F. Storm
  2002-03-01  8:01             ` Juanma Barranquero
                               ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Kim F. Storm @ 2002-03-01  0:21 UTC (permalink / raw)
  Cc: helmut, emacs-devel

Richard Stallman <rms@gnu.org> writes:

>     I think it would be much cleaner if (open-network-stream ... t) simply
>     returns nil if it doesn't support non-blocking connect.
> 
> I think that is ok.  The program could immediately try a blocking
> connect if that is the right thing to do.

I have committed the changes to process.c which add the non-blocking
connect support to open-network-stream.


The next "project" in this area is to add Helmut's server sockets.
(do we have papers for that?)

However, I think it can be done via open-network-stream:

If the HOST argument is nil, a server socket is opened which
accepts connections.  The sentinel is called - with a newly
created process - whenever a connections is accepted.

There are some details to be worked out here, but we need to
discuss whether we should use this approach or the approach
suggested by Helmut.


-- 
Kim F. Storm <storm@cua.dk> http://www.cua.dk


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-01  0:21           ` Kim F. Storm
@ 2002-03-01  8:01             ` Juanma Barranquero
  2002-03-01 10:50               ` Kim F. Storm
  2002-03-01 21:23             ` Richard Stallman
  2002-03-02  7:59             ` Helmut Eller
  2 siblings, 1 reply; 17+ messages in thread
From: Juanma Barranquero @ 2002-03-01  8:01 UTC (permalink / raw)
  Cc: rms, helmut, emacs-devel


On 01 Mar 2002 01:21:39 +0100, storm@cua.dk (Kim F. Storm) wrote:

> The next "project" in this area is to add Helmut's server sockets.
> (do we have papers for that?)
> 
> However, I think it can be done via open-network-stream:
> 
> If the HOST argument is nil, a server socket is opened which
> accepts connections.  The sentinel is called - with a newly
> created process - whenever a connections is accepted.

There will be any support for Emacs to act as a server for UDP?

                                                           /L/e/k/t/u


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-01  8:01             ` Juanma Barranquero
@ 2002-03-01 10:50               ` Kim F. Storm
  2002-03-01 17:10                 ` Pavel Janík
  0 siblings, 1 reply; 17+ messages in thread
From: Kim F. Storm @ 2002-03-01 10:50 UTC (permalink / raw)
  Cc: helmut, emacs-devel

Juanma Barranquero <lektu@terra.es> writes:

> On 01 Mar 2002 01:21:39 +0100, storm@cua.dk (Kim F. Storm) wrote:
> 
> > The next "project" in this area is to add Helmut's server sockets.
> > (do we have papers for that?)
> > 
> > However, I think it can be done via open-network-stream:
> > 
> > If the HOST argument is nil, a server socket is opened which
> > accepts connections.  The sentinel is called - with a newly
> > created process - whenever a connections is accepted.
> 
> There will be any support for Emacs to act as a server for UDP?

I don't even think it can act as a client for UDP currently, so
there is really two tasks here.  I'd like to look into that later
if others think that's something we should support.

-- 
Kim F. Storm  http://www.cua.dk


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-01 10:50               ` Kim F. Storm
@ 2002-03-01 17:10                 ` Pavel Janík
  0 siblings, 0 replies; 17+ messages in thread
From: Pavel Janík @ 2002-03-01 17:10 UTC (permalink / raw)
  Cc: Juanma Barranquero, helmut, emacs-devel

   From: storm@cua.dk (Kim F. Storm)
   Date: 01 Mar 2002 11:50:46 +0100

Hi Kim,

   > I don't even think it can act as a client for UDP currently, so
   > there is really two tasks here.  I'd like to look into that later
   > if others think that's something we should support.

I think that UDP can wait for some time. The most important thing now is
IMHO some examples for people who would like to use it and extend
Emacs. Multiplication effect...
-- 
Pavel Janík

So calm down guys. And improving the benchmark might not be a bad idea.
                  -- Linus Torvalds in linux-kernel

_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-01  0:21           ` Kim F. Storm
  2002-03-01  8:01             ` Juanma Barranquero
@ 2002-03-01 21:23             ` Richard Stallman
  2002-03-02  7:59             ` Helmut Eller
  2 siblings, 0 replies; 17+ messages in thread
From: Richard Stallman @ 2002-03-01 21:23 UTC (permalink / raw)
  Cc: helmut, emacs-devel

    If the HOST argument is nil, a server socket is opened which
    accepts connections.  The sentinel is called - with a newly
    created process - whenever a connections is accepted.

That sounds good to me in principle, if the details work ok.

_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-01  0:21           ` Kim F. Storm
  2002-03-01  8:01             ` Juanma Barranquero
  2002-03-01 21:23             ` Richard Stallman
@ 2002-03-02  7:59             ` Helmut Eller
  2002-03-03  0:12               ` Kim F. Storm
  2002-03-03 14:39               ` Richard Stallman
  2 siblings, 2 replies; 17+ messages in thread
From: Helmut Eller @ 2002-03-02  7:59 UTC (permalink / raw)
  Cc: emacs-devel


storm@cua.dk (Kim F. Storm) writes:

> (do we have papers for that?)

No.  If needed, I will sign papers.

> However, I think it can be done via open-network-stream:
> 
> If the HOST argument is nil, a server socket is opened which
> accepts connections.  The sentinel is called - with a newly
> created process - whenever a connections is accepted.

I see several problems with this approach:

 - What is the process-name for the connections?  The same as for the
   server socket?  All connections with the same name?

 - How do you open a Unix server socket?  

 - It's possible to open Unix server sockets.  It would be reasonable
   to support Unix client sockets too.  How can this be done?

 - What should NON-BLOCKING mean for server sockets?

 - open-network-stream takes already 7 arguments.  Putting even more
   functionality in makes it hard to document.

Server sockets are IMHO different enough to merit a separate
function.  What is the advantage of merging those concepts?


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-02  7:59             ` Helmut Eller
@ 2002-03-03  0:12               ` Kim F. Storm
  2002-03-03 10:46                 ` Helmut Eller
  2002-03-03 16:44                 ` Mario Lang
  2002-03-03 14:39               ` Richard Stallman
  1 sibling, 2 replies; 17+ messages in thread
From: Kim F. Storm @ 2002-03-03  0:12 UTC (permalink / raw)
  Cc: emacs-devel

Helmut Eller <helmut@xaital.km4u.net> writes:

> storm@cua.dk (Kim F. Storm) writes:
> 
> > (do we have papers for that?)
> 
> No.  If needed, I will sign papers.
> 
> > However, I think it can be done via open-network-stream:
> > 
> > If the HOST argument is nil, a server socket is opened which
> > accepts connections.  The sentinel is called - with a newly
> > created process - whenever a connections is accepted.
> 
> I see several problems with this approach:
> 
>  - What is the process-name for the connections?  The same as for the
>    server socket?  All connections with the same name?

My idea is to name it as the server process concatenated with the
ip address and source port of the client, i.e. something like

        "service <10.20.30.40:3248>"

I prefer this to manually having to give each connection a unique name
in advance (as would be necessary with your accept-connection API).

> 
>  - How do you open a Unix server socket?  
> 

I intent do rename the NON-BLOCKING argument to a more generic TYPE
argument.  Eventually, it could be things like:

If HOST specified - connect to that host:

nil - blocking connect (tcp) to SERVICE on HOST
t   - non-blocking connect (tcp) to SERVICE on HOST
udp - open udp socket with target SERVICE on HOST
unix - connect to unix socket on address SERVICE

If HOST is nil - open a server socket:

nil - open tcp socket listning on SERVICE port
t   - same as nil
udp - open udp socket bound to SERVICE port
unix - open unix socket bound to address SERVICE

>  - It's possible to open Unix server sockets.  It would be reasonable
>    to support Unix client sockets too.  How can this be done?
> 

See above.  (IIRC, Your patch didn't cover unix sockets)

>  - What should NON-BLOCKING mean for server sockets?

See above.

> 
>  - open-network-stream takes already 7 arguments.  Putting even more
>    functionality in makes it hard to document.

I don't agree.  Renaming NON-BLOCKING to TYPE and switching on
whether HOST is non-nil (connect) or nil (start server) seems
fairly clean and simple to me.

> 
> Server sockets are IMHO different enough to merit a separate
> function.  What is the advantage of merging those concepts?
> 

Most of the necessary functionality is already in open-network-stream,
so adding the server part there is pretty orthogonal I think.

-- 
Kim F. Storm <storm@cua.dk> http://www.cua.dk


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-03  0:12               ` Kim F. Storm
@ 2002-03-03 10:46                 ` Helmut Eller
  2002-03-03 16:44                 ` Mario Lang
  1 sibling, 0 replies; 17+ messages in thread
From: Helmut Eller @ 2002-03-03 10:46 UTC (permalink / raw)
  Cc: emacs-devel


storm@cua.dk (Kim F. Storm) writes:

> I intent do rename the NON-BLOCKING argument to a more generic TYPE
> argument.  Eventually, it could be things like:
> 
> If HOST specified - connect to that host:
> 
> nil - blocking connect (tcp) to SERVICE on HOST
> t   - non-blocking connect (tcp) to SERVICE on HOST
> udp - open udp socket with target SERVICE on HOST
> unix - connect to unix socket on address SERVICE
> 
> If HOST is nil - open a server socket:
> 
> nil - open tcp socket listning on SERVICE port
> t   - same as nil
> udp - open udp socket bound to SERVICE port
> unix - open unix socket bound to address SERVICE

If you overload NON-BLOCKING in this way, you lose the ability to make
a non-blocking connects to Unix sockets.  The distinction between INET
or Unix sockets should be made with an optional PROTOCOL-FAMILY
argument.  

Also, mixing up UDP with Unix sockets is not a good idea.  The main
difference between TCP and UPD is that UDP is packet oriented and TCP
is stream oriented.  You can also have packet oriented Unix sockets.
Packet or stream orientedness should probably be specified with a
separate STYLE argument.

> See above.  (IIRC, Your patch didn't cover unix sockets)

I added Unix sockets when RMS said that Unix sockets are required to
replace emacsserver.

> >  - open-network-stream takes already 7 arguments.  Putting even more
> >    functionality in makes it hard to document.
>
> I don't agree.  Renaming NON-BLOCKING to TYPE and switching on
> whether HOST is non-nil (connect) or nil (start server) seems
> fairly clean and simple to me.

I cannot help, but your TYPE argument looks like a kludge to me.

The HOST, BUFFER, and NON-BLOCKING arguments are not even used for
server sockets.

> > Server sockets are IMHO different enough to merit a separate
> > function.  What is the advantage of merging those concepts?
> > 
> 
> Most of the necessary functionality is already in open-network-stream,
> so adding the server part there is pretty orthogonal I think.

I think there is not much shared functionality.  open-network-stream
does basically this:

    gethostbyname/socket/connect/coding-system-magic

For the server socket case it should do:
    
    socket/bind/listen

Only the call to socket could be shared.


Below is my updated patch that supports Unix sockets.  Perhaps you can
reuse some parts.


Index: process.c
===================================================================
RCS file: /cvsroot/emacs/emacs/src/process.c,v
retrieving revision 1.353
diff -c -r1.353 process.c
*** process.c	28 Feb 2002 23:59:19 -0000	1.353
--- process.c	3 Mar 2002 10:36:17 -0000
***************
*** 51,59 ****
--- 51,69 ----
  
  #ifdef HAVE_SOCKETS	/* TCP connection support, if kernel can do it */
  #include <sys/socket.h>
+ #include <sys/un.h>
  #include <netdb.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
+ 
+ /* Union of all socket address types we support. */
+ union sockaddr_union 
+ {
+   struct sockaddr sa;
+   struct sockaddr_in sin;
+   struct sockaddr_un sun;
+ };
+ 
  #ifdef NEED_NET_ERRNO_H
  #include <net/errno.h>
  #endif /* NEED_NET_ERRNO_H */
***************
*** 114,119 ****
--- 124,130 ----
  Lisp_Object Qprocessp;
  Lisp_Object Qrun, Qstop, Qsignal;
  Lisp_Object Qopen, Qclosed, Qconnect, Qfailed;
+ Lisp_Object Qserver_socket, Qlisten, Qinet, Qunix;
  Lisp_Object Qlast_nonmenu_event;
  /* Qexit is declared and initialized in eval.c.  */
  
***************
*** 122,129 ****
--- 133,142 ----
  
  #ifdef HAVE_SOCKETS
  #define NETCONN_P(p) (GC_CONSP (XPROCESS (p)->childp))
+ #define SERVER_SOCKET_P(p) ((XPROCESS (p)->childp) == Qserver_socket)
  #else
  #define NETCONN_P(p) 0
+ #define SERVER_SOCKET_P(p) 0
  #endif /* HAVE_SOCKETS */
  
  /* Define first descriptor number available for subprocesses.  */
***************
*** 258,264 ****
  static struct coding_system *proc_encode_coding_system[MAXDESC];
  
  static Lisp_Object get_process ();
! static void exec_sentinel ();
  
  extern EMACS_TIME timer_check ();
  extern int timers_run;
--- 271,282 ----
  static struct coding_system *proc_encode_coding_system[MAXDESC];
  
  static Lisp_Object get_process ();
! static void exec_sentinel (Lisp_Object proc, Lisp_Object reason);
! static Lisp_Object read_process_output_error_handler (Lisp_Object error);
! static Lisp_Object read_process_output_call (Lisp_Object fun_and_args);
! #ifdef HAVE_SOCKETS
! static Lisp_Object decode_sockaddr_union (union sockaddr_union *u);
! #endif
  
  extern EMACS_TIME timer_check ();
  extern int timers_run;
***************
*** 614,619 ****
--- 632,642 ----
        XPROCESS (process)->status = Fcons (Qexit, Fcons (make_number (0), Qnil));
        XSETINT (XPROCESS (process)->tick, ++process_tick);
      }
+   else if (SERVER_SOCKET_P (process))
+     {
+       XPROCESS (process)->status = list2 (Qexit, make_number (0));
+       XSETINT (XPROCESS (process)->tick, ++process_tick);
+     }
    else if (XINT (XPROCESS (process)->infd) >= 0)
      {
        Fkill_process (process, Qnil);
***************
*** 638,643 ****
--- 661,667 ----
  closed -- for a network stream connection that is closed.
  connect -- when waiting for a non-blocking connection to complete.
  failed -- when a non-blocking connection has failed.
+ listen -- for a server socket that is listening.
  nil -- if arg is a process name and no such process exists.
  PROCESS may be a process, a buffer, the name of a process, or
  nil, indicating the current buffer's process.  */)
***************
*** 668,673 ****
--- 692,706 ----
        else if (EQ (status, Qexit))
  	status = Qclosed;
      }
+   else if (SERVER_SOCKET_P (process))
+     {
+       if (EQ (status, Qrun))
+ 	status = Qlisten;
+       else if (EQ (status, Qexit))
+ 	status = Qclosed;
+       else
+ 	abort ();
+     }
    return status;
  }
  
***************
*** 919,930 ****
  DEFUN ("process-contact", Fprocess_contact, Sprocess_contact,
         1, 1, 0,
         doc: /* Return the contact info of PROCESS; t for a real child.
! For a net connection, the value is a cons cell of the form (HOST SERVICE).  */)
       (process)
       register Lisp_Object process;
  {
    CHECK_PROCESS (process);
!   return XPROCESS (process)->childp;
  }
  
  #if 0 /* Turned off because we don't currently record this info
--- 952,974 ----
  DEFUN ("process-contact", Fprocess_contact, Sprocess_contact,
         1, 1, 0,
         doc: /* Return the contact info of PROCESS; t for a real child.
! For a net connection, the value is a cons cell of the form (HOST SERVICE). 
! For a server socket a cons cell of the form (server-socket . ADDRESS). */)
       (process)
       register Lisp_Object process;
  {
    CHECK_PROCESS (process);
! #ifdef HAVE_SOCKETS
!   if (SERVER_SOCKET_P (process))
!     {
!       union sockaddr_union u;
!       socklen_t length = sizeof u;
!       getsockname (XPROCESS (process)->infd, &u.sa, &length);
!       return Fcons (Qserver_socket, decode_sockaddr_union (&u));
!     }
!   else 
! #endif
!     return XPROCESS (process)->childp;
  }
  
  #if 0 /* Turned off because we don't currently record this info
***************
*** 998,1003 ****
--- 1042,1056 ----
  	  else
  	    Fprinc (symbol, Qnil);
  	}
+       else if (SERVER_SOCKET_P (proc))
+ 	{
+ 	  if (EQ (symbol, Qrun))
+ 	    write_string ("listen", -1);
+ 	  else if (EQ (symbol, Qexit))
+ 	    write_string ("closed", -1);
+ 	  else
+ 	    Fprinc (symbol, Qnil);
+ 	}
        else
  	Fprinc (symbol, Qnil);
  
***************
*** 1038,1043 ****
--- 1091,1115 ----
  		   XSTRING (XCAR (p->childp))->data);
  	  insert_string (tembuf);
          }
+       else if (SERVER_SOCKET_P (proc))
+ 	{
+ 	  union sockaddr_union u;
+ 	  socklen_t length = sizeof u;
+ 	  getsockname (p->infd, &u.sa, &length);
+ 	  switch (u.sa.sa_family) 
+ 	    {
+ 	    case AF_INET:
+ 	      sprintf (tembuf, "(inet socket on port %d)\n", 
+ 		       ntohs (u.sin.sin_port));
+ 	      break;
+ 	    case AF_LOCAL:
+ 	      sprintf (tembuf, "(unix socket %s)\n", u.sun.sun_path);
+ 	      break;
+ 	    default:
+ 	      abort ();
+ 	    }
+ 	  insert_string (tembuf);
+         }
        else 
  	{
  	  tem = p->command;
***************
*** 1778,1783 ****
--- 1850,1986 ----
  
  #ifdef HAVE_SOCKETS
  
+ /* Setup coding systems for communicating with the network stream.  */
+ static void 
+ select_coding_system (proc) 
+      Lisp_Object proc;
+ {
+   Lisp_Object buffer = XPROCESS (proc)->buffer;
+   Lisp_Object name = XPROCESS (proc)->name;
+   Lisp_Object host = XCAR (XPROCESS (proc)->childp);
+   Lisp_Object service = XCAR (XCDR (XPROCESS (proc)->childp));
+   int inch = XINT (XPROCESS (proc)->infd);
+   int outch = XINT (XPROCESS (proc)->outfd);
+   { 
+     struct gcpro gcpro1;
+     /* Qt denotes we have not yet called Ffind_operation_coding_system.  */
+     Lisp_Object coding_systems = Qt;
+     Lisp_Object args[5], val;
+ 
+     if (!NILP (Vcoding_system_for_read))
+       val = Vcoding_system_for_read;
+     else if ((!NILP (buffer) && NILP (XBUFFER (buffer)->enable_multibyte_characters))
+ 	     || (NILP (buffer) && NILP (buffer_defaults.enable_multibyte_characters)))
+       /* We dare not decode end-of-line format by setting VAL to
+ 	 Qraw_text, because the existing Emacs Lisp libraries
+ 	 assume that they receive bare code including a sequene of
+ 	 CR LF.  */
+       val = Qnil;
+     else
+       {
+ 	args[0] = Qopen_network_stream, args[1] = name,
+ 	  args[2] = buffer, args[3] = host, args[4] = service;
+ 	GCPRO1 (proc);
+ 	coding_systems = Ffind_operation_coding_system (5, args);
+ 	UNGCPRO;
+ 	if (CONSP (coding_systems))
+ 	  val = XCAR (coding_systems);
+ 	else if (CONSP (Vdefault_process_coding_system))
+ 	  val = XCAR (Vdefault_process_coding_system);
+ 	else
+ 	  val = Qnil;
+       }
+     XPROCESS (proc)->decode_coding_system = val;
+ 
+     if (!NILP (Vcoding_system_for_write))
+       val = Vcoding_system_for_write;
+     else if (NILP (current_buffer->enable_multibyte_characters))
+       val = Qnil;
+     else
+       {
+ 	if (EQ (coding_systems, Qt))
+ 	  {
+ 	    args[0] = Qopen_network_stream, args[1] = name,
+ 	      args[2] = buffer, args[3] = host, args[4] = service;
+ 	    GCPRO1 (proc);
+ 	    coding_systems = Ffind_operation_coding_system (5, args);
+ 	    UNGCPRO;
+ 	  }
+ 	if (CONSP (coding_systems))
+ 	  val = XCDR (coding_systems);
+ 	else if (CONSP (Vdefault_process_coding_system))
+ 	  val = XCDR (Vdefault_process_coding_system);
+ 	else
+ 	  val = Qnil;
+       }
+     XPROCESS (proc)->encode_coding_system = val;
+   }
+ 
+   if (!proc_decode_coding_system[inch])
+     proc_decode_coding_system[inch]
+       = (struct coding_system *) xmalloc (sizeof (struct coding_system));
+   setup_coding_system (XPROCESS (proc)->decode_coding_system,
+ 		       proc_decode_coding_system[inch]);
+   if (!proc_encode_coding_system[outch])
+     proc_encode_coding_system[outch]
+       = (struct coding_system *) xmalloc (sizeof (struct coding_system));
+   setup_coding_system (XPROCESS (proc)->encode_coding_system,
+ 		       proc_encode_coding_system[outch]);
+ 
+   XPROCESS (proc)->decoding_buf = make_uninit_string (0);
+   XPROCESS (proc)->decoding_carryover = make_number (0);
+   XPROCESS (proc)->encoding_buf = make_uninit_string (0);
+   XPROCESS (proc)->encoding_carryover = make_number (0);
+ 
+   XPROCESS (proc)->inherit_coding_system_flag
+     = (NILP (buffer) || !inherit_process_coding_system
+        ? Qnil : Qt);
+ 
+ }
+ 
+ /* create and initialize a process representing a socket. */
+ static Lisp_Object 
+ make_socket_process (fd, name, childp, status, buffer) 
+      int fd; 
+      Lisp_Object name, childp, status, buffer;
+ {
+   struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
+   GCPRO4 (name, childp, status, buffer);
+   { 
+     Lisp_Object proc = make_process (name);
+     chan_process[fd] = proc;
+     XPROCESS (proc)->childp = childp;
+     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, fd);
+     XSETINT (XPROCESS (proc)->outfd, fd);
+     XPROCESS (proc)->status = status;
+     if (fd > max_process_desc)
+       max_process_desc = fd;
+     return proc;
+   }
+ }
+ 
+ /* Make FD non-blocking and add it to the input fd-sets.  */
+ static void 
+ register_fd_for_input (fd)  
+      int fd;
+ {
+ #ifdef O_NONBLOCK
+   fcntl (fd, F_SETFL, O_NONBLOCK);
+ #else
+ #ifdef O_NDELAY
+   fcntl (fd, F_SETFL, O_NDELAY);
+ #endif
+ #endif
+   FD_SET (fd, &input_wait_mask);
+   FD_SET (fd, &non_keyboard_wait_mask);
+ }
+ 
  /* open a TCP network connection to a given HOST/SERVICE.  Treated
     exactly like a normal process when reading and writing.  Only
     differences are in status display and process deletion.  A network
***************
*** 1836,1842 ****
  #endif /* HAVE_GETADDRINFO */
    int ret = 0;
    int xerrno = 0;
!   int s = -1, outch, inch;
    struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
    int retry = 0;
    int count = specpdl_ptr - specpdl;
--- 2039,2045 ----
  #endif /* HAVE_GETADDRINFO */
    int ret = 0;
    int xerrno = 0;
!   int s = -1;
    struct gcpro gcpro1, gcpro2, gcpro3, gcpro4, gcpro5;
    int retry = 0;
    int count = specpdl_ptr - specpdl;
***************
*** 2135,2167 ****
    send_command (s, C_DUMB, 1, 0);
  #endif /* TERM */
  
-   inch = s;
-   outch = s;
- 
    if (!NILP (buffer))
      buffer = Fget_buffer_create (buffer);
-   proc = make_process (name);
- 
-   chan_process[inch] = proc;
- 
- #ifdef O_NONBLOCK
-   fcntl (inch, F_SETFL, O_NONBLOCK);
- #else
- #ifdef O_NDELAY
-   fcntl (inch, F_SETFL, O_NDELAY);
- #endif
- #endif
  
!   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;
  
  #ifdef NON_BLOCKING_CONNECT
    if (!NILP (non_blocking))
--- 2338,2349 ----
    send_command (s, C_DUMB, 1, 0);
  #endif /* TERM */
  
    if (!NILP (buffer))
      buffer = Fget_buffer_create (buffer);
  
!   proc = make_socket_process (s, name, list2 (host, service), Qrun, buffer);
    XPROCESS (proc)->sentinel = XCAR (sentinel);
    XPROCESS (proc)->filter = XCDR (sentinel);
  
  #ifdef NON_BLOCKING_CONNECT
    if (!NILP (non_blocking))
***************
*** 2170,2271 ****
  	 in that case, we still need to signal this like a non-blocking
  	 connection.  */
        XPROCESS (proc)->status = Qconnect;
!       if (!FD_ISSET (inch, &connect_wait_mask))
! 	{
! 	  FD_SET (inch, &connect_wait_mask);
  	  num_pending_connects++;
  	}
      }
    else
  #endif
!     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;
  
    {
!     /* Setup coding systems for communicating with the network stream.  */
!     struct gcpro gcpro1;
!     /* Qt denotes we have not yet called Ffind_operation_coding_system.  */
!     Lisp_Object coding_systems = Qt;
!     Lisp_Object args[5], val;
  
!     if (!NILP (Vcoding_system_for_read))
!       val = Vcoding_system_for_read;
!     else if ((!NILP (buffer) && NILP (XBUFFER (buffer)->enable_multibyte_characters))
! 	     || (NILP (buffer) && NILP (buffer_defaults.enable_multibyte_characters)))
!       /* We dare not decode end-of-line format by setting VAL to
! 	 Qraw_text, because the existing Emacs Lisp libraries
! 	 assume that they receive bare code including a sequene of
! 	 CR LF.  */
!       val = Qnil;
!     else
!       {
! 	args[0] = Qopen_network_stream, args[1] = name,
! 	  args[2] = buffer, args[3] = host, args[4] = service;
! 	GCPRO1 (proc);
! 	coding_systems = Ffind_operation_coding_system (5, args);
! 	UNGCPRO;
! 	if (CONSP (coding_systems))
! 	  val = XCAR (coding_systems);
! 	else if (CONSP (Vdefault_process_coding_system))
! 	  val = XCAR (Vdefault_process_coding_system);
! 	else
! 	  val = Qnil;
!       }
!     XPROCESS (proc)->decode_coding_system = val;
  
!     if (!NILP (Vcoding_system_for_write))
!       val = Vcoding_system_for_write;
!     else if (NILP (current_buffer->enable_multibyte_characters))
!       val = Qnil;
!     else
        {
! 	if (EQ (coding_systems, Qt))
  	  {
! 	    args[0] = Qopen_network_stream, args[1] = name,
! 	      args[2] = buffer, args[3] = host, args[4] = service;
! 	    GCPRO1 (proc);
! 	    coding_systems = Ffind_operation_coding_system (5, args);
! 	    UNGCPRO;
  	  }
- 	if (CONSP (coding_systems))
- 	  val = XCDR (coding_systems);
- 	else if (CONSP (Vdefault_process_coding_system))
- 	  val = XCDR (Vdefault_process_coding_system);
- 	else
- 	  val = Qnil;
        }
!     XPROCESS (proc)->encode_coding_system = val;
!   }
! 
!   if (!proc_decode_coding_system[inch])
!     proc_decode_coding_system[inch]
!       = (struct coding_system *) xmalloc (sizeof (struct coding_system));
!   setup_coding_system (XPROCESS (proc)->decode_coding_system,
! 		       proc_decode_coding_system[inch]);
!   if (!proc_encode_coding_system[outch])
!     proc_encode_coding_system[outch]
!       = (struct coding_system *) xmalloc (sizeof (struct coding_system));
!   setup_coding_system (XPROCESS (proc)->encode_coding_system,
! 		       proc_encode_coding_system[outch]);
! 
!   XPROCESS (proc)->decoding_buf = make_uninit_string (0);
!   XPROCESS (proc)->decoding_carryover = make_number (0);
!   XPROCESS (proc)->encoding_buf = make_uninit_string (0);
!   XPROCESS (proc)->encoding_carryover = make_number (0);
! 
!   XPROCESS (proc)->inherit_coding_system_flag
!     = (NILP (buffer) || !inherit_process_coding_system
!        ? Qnil : Qt);
! 
!   UNGCPRO;
!   return proc;
  }
  #endif	/* HAVE_SOCKETS */
  
  void
--- 2352,2618 ----
  	 in that case, we still need to signal this like a non-blocking
  	 connection.  */
        XPROCESS (proc)->status = Qconnect;
!       if (!FD_ISSET (s, &connect_wait_mask))
! 	{ 
! 	  FD_SET (s, &connect_wait_mask);
  	  num_pending_connects++;
  	}
      }
    else
  #endif
!     register_fd_for_input (s);
! 
!   select_coding_system (proc);  
! 
!   UNGCPRO;
!   return proc;
! }
! 
! #define BACKLOG 5
! 
! /* Open a listening socket on PORT and return the socket descriptor. */
! static int 
! open_inet_socket (int port)
! {
!   int fd = socket (PF_INET, SOCK_STREAM, 0);
!   if (fd <= 0) goto error; 
!   { 
!     int err;
!     int optval = 1;  
!     struct sockaddr_in servaddr;
!     bzero (&servaddr, sizeof servaddr);
!     servaddr.sin_family = AF_INET;
!     servaddr.sin_port = htons (port);
!     servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
!     err = setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
!     if (err != 0) goto error;
!     err = bind (fd, (struct sockaddr *)&servaddr, sizeof servaddr);
!     if (err != 0) goto error;
!     err = listen (fd, BACKLOG);
!     if (err != 0) goto error;
!     return fd;
!   }
!   error:
!     if (fd > 0) 
!       close (fd);
!     report_file_error ("open-server-socket", Qnil);
!     abort ();
! }
! 
! static int 
! open_unix_socket (char *filename)
! {
!   int fd = socket (PF_LOCAL, SOCK_STREAM, 0);
!   if (fd <= 0) goto error; 
!   { 
!     int err;
!     struct sockaddr_un servaddr;
!     bzero (&servaddr, sizeof servaddr);
!     servaddr.sun_family = AF_LOCAL;
!     strncpy (servaddr.sun_path, filename, sizeof servaddr.sun_path);
!     err = bind (fd, (struct sockaddr *)&servaddr, sizeof servaddr);
!     if (err != 0) goto error;
!     err = listen (fd, BACKLOG);
!     if (err != 0) goto error;
!     return fd;
!   }
!   error:
!     if (fd > 0) 
!       close (fd);
!     report_file_error ("open-server-socket", Qnil);
!     abort ();
! }
! 
! /* Open a server socket on a given port.  The filter function must be
!    set before accepting connections with `accept-connection'.  The
!    process-buffer is not used. */
! 
! DEFUN ("open-server-socket", Fopen_server_socket, Sopen_server_socket,
!        3, 3, 0, 
!        doc: /* Open a server socket on a port.
! 
! Returns a process object to represent the socket.  The filter function
! can be used to accept connections.  See `accept-connection'.
! `delete-process' closes the server socket.
! 
! NAME is the name for the process.
! PROTOCOL either 'inet or 'unix.
! PORT is the port number resp. filename for the socket.  */ )
!      (name, protocol, port)
!      Lisp_Object name, protocol, port; 
! {
!   int fd;
!   CHECK_STRING (name);
!   CHECK_SYMBOL (protocol);
!   if (protocol == Qinet)
!     {
!       CHECK_NATNUM (port);
!       if ((XINT (port) < 0) || ((1 << 16) <= XINT (port)))
! 	error ("Port number out of range");
!       fd = open_inet_socket (XINT (port));
!     }
!   else if (protocol == Qunix)
!     {
!       CHECK_STRING (port);
!       fd = open_unix_socket (XSTRING (port)->data);
!     }
!   else 
!     error ("Unsupported protocol %s", XSYMBOL (protocol)->name->data);
!   return make_socket_process (fd, name, Qserver_socket, Qrun, Qnil);
! }
! 
! /* return a Lisp representation for U: 
!    (HOST PORT) for AF_INET
!    (unix FILENAME) for AF_UNIX.  */
! static Lisp_Object
! decode_sockaddr_union (union sockaddr_union *u) 
! {
!   switch (u->sa.sa_family) 
!     {
!     case AF_INET: 
        {
! 	char string[INET_ADDRSTRLEN];
! 	inet_ntop (AF_INET, &u->sin.sin_addr, string, sizeof string);
! 	return list2 (build_string (string),
! 		      make_number (ntohs (u->sin.sin_port)));
        }
+     case AF_LOCAL:
+       return list2 (Qunix, build_string (u->sun.sun_path));
+     default:
+       abort ();
+     }
+ }
+ 
+ /* Accept a connection on SERVER_SOCKET.  Remove SERVER_SOCKET from
+    the input fd-set so that only the first waiting client is accepted.
+    Then create a process representing the new connection and pass it
+    to PROCESS's filter function. */
+ static void
+ accept_client (process, server_socket)
+      Lisp_Object process;
+      int server_socket; 
+ {
+   struct gcpro gcpro1, gcpro2;
+   union sockaddr_union u;
+   socklen_t length = sizeof u;
+   int fd = accept (server_socket, (struct sockaddr*)&u, &length);
+   FD_CLR (server_socket, &input_wait_mask);
+   FD_CLR (server_socket, &non_keyboard_wait_mask);
+   if (fd == -1) 
+     { 
+       if (NILP (XPROCESS (process)->sentinel))
+ 	report_file_error ("accept", process);
+       else 
+ 	exec_sentinel (process, build_string (emacs_strerror (errno)));
+     } 
+   else 
+     {
+       Lisp_Object childp = decode_sockaddr_union (&u);
+       Lisp_Object proc = make_socket_process (fd, XPROCESS (process)->mark,
+ 					      childp, Qrun, Qnil);
+       GCPRO2 (process, proc);
+       select_coding_system (proc);
+       register_fd_for_input (fd);
+       internal_condition_case_1 (read_process_output_call,
+ 				 list3 (XPROCESS (process)->filter,
+ 					process, 
+ 					proc),
+ 				 NILP (Vdebug_on_error) ? Qerror : Qnil,
+ 				 read_process_output_error_handler);
+       UNGCPRO;
+     }
+ }
  
! DEFUN ("accept-connection", Faccept_connection, Saccept_connection, 
!        2, 2, 0,
!        doc: /* Accept a connection on the server socket. 
  
+ This function is non-blocking and returns nil.  The next incoming
+ connection will be accepted and passed to the process filter.  The
+ process filter function receives 2 arguments: the server socket and a
+ process representing the new connection.  The new process is treated
+ exactly like a network connection opened with `open-network-stream'.
+ 
+ PROCESS a process representing a server socket.
+ NAME is the name for the new connection.  */ )
+      (process, name)
+      Lisp_Object process, name;
+ {
+   CHECK_PROCESS (process);
+   if (! (SERVER_SOCKET_P (process)))
+     error ("Process %s is not a server socket process",
+ 	   XSTRING (XPROCESS (process)->name)->data);
+   CHECK_STRING (name);
    {
!     int fd = XINT (XPROCESS (process)->infd);
!     XPROCESS (process)->mark = name; /* kludge: field is otherwise unused */
!     register_fd_for_input (fd);
!     return Qnil;
!   }
! }
  
! DEFUN ("gethostbyname", Fgethostbyname, Sgethostbyname,
!        1, 1, 0,
!        doc: /* Look up a host by name.  
  
! Return the IP address and aliases for NAME.  The result is a vector of
! this form: [hostent CANONICAL-NAME ALIASES ADDRTYPE ADDRLIST]
! 
! CANONICAL-NAME is a string.
! ALIASES is a list of strings.
! ADDRTYPE is the symbol inet.
! ADDRLIST is a list of this form ((INT INT INT INT)* )
!  each INT correspond to 1 byte of the 32 bit address. */)
!      (name)
!      Lisp_Object name;
! {
!   int gcpro1;
!   struct hostent *hostent;
!   CHECK_STRING (name);
!   hostent = gethostbyname (XSTRING (name)->data);
!   if (hostent == 0) 
!     error ("%s", hstrerror (h_errno));
!   else 
!     {
!       Lisp_Object host_info = Fmake_vector (make_number (5), Qnil);
!       Lisp_Object *vector = XVECTOR (host_info)->contents;
!       GCPRO1 (host_info);
!       vector[0] = intern ("hostent");
!       vector[1] = build_string (hostent->h_name);
        {
! 	char **p = hostent->h_aliases;
! 	while (*p != 0) 
  	  {
! 	    vector[2] = Fcons (build_string (*p), vector[2]);
! 	    p++;
  	  }
        }
!       switch (hostent->h_addrtype) 
! 	{
! 	case AF_INET:
! 	  { 
! 	    struct in_addr **p = (struct in_addr **)hostent->h_addr_list;
! 	    vector[3] = Qinet;
! 	    while (*p != 0) 
! 	      {
! 		uint32_t a = ntohl ((**p).s_addr);
! 		vector[4] = Fcons (list4 (make_number ((a >> 24) & 0xff), 
! 					  make_number ((a >> 16) & 0xff), 
! 					  make_number ((a >>  8) & 0xff), 
! 					  make_number ((a >>  0) & 0xff)),
! 				   vector[4]);
! 		p++;
! 	      }
! 	    break;
! 	  }
! 	default:
! 	  abort ();
! 	}
!       UNGCPRO;
!       return host_info;
!     }
  }
+ 
  #endif	/* HAVE_SOCKETS */
  
  void
***************
*** 2911,2916 ****
--- 3258,3268 ----
  	      proc = chan_process[channel];
  	      if (NILP (proc))
  		continue;
+ 	      if (SERVER_SOCKET_P (proc)) 
+ 		{
+ 		  accept_client (proc, channel);
+ 		  continue;
+ 		}
  
  	      /* Read data from the process, starting with our
  		 buffered-ahead character if we have one.  */
***************
*** 3469,3474 ****
--- 3821,3828 ----
    if (XINT (XPROCESS (proc)->outfd) < 0)
      error ("Output file descriptor of %s is closed",
  	   XSTRING (XPROCESS (proc)->name)->data);
+   if (SERVER_SOCKET_P (proc))
+     error ("Cannot write to server socket");
  
    coding = proc_encode_coding_system[XINT (XPROCESS (proc)->outfd)];
    Vlast_coding_system_used = coding->symbol;
***************
*** 4246,4251 ****
--- 4600,4607 ----
      update_status (XPROCESS (proc));
    if (! EQ (XPROCESS (proc)->status, Qrun))
      error ("Process %s not running", XSTRING (XPROCESS (proc)->name)->data);
+   if (SERVER_SOCKET_P (proc))
+     error ("Cannot write to server socket", XSTRING (XPROCESS (proc)->name));
  
    if (CODING_REQUIRE_FLUSHING (coding))
      {
***************
*** 4311,4316 ****
--- 4667,4674 ----
  	{
  	  if (NETCONN_P (proc))
  	    Fdelete_process (proc);
+ 	  else if (SERVER_SOCKET_P (proc))
+ 	    Fdelete_process (proc);
  	  else if (XINT (XPROCESS (proc)->infd) >= 0)
  	    process_send_signal (proc, SIGHUP, Qnil, 1);
  	}
***************
*** 4861,4866 ****
--- 5219,5234 ----
    Qfailed = intern ("failed");
    staticpro (&Qfailed);
  
+   Qlisten = intern ("listen");
+   staticpro (&Qlisten);
+ 
+   Qserver_socket = intern ("server-socket");
+   staticpro (&Qserver_socket);
+   Qinet = intern ("inet");
+   staticpro (&Qinet);
+   Qunix = intern ("unix");
+   staticpro (&Qunix);
+   
    Qlast_nonmenu_event = intern ("last-nonmenu-event");
    staticpro (&Qlast_nonmenu_event);
  
***************
*** 4907,4912 ****
--- 5275,5283 ----
    defsubr (&Sstart_process);
  #ifdef HAVE_SOCKETS
    defsubr (&Sopen_network_stream);
+   defsubr (&Sopen_server_socket);
+   defsubr (&Saccept_connection);
+   defsubr (&Sgethostbyname);
  #endif /* HAVE_SOCKETS */
    defsubr (&Saccept_process_output);
    defsubr (&Sprocess_send_region);



_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-02  7:59             ` Helmut Eller
  2002-03-03  0:12               ` Kim F. Storm
@ 2002-03-03 14:39               ` Richard Stallman
  1 sibling, 0 replies; 17+ messages in thread
From: Richard Stallman @ 2002-03-03 14:39 UTC (permalink / raw)
  Cc: storm, emacs-devel

    Server sockets are IMHO different enough to merit a separate
    function.  What is the advantage of merging those concepts?

A priori it is a simplification to merge them.  But if merging them is
painful in the details, then we are better off not merging them.


_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: Non-blocking open-network-stream
  2002-03-03  0:12               ` Kim F. Storm
  2002-03-03 10:46                 ` Helmut Eller
@ 2002-03-03 16:44                 ` Mario Lang
  1 sibling, 0 replies; 17+ messages in thread
From: Mario Lang @ 2002-03-03 16:44 UTC (permalink / raw)


storm@cua.dk (Kim F. Storm) writes:

> I intent do rename the NON-BLOCKING argument to a more generic TYPE
> argument.  Eventually, it could be things like:
> 
> If HOST specified - connect to that host:
> 
> nil - blocking connect (tcp) to SERVICE on HOST
> t   - non-blocking connect (tcp) to SERVICE on HOST
> udp - open udp socket with target SERVICE on HOST
> unix - connect to unix socket on address SERVICE
> 
> If HOST is nil - open a server socket:
> 
> nil - open tcp socket listning on SERVICE port
> t   - same as nil
> udp - open udp socket bound to SERVICE port
> unix - open unix socket bound to address SERVICE

Isn't this quite limiting? What comes into mind first is that binding a 
server socket to a specific interface is not possible then.

Also, as I discussed privately with Helmut, it should be possible to specify
either 0 or nil (not sure) for the PORT to be able to assign a
arbitrary free port (dynamic server socket for e.g. DCC).

I didn't look very closely at both approaches yet, but it seems to me
from first impression that merging non-blocking connects and server sockets
into one single function is quite messy.

-- 
CYa,
   Mario <mlang@delysid.org>

_______________________________________________
Emacs-devel mailing list
Emacs-devel@gnu.org
http://mail.gnu.org/mailman/listinfo/emacs-devel


^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2002-03-03 16:44 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-02-27 17:49 Non-blocking open-network-stream Helmut Eller
     [not found] <m2u1sa7819.fsf@xaital.online-marketwatch.com>
2002-02-21 23:45 ` Kim F. Storm
2002-02-22 16:04   ` Stefan Monnier
2002-02-25 22:38   ` Kim F. Storm
2002-02-26 22:46     ` Helmut Eller
2002-02-27 11:59       ` Kim F. Storm
2002-02-28  4:08         ` Richard Stallman
2002-03-01  0:21           ` Kim F. Storm
2002-03-01  8:01             ` Juanma Barranquero
2002-03-01 10:50               ` Kim F. Storm
2002-03-01 17:10                 ` Pavel Janík
2002-03-01 21:23             ` Richard Stallman
2002-03-02  7:59             ` Helmut Eller
2002-03-03  0:12               ` Kim F. Storm
2002-03-03 10:46                 ` Helmut Eller
2002-03-03 16:44                 ` Mario Lang
2002-03-03 14:39               ` Richard Stallman

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).