[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <a3ecf04de4fded7c71284a9695b7146d284d1f22.camel@kurtz.be>
Date: Wed, 23 May 2018 13:15:42 +0200
From: Alexander Kurtz <alexander@...tz.be>
To: linux-kernel@...r.kernel.org
Subject: Expected result when racing listen(2) on two sockets bound to the
same address
[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;
}
Download attachment "signature.asc" of type "application/pgp-signature" (834 bytes)
Powered by blists - more mailing lists