[Top][All Lists]
[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);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- non-blocking connect(),
山本和彦 <=