Messages in this thread | | | Subject | Expected result when racing listen(2) on two sockets bound to the same address | From | Alexander Kurtz <> | Date | Wed, 23 May 2018 13:15:42 +0200 |
| |
[Please keep me CC'ed; I'm not subscribed to the list]
Hi!
The program shown below (also available at [0]) does the following:
* Create two sockets * Enable SO_REUSEADDR on both * Bind both sockets to [::1]:12345 * Spawn two threads which both call listen(2) on one socket each * Check that at least one thread succeeded
Unfortunately, when running this program on Linux 4.16, it is sometimes possible that neither thread succeeds in calling listen(2):
$ uname -a Linux shepard 4.16.0-1-amd64 #1 SMP Debian 4.16.5-1 (2018-04-29) x86_64 GNU/Linux $ time make cc -Wall -Wextra -pedantic -Werror -O3 listenrace.c -lpthread -o listenrace for i in `seq 10000`; do ./listenrace; done listenrace: listenrace.c:58: main: Assertion `result1 == 0 || result2 == 0' failed. Aborted listenrace: listenrace.c:58: main: Assertion `result1 == 0 || result2 == 0' failed. Aborted listenrace: listenrace.c:58: main: Assertion `result1 == 0 || result2 == 0' failed. Aborted
real 0m8.201s user 0m6.801s sys 0m2.141s $
As can be seen, on 3 runs (out of 10000) calling listen(2) failed in *both* threads. Is this to be expected (i.e. "don't do this then") or could this be some race condition in the Linux kernel?
Best regards
Alexander Kurtz
[0] https://github.com/AlexanderKurtz/listenrace
==> Makefile <== CFLAGS = -Wall -Wextra -pedantic -Werror -O3 LDLIBS = -lpthread
all: listenrace for i in `seq 10000`; do ./listenrace; done
listenrace: listenrace.c
clean: rm 'listenrace'
.PHONY: all clean
==> listenrace.c <== #include <assert.h> // for assert #include <netdb.h> // for addrinfo, getaddrinfo #include <pthread.h> // for pthread_create, pthread_join, pthread_t #include <stdint.h> // for intptr_t #include <stdio.h> // for NULL #include <sys/socket.h> // for bind, setsockopt, listen, socket, AF_INET6
int my_socket (struct addrinfo* address) { // Create socket int fd = socket (address->ai_family, address->ai_socktype, 0); assert (fd >= 0);
// Enable SO_REUSEADDR assert (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, & (int) { 1 }, sizeof (int)) == 0);
// Bind socket assert (bind (fd, address->ai_addr, address->ai_addrlen) == 0);
// Return socket return fd; }
void* my_listen (void* data) { int fd = (intptr_t) data;
intptr_t result = listen (fd, 64);
return (void*) result; }
int main () { // Create sockaddr struct struct addrinfo hints = { .ai_family = AF_INET6, .ai_socktype = SOCK_STREAM, }; struct addrinfo* address = NULL; assert (getaddrinfo ("::1", "12345", &hints, &address) == 0); assert (address);
// Create sockets int socket1 = my_socket (address); int socket2 = my_socket (address);
// Create threads pthread_t thread1; pthread_t thread2; assert (pthread_create (&thread1, NULL, my_listen, (void*) (intptr_t) socket1) == 0); assert (pthread_create (&thread2, NULL, my_listen, (void*) (intptr_t) socket2) == 0);
// Wait for the threads void* result1 = NULL; void* result2 = NULL; assert (pthread_join (thread1, &result1) == 0); assert (pthread_join (thread2, &result2) == 0);
// Check that at least one thread could successfully call listen assert (result1 == 0 || result2 == 0);
return 0; }[unhandled content-type:application/pgp-signature] | |