NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: kern/60786: poll reports socket writable after shutdown(SHUT_WR)
The following reply was made to PR kern/60786; it has been noted by GNATS.
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: Robert Elz <kre%munnari.OZ.AU@localhost>
Cc: gnats-bugs%NetBSD.org@localhost, netbsd-bugs%NetBSD.org@localhost
Subject: Re: kern/60786: poll reports socket writable after shutdown(SHUT_WR)
Date: Fri, 25 Sep 2026 01:06:50 +0000
This is a multi-part message in MIME format.
--=_nsoQjY3CD7bBvFEdm/FiyAa+axP8majm
Content-Transfer-Encoding: quoted-printable
> Date: Fri, 25 Sep 2026 05:49:25 +0700
> From: Robert Elz <kre%munnari.OZ.AU@localhost>
>=20
> Date: Thu, 24 Sep 2026 22:05:00 +0000 (UTC)
> From: "campbell+netbsd%mumble.net@localhost via gnats" <gnats-admin@NetB=
SD.org>
> Message-ID: <20260924220500.D099A1A923E%mollari.NetBSD.org@localhost>
>=20
> | I don't know if there's language in POSIX about this but surely
> | it is nonsensical for poll to claim that a socket is writable
> | after it has been shut down in the writing direction!
>=20
> poll() (and select()) have never claimed anything was writable or
> readable - they merely indicate whether or not a write (or read) will
> hang if attempted at the time of the poll/select (and assuming some
> other process/thread isn't manipulating the same object).
OK, in that case shutdown(SHUT_WR) should cause concurrent poll for
POLLOUT to wake, but it doesn't!
--=_nsoQjY3CD7bBvFEdm/FiyAa+axP8majm
Content-Type: text/plain; charset="ISO-8859-1"; name="shutpoll"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment; filename="shutpoll.c"
#include <sys/socket.h>
#include <netinet/in.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
struct context {
pthread_barrier_t bar;
int peer;
int fd;
};
static void *
start_thread(void *cookie)
{
struct context *C =3D cookie;
(void)pthread_barrier_wait(&C->bar);
if (usleep(1) =3D=3D -1)
err(EXIT_FAILURE, "usleep");
if (shutdown(C->peer, SHUT_WR) =3D=3D -1)
err(EXIT_FAILURE, "shutdown");
(void)pthread_barrier_wait(&C->bar);
if (usleep(1) =3D=3D -1)
err(EXIT_FAILURE, "usleep");
if (close(C->fd) =3D=3D -1)
err(EXIT_FAILURE, "close");
return NULL;
}
int
main(void)
{
struct context ctx, *C =3D &ctx;
int listener;
union {
struct sockaddr sa;
struct sockaddr_in sin;
} addr;
socklen_t addrlen;
int flags;
char buf[BUFSIZ] =3D {0};
pthread_t t;
struct pollfd pfd;
int nfds;
memset(C, 0, sizeof(*C));
errno =3D pthread_barrier_init(&C->bar, NULL, 2);
if (errno)
err(EXIT_FAILURE, "pthread_barrier_init");
/*
* Listen on a random port number on 127.0.0.1.
*/
listener =3D socket(AF_INET, SOCK_STREAM, 0);
if (listener =3D=3D -1)
err(EXIT_FAILURE, "socket");
memset(&addr, 0, sizeof(addr));
addr.sin.sin_family =3D AF_INET;
addr.sin.sin_port =3D htons(0);
addr.sin.sin_addr.s_addr =3D htonl(INADDR_LOOPBACK);
if (bind(listener, &addr.sa, sizeof(addr.sin)) =3D=3D -1)
err(EXIT_FAILURE, "bind");
if (listen(listener, 1) =3D=3D -1)
err(EXIT_FAILURE, "listen");
/*
* Get the socket address that was assigned so we can connect
* to it (we need to find the port number).
*/
addrlen =3D sizeof(addr);
if (getsockname(listener, &addr.sa, &addrlen) =3D=3D -1)
err(EXIT_FAILURE, "getsockname");
/*
* Connect to the listener.
*/
C->fd =3D socket(AF_INET, SOCK_STREAM, 0);
if (C->fd =3D=3D -1)
err(EXIT_FAILURE, "socket");
if (connect(C->fd, &addr.sa, addrlen) =3D=3D -1)
err(EXIT_FAILURE, "connect");
/*
* Accept the connetion.
*/
C->peer =3D accept(listener, NULL, NULL);
if (C->peer =3D=3D -1)
err(EXIT_FAILURE, "accept");
/*
* Make the peer nonblocking so we can fill its buffer to the
* brim.
*/
flags =3D fcntl(C->peer, F_GETFL);
if (flags =3D=3D -1)
err(EXIT_FAILURE, "fcntl(F_GETFL)");
if (fcntl(C->peer, F_SETFL, flags|O_NONBLOCK) =3D=3D -1)
err(EXIT_FAILURE, "fcntl(F_GETFL)");
for (;;) {
if (write(C->peer, buf, sizeof(buf)) =3D=3D -1) {
if (errno =3D=3D EAGAIN)
break;
err(EXIT_FAILURE, "write");
}
}
/*
* Verify that peer is NOT writable right now.
*/
pfd.fd =3D C->peer;
pfd.events =3D POLLOUT|POLLWRNORM;
nfds =3D poll(&pfd, 1, 0);
if (nfds =3D=3D -1)
err(EXIT_FAILURE, "nfds");
if (pfd.revents & (POLLOUT|POLLWRNORM))
errx(EXIT_FAILURE, "writable but the buffer is full! [1]");
/*
* Wait for the thread to shutdown writes. After this point,
* we cannot write any further, so there should be no reason
* for poll to report a transition to writable.
*/
alarm(1);
(void)pthread_barrier_wait(&C->bar);
pfd.fd =3D C->peer;
pfd.events =3D POLLOUT|POLLWRNORM;
nfds =3D poll(&pfd, 1, 0);
if (nfds =3D=3D -1)
err(EXIT_FAILURE, "nfds");
if (pfd.revents & (POLLOUT|POLLWRNORM))
errx(EXIT_FAILURE, "writable but the buffer is full! [2]");
/*
* Start a thread to close the socket.
*/
errno =3D pthread_create(&t, NULL, &start_thread, C);
if (errno)
err(EXIT_FAILURE, "pthread_create");
/*
* Wait to start and see what poll says is ready.
*/
(void)pthread_barrier_wait(&C->bar);
/*
* See what I/O is available.
*/
pfd.fd =3D C->peer;
pfd.events =3D POLLIN|POLLOUT|POLLRDNORM|POLLWRNORM;
nfds =3D poll(&pfd, 1, -1);
if (nfds =3D=3D -1)
err(EXIT_FAILURE, "poll");
if (nfds !=3D 1)
errx(EXIT_FAILURE, "poll returned nfds%d", nfds);
fprintf(stderr, "pfd.revents=3D0x%x\n", pfd.revents);
if (pfd.events & (POLLOUT|POLLWRNORM))
errx(EXIT_FAILURE, "writable but shutdown!");
errno =3D pthread_join(t, NULL);
if (errno)
err(EXIT_FAILURE, "pthread_join");
return 0;
}
--=_nsoQjY3CD7bBvFEdm/FiyAa+axP8majm--
Home |
Main Index |
Thread Index |
Old Index