SIGINFO interrupts connect() with libpthread
Robert Watson
rwatson at FreeBSD.org
Sat Oct 8 03:16:20 PDT 2005
On Fri, 7 Oct 2005, Daniel Eischen wrote:
> On Thu, 6 Oct 2005, Robert Watson wrote:
>
>> I was writing test tools yesterday, and was a bit surprised to find
>> that hitting ctrl-T interrupts connect() for applications linked
>> against libpthread(). I wrote a simple test tool and found that this
>> is not the case for any of the other thread libraries (which seems
>> correct to me). Test tool attached:
>
> This isn't a problem with libpthread. If you install a signal handler
> for SIGINFO in your application with sa_flags = SA_RESTART, it gets
> interrupted even when just linked with libc.
Unlike, say, an I/O operation, the notion of restarting a connect() system
call is fairly fraught with peril. Normally, attempts to call connect()
on a socket that is already in the process of connecting will return
EALREADY, since you don't want to further frob the protocol state machine.
A bit of googling suggests that Stephens talks about this in some detail,
pointing out that in the EINTR case, the application will really need to
select() for the socket becoming writable in order to wait for the
connection attempt to finish (or fail). While a bit awkward for the
application, it appears these are not uncommon semantics for the system
call, and suggest the perils of using blocking I/O with signals are
significant. Unfortunately, the problem with this libpthread feature is
that, unlike our other thread libraries, it exposes the application to
this peril even if the application itself doesn't actively use signals.
Is your suggestion that connect() should detect an attempt to connect to
the same host and "continue where it left off"? POSIX tells us that
EALREADY should be returned if that is attempted:
[EALREADY]
A connection request is already in progress for the specified socket.
I guess in principle we could add a thread flag to indicate that a system
call has been restarted, and therefore that the system call should adopt
different blocking/waiting smeantics? There are a lot of potential
complications though, since you might want to know when it was restarted.
In the connect() case it might be straight forward though, converting the
current logic to something that short circuits passage into the socket
and protocol code:
if ((so->so_state & SS_ISCONNECTING) &&
(td->td_flags & TDF_SARESTARTED))
goto skip_proto:
...
error = soconnect(so, sa td);
...
skip_proto:
if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
error = EINPROGRESS;
goto done1;
}
...
This is only good if we can assume the only interruptible sleep of
interest is the msleep() waiting for a transition to SS_ISCONNECTED. If
the protocol code itself (called via soconnect()) has interruptible
sleeps, then we might need more handling here. I have to admit the whole
idea of this is worrying, but then so is the socket life cycle state
machine :-).
Robert N M Watson
>
> --
> DE
>
> /*-
> * Copyright (c) 2005 Robert N. M. Watson
> * All rights reserved.
> *
> * Redistribution and use in source and binary forms, with or without
> * modification, are permitted provided that the following conditions
> * are met:
> * 1. Redistributions of source code must retain the above copyright
> * notice, this list of conditions and the following disclaimer.
> * 2. Redistributions in binary form must reproduce the above copyright
> * notice, this list of conditions and the following disclaimer in the
> * documentation and/or other materials provided with the distribution.
> *
> * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
> * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
> * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
> * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
> * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
> * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
> * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
> * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
> * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
> * SUCH DAMAGE.
> *
> * $FreeBSD$
> */
>
> #include <sys/types.h>
> #include <sys/socket.h>
>
> #include <netinet/in.h>
>
> #include <arpa/inet.h>
>
> #include <err.h>
> #include <pthread.h>
> #include <signal.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
>
> void
> sighandler(int sig)
> {
> }
>
> int
> main(int argc, char *argv[])
> {
> struct sockaddr_in sin;
> struct sigaction act;
> int sock;
>
> if (argc != 3)
> errx(-1, "usage: connect [ip] [port]");
>
> sigfillset(&act.sa_mask);
> act.sa_flags = SA_RESTART;
> act.sa_handler = sighandler;
> sigaction(SIGINFO, &act, NULL);
>
> bzero(&sin, sizeof(sin));
> sin.sin_len = sizeof(sin);
> sin.sin_family = AF_INET;
> sin.sin_addr.s_addr = inet_addr(argv[1]);
> sin.sin_port = htons(atoi(argv[2]));
>
> sock = socket(PF_INET, SOCK_STREAM, 0);
> if (sock < 0)
> err(-1, "socket(PF_INET, SOCK_STREAM, 0)");
>
> printf("Connecting to %s:%d\n", inet_ntoa(sin.sin_addr),
> htons(sin.sin_port));
> if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0)
> err(-1, "connect(%s %d)", inet_ntoa(sin.sin_addr),
> htons(sin.sin_port));
> printf("Connected\n");
>
> return (0);
> }
>
>
>
More information about the freebsd-threads
mailing list