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