Changeset View
Standalone View
lib/libc/tests/resolv/resolv_test.c
Show All 32 Lines | |||||
__RCSID("$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $"); | __RCSID("$NetBSD: resolv.c,v 1.6 2004/05/23 16:59:11 christos Exp $"); | ||||
#include <sys/types.h> | #include <sys/types.h> | ||||
#include <sys/socket.h> | #include <sys/socket.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdatomic.h> | |||||
#include <netdb.h> | #include <netdb.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <stringlist.h> | #include <stringlist.h> | ||||
#include <atf-c.h> | #include <atf-c.h> | ||||
#define NTHREADS 10 | #define NTHREADS 10 | ||||
#define NHOSTS 100 | #define NHOSTS 100 | ||||
#define WS " \t\n\r" | #define WS " \t\n\r" | ||||
enum method { | enum method { | ||||
METHOD_GETADDRINFO, | METHOD_GETADDRINFO, | ||||
METHOD_GETHOSTBY, | METHOD_GETHOSTBY, | ||||
METHOD_GETIPNODEBY | METHOD_GETIPNODEBY | ||||
}; | }; | ||||
static StringList *hosts = NULL; | static StringList *hosts = NULL; | ||||
static int *ask = NULL; | static _Atomic(int) *ask = NULL; | ||||
static int *got = NULL; | static _Atomic(int) *got = NULL; | ||||
static bool debug_output = 0; | |||||
static void load(const char *); | static void load(const char *); | ||||
static void resolvone(int, enum method); | static void resolvone(long, int, enum method); | ||||
static void *resolvloop(void *); | static void *resolvloop(void *); | ||||
static void run(int *, enum method); | static pthread_t run(int, enum method, long); | ||||
static pthread_mutex_t stats = PTHREAD_MUTEX_INITIALIZER; | #define DBG(...) do { \ | ||||
if (debug_output) \ | |||||
dprintf(STDOUT_FILENO, __VA_ARGS__); \ | |||||
} while (0) | |||||
static void | static void | ||||
load(const char *fname) | load(const char *fname) | ||||
{ | { | ||||
FILE *fp; | FILE *fp; | ||||
size_t linecap; | size_t linecap; | ||||
char *line; | char *line; | ||||
Show All 11 Lines | while (getline(&line, &linecap, fp) >= 0) { | ||||
} | } | ||||
} | } | ||||
free(line); | free(line); | ||||
(void)fclose(fp); | (void)fclose(fp); | ||||
} | } | ||||
static int | static int | ||||
resolv_getaddrinfo(pthread_t self, char *host, int port) | resolv_getaddrinfo(long threadnum, char *host, int port, const char **errstr) | ||||
{ | { | ||||
char portstr[6], buf[1024], hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; | char portstr[6], hbuf[NI_MAXHOST], pbuf[NI_MAXSERV]; | ||||
struct addrinfo hints, *res; | struct addrinfo hints, *res; | ||||
int error, len; | int error; | ||||
snprintf(portstr, sizeof(portstr), "%d", port); | snprintf(portstr, sizeof(portstr), "%d", port); | ||||
memset(&hints, 0, sizeof(hints)); | memset(&hints, 0, sizeof(hints)); | ||||
hints.ai_family = AF_UNSPEC; | hints.ai_family = AF_UNSPEC; | ||||
hints.ai_flags = AI_PASSIVE; | hints.ai_flags = AI_PASSIVE; | ||||
hints.ai_socktype = SOCK_STREAM; | hints.ai_socktype = SOCK_STREAM; | ||||
error = getaddrinfo(host, portstr, &hints, &res); | error = getaddrinfo(host, portstr, &hints, &res); | ||||
len = snprintf(buf, sizeof(buf), "%p: host %s %s\n", | |||||
self, host, error ? "not found" : "ok"); | |||||
(void)write(STDOUT_FILENO, buf, len); | |||||
if (error == 0) { | if (error == 0) { | ||||
DBG("T%ld: host %s ok\n", threadnum, host); | |||||
memset(hbuf, 0, sizeof(hbuf)); | memset(hbuf, 0, sizeof(hbuf)); | ||||
memset(pbuf, 0, sizeof(pbuf)); | memset(pbuf, 0, sizeof(pbuf)); | ||||
getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), | getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf), | ||||
pbuf, sizeof(pbuf), 0); | pbuf, sizeof(pbuf), 0); | ||||
len = snprintf(buf, sizeof(buf), | DBG("T%ld: reverse %s %s\n", threadnum, hbuf, pbuf); | ||||
"%p: reverse %s %s\n", self, hbuf, pbuf); | |||||
(void)write(STDOUT_FILENO, buf, len); | |||||
} | |||||
if (error == 0) | |||||
freeaddrinfo(res); | freeaddrinfo(res); | ||||
} else { | |||||
*errstr = gai_strerror(error); | |||||
DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); | |||||
} | |||||
return error; | return error; | ||||
} | } | ||||
static int | static int | ||||
resolv_gethostby(pthread_t self, char *host) | resolv_gethostby(long threadnum, char *host, const char **errstr) | ||||
{ | { | ||||
char buf[1024]; | char buf[1024]; | ||||
struct hostent *hp, *hp2; | struct hostent *hp, *hp2; | ||||
int len; | |||||
hp = gethostbyname(host); | hp = gethostbyname(host); | ||||
len = snprintf(buf, sizeof(buf), "%p: host %s %s\n", | |||||
self, host, (hp == NULL) ? "not found" : "ok"); | |||||
(void)write(STDOUT_FILENO, buf, len); | |||||
if (hp) { | if (hp) { | ||||
DBG("T%ld: host %s ok\n", threadnum, host); | |||||
memcpy(buf, hp->h_addr, hp->h_length); | memcpy(buf, hp->h_addr, hp->h_length); | ||||
hp2 = gethostbyaddr(buf, hp->h_length, hp->h_addrtype); | hp2 = gethostbyaddr(buf, hp->h_length, hp->h_addrtype); | ||||
if (hp2) { | if (hp2) { | ||||
len = snprintf(buf, sizeof(buf), | DBG("T%ld: reverse %s\n", threadnum, hp2->h_name); | ||||
"%p: reverse %s\n", self, hp2->h_name); | |||||
(void)write(STDOUT_FILENO, buf, len); | |||||
} | } | ||||
} else { | |||||
*errstr = hstrerror(h_errno); | |||||
DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); | |||||
} | } | ||||
return hp ? 0 : -1; | return hp ? 0 : h_errno; | ||||
} | } | ||||
static int | static int | ||||
resolv_getipnodeby(pthread_t self, char *host) | resolv_getipnodeby(long threadnum, char *host, const char **errstr) | ||||
{ | { | ||||
char buf[1024]; | char buf[1024]; | ||||
struct hostent *hp, *hp2; | struct hostent *hp, *hp2; | ||||
int len, h_error; | int error = 0; | ||||
hp = getipnodebyname(host, AF_INET, 0, &h_error); | hp = getipnodebyname(host, AF_INET, 0, &error); | ||||
len = snprintf(buf, sizeof(buf), "%p: host %s %s\n", | |||||
self, host, (hp == NULL) ? "not found" : "ok"); | |||||
(void)write(STDOUT_FILENO, buf, len); | |||||
if (hp) { | if (hp) { | ||||
DBG("T%ld: host %s ok\n", threadnum, host); | |||||
memcpy(buf, hp->h_addr, hp->h_length); | memcpy(buf, hp->h_addr, hp->h_length); | ||||
hp2 = getipnodebyaddr(buf, hp->h_length, hp->h_addrtype, | hp2 = getipnodebyaddr(buf, hp->h_length, hp->h_addrtype, | ||||
&h_error); | &error); | ||||
if (hp2) { | if (hp2) { | ||||
len = snprintf(buf, sizeof(buf), | DBG("T%ld: reverse %s\n", threadnum, hp2->h_name); | ||||
"%p: reverse %s\n", self, hp2->h_name); | |||||
(void)write(STDOUT_FILENO, buf, len); | |||||
} | |||||
if (hp2) | |||||
freehostent(hp2); | freehostent(hp2); | ||||
} | } | ||||
if (hp) | |||||
freehostent(hp); | freehostent(hp); | ||||
return hp ? 0 : -1; | } else { | ||||
*errstr = hstrerror(error); | |||||
DBG("T%ld: host %s not found: %s\n", threadnum, host, *errstr); | |||||
} | } | ||||
return hp ? 0 : error; | |||||
} | |||||
static void | static void | ||||
resolvone(int n, enum method method) | resolvone(long threadnum, int n, enum method method) | ||||
{ | { | ||||
char buf[1024]; | const char* errstr = NULL; | ||||
pthread_t self = pthread_self(); | |||||
size_t i = (random() & 0x0fffffff) % hosts->sl_cur; | size_t i = (random() & 0x0fffffff) % hosts->sl_cur; | ||||
char *host = hosts->sl_str[i]; | char *host = hosts->sl_str[i]; | ||||
int error, len; | int error; | ||||
len = snprintf(buf, sizeof(buf), "%p: %d resolving %s %d\n", | DBG("T%ld: %d resolving %s %zd\n", threadnum, n, host, i); | ||||
self, n, host, (int)i); | |||||
(void)write(STDOUT_FILENO, buf, len); | |||||
error = 0; | |||||
switch (method) { | switch (method) { | ||||
case METHOD_GETADDRINFO: | case METHOD_GETADDRINFO: | ||||
error = resolv_getaddrinfo(self, host, i); | error = resolv_getaddrinfo(threadnum, host, i, &errstr); | ||||
break; | break; | ||||
case METHOD_GETHOSTBY: | case METHOD_GETHOSTBY: | ||||
error = resolv_gethostby(self, host); | error = resolv_gethostby(threadnum, host, &errstr); | ||||
break; | break; | ||||
case METHOD_GETIPNODEBY: | case METHOD_GETIPNODEBY: | ||||
error = resolv_getipnodeby(self, host); | error = resolv_getipnodeby(threadnum, host, &errstr); | ||||
break; | break; | ||||
default: | default: | ||||
/* UNREACHABLE */ | /* UNREACHABLE */ | ||||
/* XXX Needs an __assert_unreachable() for userland. */ | /* XXX Needs an __assert_unreachable() for userland. */ | ||||
assert(0 && "Unreachable segment reached"); | assert(0 && "Unreachable segment reached"); | ||||
abort(); | abort(); | ||||
break; | break; | ||||
} | } | ||||
pthread_mutex_lock(&stats); | atomic_fetch_add_explicit(&ask[i], 1, memory_order_relaxed); | ||||
ask[i]++; | if (error == 0) | ||||
got[i] += error == 0; | atomic_fetch_add_explicit(&got[i], 1, memory_order_relaxed); | ||||
pthread_mutex_unlock(&stats); | else if (got[i] != 0) | ||||
jhb: I think if you cared about the error you could probably write this simpler as:
```… | |||||
Done Inline ActionsI guess could do that instead, but since fetchadd returns the old value, there's no need to load it again? Multiple threads are reading+writing to both ask[] and got[]. I think using _Atomic(int) * will ensure that the ++ is a fetchadd, but might as well be explicit. arichardson: I guess could do that instead, but since fetchadd returns the old value, there's no need to… | |||||
Done Inline ActionsIn the code above, you don't load got[i] twice as you either increment it for error == 0, or you check it against 0 for error != 0. I find the current code a bit confusing in that case (it intentionally adds 0 sometimes). It was a cute trick to avoid a branch perhaps under the mutex (but not really), but since you have to check error explicitly for the new stderr message, you might as well avoid the cute "add 0" trick. I understand you need atomics, but using 'fetchadd' instead of plain 'add' for ask[i] when you don't use the returned value is what I found confusing. jhb: In the code above, you don't load got[i] twice as you either increment it for error == 0, or… | |||||
Done Inline ActionsAh sorry I misunderstood. I'll update it to avoid the add 0 arichardson: Ah sorry I misunderstood. I'll update it to avoid the add 0 | |||||
fprintf(stderr, | |||||
"T%ld ERROR after previous success for %s: %d (%s)\n", | |||||
threadnum, hosts->sl_str[i], error, errstr); | |||||
} | } | ||||
struct resolvloop_args { | struct resolvloop_args { | ||||
int *nhosts; | int nhosts; | ||||
enum method method; | enum method method; | ||||
long threadnum; | |||||
}; | }; | ||||
static void * | static void * | ||||
resolvloop(void *p) | resolvloop(void *p) | ||||
{ | { | ||||
struct resolvloop_args *args = p; | struct resolvloop_args *args = p; | ||||
int nhosts = args->nhosts; | |||||
if (*args->nhosts == 0) { | if (nhosts == 0) { | ||||
free(args); | free(args); | ||||
return NULL; | return NULL; | ||||
} | } | ||||
do | do { | ||||
resolvone(*args->nhosts, args->method); | resolvone(args->threadnum, nhosts, args->method); | ||||
while (--(*args->nhosts)); | } while (--nhosts); | ||||
free(args); | free(args); | ||||
return NULL; | return (void *)(uintptr_t)nhosts; | ||||
} | } | ||||
static void | static pthread_t | ||||
run(int *nhosts, enum method method) | run(int nhosts, enum method method, long i) | ||||
{ | { | ||||
pthread_t self; | pthread_t t; | ||||
int rc; | int rc; | ||||
struct resolvloop_args *args; | struct resolvloop_args *args; | ||||
/* Created thread is responsible for free(). */ | /* Created thread is responsible for free(). */ | ||||
args = malloc(sizeof(*args)); | args = malloc(sizeof(*args)); | ||||
ATF_REQUIRE(args != NULL); | ATF_REQUIRE(args != NULL); | ||||
args->nhosts = nhosts; | args->nhosts = nhosts; | ||||
args->method = method; | args->method = method; | ||||
self = pthread_self(); | args->threadnum = i + 1; | ||||
rc = pthread_create(&self, NULL, resolvloop, args); | rc = pthread_create(&t, NULL, resolvloop, args); | ||||
ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc)); | ATF_REQUIRE_MSG(rc == 0, "pthread_create failed: %s", strerror(rc)); | ||||
return t; | |||||
} | } | ||||
static int | static int | ||||
run_tests(const char *hostlist_file, enum method method) | run_tests(const char *hostlist_file, enum method method) | ||||
{ | { | ||||
size_t nthreads = NTHREADS; | size_t nthreads = NTHREADS; | ||||
pthread_t *threads; | |||||
size_t nhosts = NHOSTS; | size_t nhosts = NHOSTS; | ||||
size_t i; | size_t i; | ||||
int c, done, *nleft; | int c; | ||||
hosts = sl_init(); | hosts = sl_init(); | ||||
srandom(1234); | srandom(1234); | ||||
debug_output = getenv("DEBUG_OUTPUT") != NULL; | |||||
load(hostlist_file); | load(hostlist_file); | ||||
ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file); | ATF_REQUIRE_MSG(0 < hosts->sl_cur, "0 hosts in %s", hostlist_file); | ||||
nleft = malloc(nthreads * sizeof(int)); | |||||
ATF_REQUIRE(nleft != NULL); | |||||
ask = calloc(hosts->sl_cur, sizeof(int)); | ask = calloc(hosts->sl_cur, sizeof(int)); | ||||
ATF_REQUIRE(ask != NULL); | ATF_REQUIRE(ask != NULL); | ||||
got = calloc(hosts->sl_cur, sizeof(int)); | got = calloc(hosts->sl_cur, sizeof(int)); | ||||
ATF_REQUIRE(got != NULL); | ATF_REQUIRE(got != NULL); | ||||
threads = calloc(nthreads, sizeof(pthread_t)); | |||||
ATF_REQUIRE(threads != NULL); | |||||
for (i = 0; i < nthreads; i++) { | for (i = 0; i < nthreads; i++) { | ||||
nleft[i] = nhosts; | threads[i] = run(nhosts, method, i); | ||||
run(&nleft[i], method); | |||||
} | } | ||||
/* Wait for all threads to join and check that they checked all hosts */ | |||||
for (done = 0; !done;) { | |||||
done = 1; | |||||
for (i = 0; i < nthreads; i++) { | for (i = 0; i < nthreads; i++) { | ||||
if (nleft[i] != 0) { | size_t remaining; | ||||
done = 0; | |||||
break; | remaining = (uintptr_t)pthread_join(threads[i], NULL); | ||||
ATF_CHECK_EQ_MSG(0, remaining, | |||||
"Thread %zd still had %zd hosts to check!", i, remaining); | |||||
} | } | ||||
} | |||||
sleep(1); | |||||
} | |||||
c = 0; | c = 0; | ||||
for (i = 0; i < hosts->sl_cur; i++) { | for (i = 0; i < hosts->sl_cur; i++) { | ||||
if (ask[i] != got[i] && got[i] != 0) { | if (got[i] != 0) { | ||||
printf("Error: host %s ask %d got %d\n", | ATF_CHECK_EQ_MSG(ask[i], got[i], | ||||
hosts->sl_str[i], ask[i], got[i]); | "Error: host %s ask %d got %d", hosts->sl_str[i], | ||||
c++; | ask[i], got[i]); | ||||
c += ask[i] != got[i]; | |||||
} | } | ||||
} | } | ||||
free(nleft); | free(threads); | ||||
free(ask); | free(ask); | ||||
free(got); | free(got); | ||||
sl_free(hosts, 1); | sl_free(hosts, 1); | ||||
return c; | return c; | ||||
} | } | ||||
#define HOSTLIST_FILE "mach" | #define HOSTLIST_FILE "mach" | ||||
▲ Show 20 Lines • Show All 48 Lines • Show Last 20 Lines |
I think if you cared about the error you could probably write this simpler as:
Not sure why you needed the fetchadd for ask[i], and I think the race you are worried about here doesn't matter in terms of reading got[i]?