emacs-pretest-bug
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

non-blocking connect()


From: 山本和彦
Subject: non-blocking connect()
Date: Mon, 01 Nov 2004 13:38:46 +0900 (JST)

[description]

Due to implementation flaw of src/process.c/make_network_process(),
open-network-stream() occasionally fails to make a TCP connection on
BSD variants.

[analysis]

I don't know why but connect(2) in make_network_process() on Emacs
21.3.50 is occasionally interrupted by signals. (Some friends and I
met this phenomenon on Emacs 21.3.5 but we did not experience this on
Emacs 21.3).

When connect(2) is interrupted, it retunrs EINTR. In this case,
make_network_process() tries connect(2) again with the same socket
(retry_connect:).

In many cases, this results in EADDRINUSE on FreeBSD and in EALREADY
on NetBSD. If EADDRINUSE is returned, make_network_process() sleeps
one second and goes to retry_connect:.

Since the current make_network_process() does not handle EALREADY, a
TCP connection cannot be established on NetBSD if connect(2) is
interrupted.

Even on FreeBSD, it takes several seconds to make a TCP connection.

If we add code to handle EALREADY, a TCP connection can be established
somehow on NetBSD. But the inefficiency appeared on FreeBSD still
remains.

[solution]

The best way to fix this problem is use non-blocking socket for
connect(). 

Both Emacs 21.3 and Emacs 21.3.50 have the following comments in
make_network_process().

        ----
         It'd be nice to be able to control the connect timeout
         though.  Would non-blocking connect calls be portable?
        ----

Since Emacs 21.3.50 already has code to use non-blocking socket
(:nowait t), I think non-blocking socket for connect(2) should be
acceptable.

[patch]

The following code fixes the problem described above.

On platforms that supports non-blocking socket, the new code of
non-blocking socket for connect(2) is used.

On platforms that does NOT support non-blocking socket, the original
code is used.

This code has been tested on:
        NetBSD current
        FreeBSD 5.3-RC1
        FreeBSD 6.0-CURRENT
        SunOS xxxx 5.7 Generic_106541-30 sun4u sparc SUNW,Ultra-80

The current time out for select() is 10 seconds. This value should be
considered when merged.

[note]

If this patch is merged to Emacs 21.3.50, please tell me.  I will tell
my address on personal e-mail so that ASSIGNMENT paper can be sent to
me. I will sign and return it ASAP.

--Kazu Yamamoto

Index: src/process.c
===================================================================
RCS file: /cvsroot/emacs/emacs/src/process.c,v
retrieving revision 1.442
diff -c -r1.442 process.c
*** src/process.c       29 Sep 2004 23:43:08 -0000      1.442
--- src/process.c       1 Nov 2004 02:28:26 -0000
***************
*** 2727,2732 ****
--- 2727,2737 ----
    int is_server = 0, backlog = 5;
    int socktype;
    int family = -1;
+ #ifdef NON_BLOCKING_CONNECT
+   int flags, len;
+   fd_set rset, wset;
+   struct timeval timeout;
+ #endif
  
    if (nargs == 0)
      return Qnil;
***************
*** 3094,3100 ****
--- 3099,3146 ----
  
          break;
        }
+ #ifdef NON_BLOCKING_CONNECT
+       flags = fcntl(s, F_GETFL, 0);
+       ret = fcntl(s, F_SETFL, flags | O_NONBLOCK);
+       if (ret < 0) goto next;
+ 
+       ret = connect (s, lres->ai_addr, lres->ai_addrlen);
+       if (ret == 0)
+       {
+         if (fcntl(s, F_SETFL, flags) < 0)
+           goto next;
+         break;
+       }
+       if ((ret < 0) && (errno != EINPROGRESS)) goto next;
  
+       FD_ZERO(&rset);
+       FD_SET(s,&rset);
+       wset = rset;
+       timeout.tv_sec=10; /* need to consider */
+       timeout.tv_usec=0;
+ 
+       immediate_quit = 1;
+       QUIT;
+       select(s+1, &rset, &wset, NULL, &timeout);
+       immediate_quit = 0;
+       
+       if (FD_ISSET(s, &rset) || FD_ISSET(s, &wset))
+       {
+       len = sizeof(xerrno);
+       if (getsockopt(s, SOL_SOCKET, SO_ERROR, &xerrno, &len) < 0)
+         goto next;
+       if (xerrno != 0) {
+         errno = xerrno;
+         goto next;
+       }
+       if (fcntl(s, F_SETFL, flags) < 0)
+         goto next;
+       break;
+       }
+ 
+     next:
+       xerrno = errno;
+ #else /* NON_BLOCKING_CONNECT */
      retry_connect:
  
        immediate_quit = 1;
***************
*** 3149,3155 ****
          retry++;
          goto retry_connect;
        }
! 
        /* Discard the unwind protect closing S.  */
        specpdl_ptr = specpdl + count1;
        emacs_close (s);
--- 3195,3201 ----
          retry++;
          goto retry_connect;
        }
! #endif /* NON_BLOCKING_CONNECT */ 
        /* Discard the unwind protect closing S.  */
        specpdl_ptr = specpdl + count1;
        emacs_close (s);







reply via email to

[Prev in Thread] Current Thread [Next in Thread]