Rusty Russell
6 years ago
19 changed files with 1152 additions and 72 deletions
@ -1,3 +1,3 @@ |
|||
CCAN imported from http://ccodearchive.net. |
|||
|
|||
CCAN version: init-2458-g8cc0749a |
|||
CCAN version: init-2470-g2b3517d4 |
|||
|
@ -0,0 +1,70 @@ |
|||
/* Include the C files directly. */ |
|||
#include <ccan/htable/htable.h> |
|||
#include <ccan/htable/htable.c> |
|||
#include <ccan/tap/tap.h> |
|||
#include <stdbool.h> |
|||
#include <string.h> |
|||
|
|||
struct htable_with_counters { |
|||
struct htable ht; |
|||
size_t num_alloc, num_free; |
|||
}; |
|||
|
|||
static void *test_alloc(struct htable *ht, size_t len) |
|||
{ |
|||
((struct htable_with_counters *)ht)->num_alloc++; |
|||
return calloc(len, 1); |
|||
} |
|||
|
|||
|
|||
static void test_free(struct htable *ht, void *p) |
|||
{ |
|||
if (p) { |
|||
((struct htable_with_counters *)ht)->num_free++; |
|||
free(p); |
|||
} |
|||
} |
|||
|
|||
static size_t hash(const void *elem, void *unused UNNEEDED) |
|||
{ |
|||
return *(size_t *)elem; |
|||
} |
|||
|
|||
int main(void) |
|||
{ |
|||
struct htable_with_counters htc; |
|||
size_t val[] = { 0, 1 }; |
|||
|
|||
htc.num_alloc = htc.num_free = 0; |
|||
plan_tests(12); |
|||
|
|||
htable_set_allocator(test_alloc, test_free); |
|||
htable_init(&htc.ht, hash, NULL); |
|||
htable_add(&htc.ht, hash(&val[0], NULL), &val[0]); |
|||
ok1(htc.num_alloc == 1); |
|||
ok1(htc.num_free == 0); |
|||
/* Adding another increments, then frees old */ |
|||
htable_add(&htc.ht, hash(&val[1], NULL), &val[1]); |
|||
ok1(htc.num_alloc == 2); |
|||
ok1(htc.num_free == 1); |
|||
htable_clear(&htc.ht); |
|||
ok1(htc.num_alloc == 2); |
|||
ok1(htc.num_free == 2); |
|||
|
|||
/* Should restore defaults */ |
|||
htable_set_allocator(NULL, NULL); |
|||
ok1(htable_alloc == htable_default_alloc); |
|||
ok1(htable_free == htable_default_free); |
|||
|
|||
htable_init(&htc.ht, hash, NULL); |
|||
htable_add(&htc.ht, hash(&val[0], NULL), &val[0]); |
|||
ok1(htc.num_alloc == 2); |
|||
ok1(htc.num_free == 2); |
|||
htable_add(&htc.ht, hash(&val[1], NULL), &val[1]); |
|||
ok1(htc.num_alloc == 2); |
|||
ok1(htc.num_free == 2); |
|||
htable_clear(&htc.ht); |
|||
|
|||
/* This exits depending on whether all tests passed */ |
|||
return exit_status(); |
|||
} |
@ -0,0 +1,223 @@ |
|||
#define CCAN_HTABLE_DEBUG |
|||
#include <ccan/htable/htable.h> |
|||
#include <ccan/htable/htable.c> |
|||
#include <ccan/tap/tap.h> |
|||
#include <stdbool.h> |
|||
#include <string.h> |
|||
|
|||
#define NUM_BITS 7 |
|||
#define NUM_VALS (1 << NUM_BITS) |
|||
|
|||
static void *bad_pointer; |
|||
|
|||
/* We use the number divided by two as the hash (for lots of
|
|||
collisions), plus set all the higher bits so we can detect if they |
|||
don't get masked out. */ |
|||
static size_t hash(const void *elem, void *unused UNNEEDED) |
|||
{ |
|||
size_t h; |
|||
|
|||
/* With CCAN_HTABLE_DEBUG enabled, it will try to hash each element,
|
|||
* including this one... */ |
|||
if (elem == bad_pointer) |
|||
return 0; |
|||
|
|||
h = *(uint64_t *)elem / 2; |
|||
h |= -1UL << NUM_BITS; |
|||
return h; |
|||
} |
|||
|
|||
static bool objcmp(const void *htelem, void *cmpdata) |
|||
{ |
|||
return *(uint64_t *)htelem == *(uint64_t *)cmpdata; |
|||
} |
|||
|
|||
static void add_vals(struct htable *ht, |
|||
const uint64_t val[], |
|||
unsigned int off, unsigned int num) |
|||
{ |
|||
uint64_t i; |
|||
|
|||
for (i = off; i < off+num; i++) { |
|||
if (htable_get(ht, hash(&i, NULL), objcmp, &i)) { |
|||
fail("%llu already in hash", (long long)i); |
|||
return; |
|||
} |
|||
htable_add(ht, hash(&val[i], NULL), &val[i]); |
|||
if (htable_get(ht, hash(&i, NULL), objcmp, &i) != &val[i]) { |
|||
fail("%llu not added to hash", (long long)i); |
|||
return; |
|||
} |
|||
} |
|||
pass("Added %llu numbers to hash", (long long)i); |
|||
} |
|||
|
|||
#if 0 |
|||
static void refill_vals(struct htable *ht, |
|||
const uint64_t val[], unsigned int num) |
|||
{ |
|||
uint64_t i; |
|||
|
|||
for (i = 0; i < num; i++) { |
|||
if (htable_get(ht, hash(&i, NULL), objcmp, &i)) |
|||
continue; |
|||
htable_add(ht, hash(&val[i], NULL), &val[i]); |
|||
} |
|||
} |
|||
#endif |
|||
|
|||
static void find_vals(struct htable *ht, |
|||
const uint64_t val[], unsigned int num) |
|||
{ |
|||
uint64_t i; |
|||
|
|||
for (i = 0; i < num; i++) { |
|||
if (htable_get(ht, hash(&i, NULL), objcmp, &i) != &val[i]) { |
|||
fail("%llu not found in hash", (long long)i); |
|||
return; |
|||
} |
|||
} |
|||
pass("Found %llu numbers in hash", (long long)i); |
|||
} |
|||
|
|||
static void del_vals(struct htable *ht, |
|||
const uint64_t val[], unsigned int num) |
|||
{ |
|||
uint64_t i; |
|||
|
|||
for (i = 0; i < num; i++) { |
|||
if (!htable_del(ht, hash(&val[i], NULL), &val[i])) { |
|||
fail("%llu not deleted from hash", (long long)i); |
|||
return; |
|||
} |
|||
} |
|||
pass("Deleted %llu numbers in hash", (long long)i); |
|||
} |
|||
|
|||
static bool check_mask(struct htable *ht, uint64_t val[], unsigned num) |
|||
{ |
|||
uint64_t i; |
|||
|
|||
for (i = 0; i < num; i++) { |
|||
if (((uintptr_t)&val[i] & ht->common_mask) != ht->common_bits) |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
int main(void) |
|||
{ |
|||
unsigned int i, weight; |
|||
uintptr_t perfect_bit; |
|||
struct htable ht; |
|||
uint64_t val[NUM_VALS]; |
|||
uint64_t dne; |
|||
void *p; |
|||
struct htable_iter iter; |
|||
|
|||
plan_tests(36); |
|||
for (i = 0; i < NUM_VALS; i++) |
|||
val[i] = i; |
|||
dne = i; |
|||
|
|||
htable_init(&ht, hash, NULL); |
|||
ok1(ht.max == 0); |
|||
ok1(ht.bits == 0); |
|||
|
|||
/* We cannot find an entry which doesn't exist. */ |
|||
ok1(!htable_get(&ht, hash(&dne, NULL), objcmp, &dne)); |
|||
|
|||
/* This should increase it once. */ |
|||
add_vals(&ht, val, 0, 1); |
|||
ok1(ht.bits == 1); |
|||
ok1(ht.max == 1); |
|||
weight = 0; |
|||
for (i = 0; i < sizeof(ht.common_mask) * CHAR_BIT; i++) { |
|||
if (ht.common_mask & ((uintptr_t)1 << i)) { |
|||
weight++; |
|||
} |
|||
} |
|||
/* Only one bit should be clear. */ |
|||
ok1(weight == i-1); |
|||
|
|||
/* Mask should be set. */ |
|||
ok1(check_mask(&ht, val, 1)); |
|||
|
|||
/* This should increase it again. */ |
|||
add_vals(&ht, val, 1, 1); |
|||
ok1(ht.bits == 2); |
|||
ok1(ht.max == 3); |
|||
|
|||
/* Mask should be set. */ |
|||
ok1(ht.common_mask != 0); |
|||
ok1(ht.common_mask != -1); |
|||
ok1(check_mask(&ht, val, 2)); |
|||
|
|||
/* Now do the rest. */ |
|||
add_vals(&ht, val, 2, NUM_VALS - 2); |
|||
|
|||
/* Find all. */ |
|||
find_vals(&ht, val, NUM_VALS); |
|||
ok1(!htable_get(&ht, hash(&dne, NULL), objcmp, &dne)); |
|||
|
|||
/* Walk once, should get them all. */ |
|||
i = 0; |
|||
for (p = htable_first(&ht,&iter); p; p = htable_next(&ht, &iter)) |
|||
i++; |
|||
ok1(i == NUM_VALS); |
|||
|
|||
i = 0; |
|||
for (p = htable_prev(&ht, &iter); p; p = htable_prev(&ht, &iter)) |
|||
i++; |
|||
ok1(i == NUM_VALS); |
|||
|
|||
/* Delete all. */ |
|||
del_vals(&ht, val, NUM_VALS); |
|||
ok1(!htable_get(&ht, hash(&val[0], NULL), objcmp, &val[0])); |
|||
|
|||
/* Worst case, a "pointer" which doesn't have any matching bits. */ |
|||
bad_pointer = (void *)~(uintptr_t)&val[NUM_VALS-1]; |
|||
htable_add(&ht, 0, bad_pointer); |
|||
htable_add(&ht, hash(&val[NUM_VALS-1], NULL), &val[NUM_VALS-1]); |
|||
ok1(ht.common_mask == 0); |
|||
ok1(ht.common_bits == 0); |
|||
/* Get rid of bogus pointer before we trip over it! */ |
|||
htable_del(&ht, 0, bad_pointer); |
|||
|
|||
/* Add the rest. */ |
|||
add_vals(&ht, val, 0, NUM_VALS-1); |
|||
|
|||
/* Check we can find them all. */ |
|||
find_vals(&ht, val, NUM_VALS); |
|||
ok1(!htable_get(&ht, hash(&dne, NULL), objcmp, &dne)); |
|||
|
|||
/* Corner cases: wipe out the perfect bit using bogus pointer. */ |
|||
htable_clear(&ht); |
|||
htable_add(&ht, hash(&val[NUM_VALS-1], NULL), &val[NUM_VALS-1]); |
|||
ok1(ht.perfect_bit); |
|||
perfect_bit = ht.perfect_bit; |
|||
bad_pointer = (void *)((uintptr_t)&val[NUM_VALS-1] | perfect_bit); |
|||
htable_add(&ht, 0, bad_pointer); |
|||
ok1(ht.perfect_bit == 0); |
|||
htable_del(&ht, 0, bad_pointer); |
|||
|
|||
/* Enlarging should restore it... */ |
|||
add_vals(&ht, val, 0, NUM_VALS-1); |
|||
|
|||
ok1(ht.perfect_bit != 0); |
|||
htable_clear(&ht); |
|||
|
|||
ok1(htable_init_sized(&ht, hash, NULL, 1024)); |
|||
ok1(ht.max >= 1024); |
|||
htable_clear(&ht); |
|||
|
|||
ok1(htable_init_sized(&ht, hash, NULL, 1023)); |
|||
ok1(ht.max >= 1023); |
|||
htable_clear(&ht); |
|||
|
|||
ok1(htable_init_sized(&ht, hash, NULL, 1025)); |
|||
ok1(ht.max >= 1025); |
|||
htable_clear(&ht); |
|||
|
|||
return exit_status(); |
|||
} |
@ -0,0 +1,51 @@ |
|||
#include "../htable.c" |
|||
|
|||
static size_t hash(const void *ptr, void *priv UNNEEDED) |
|||
{ |
|||
/* We're hashing pointers; no need to get too fancy. */ |
|||
return ((size_t)ptr / sizeof(ptr)) ^ ((size_t)ptr % sizeof(ptr)); |
|||
} |
|||
|
|||
/* 24042: Waiting on 0x5570a500c3f8 (11742786623615)
|
|||
24042: Waiting on 0x5570a500c430 (11742786623622) |
|||
24042: Searching for 0x5570a500c3f8 (11742786623615) in 2 elems |
|||
24042: Searching for 0x5570a500c3f8 (11742786623615) in 2 elems |
|||
*/ |
|||
static struct htable waittable = HTABLE_INITIALIZER(waittable, hash, NULL); |
|||
|
|||
int main(void) |
|||
{ |
|||
const void *p1 = (void *)0x5570a500c3f8ULL; |
|||
const void *p2 = (void *)0x5570a500c430ULL; |
|||
size_t h; |
|||
struct htable_iter i; |
|||
void *p; |
|||
bool found; |
|||
|
|||
printf("hash %p == %zu\n", p1, hash(p1, NULL)); |
|||
printf("hash %p == %zu\n", p2, hash(p2, NULL)); |
|||
htable_add(&waittable, hash(p1, NULL), p1); |
|||
htable_add(&waittable, hash(p2, NULL), p2); |
|||
|
|||
found = false; |
|||
h = hash(p1, NULL); |
|||
for (p = htable_firstval(&waittable, &i, h); |
|||
p; |
|||
p = htable_nextval(&waittable, &i, h)) { |
|||
if (p == p1) |
|||
found = true; |
|||
} |
|||
assert(found); |
|||
|
|||
found = false; |
|||
h = hash(p2, NULL); |
|||
for (p = htable_firstval(&waittable, &i, h); |
|||
p; |
|||
p = htable_nextval(&waittable, &i, h)) { |
|||
if (p == p2) |
|||
found = true; |
|||
} |
|||
assert(found); |
|||
|
|||
return found ? 0 : 1; |
|||
} |
@ -0,0 +1,142 @@ |
|||
#include <ccan/io/io.h> |
|||
/* Include the C files directly. */ |
|||
#include <ccan/io/poll.c> |
|||
#include <ccan/io/io.c> |
|||
#include <ccan/tap/tap.h> |
|||
#include <sys/wait.h> |
|||
#include <stdio.h> |
|||
|
|||
#define PORT "65046" |
|||
|
|||
struct data { |
|||
struct io_listener *l; |
|||
int num_clients; |
|||
char *pattern; |
|||
char buf[30]; |
|||
size_t buflen; |
|||
}; |
|||
|
|||
static struct io_plan *read_more(struct io_conn *conn, struct data *d); |
|||
|
|||
static struct io_plan *read_done(struct io_conn *conn, struct data *d) |
|||
{ |
|||
tal_resize(&d->pattern, tal_count(d->pattern) + strlen(d->buf)); |
|||
strcat(d->pattern, d->buf); |
|||
return read_more(conn, d); |
|||
} |
|||
|
|||
static struct io_plan *read_more(struct io_conn *conn, struct data *d) |
|||
{ |
|||
memset(d->buf, 0, sizeof(d->buf)); |
|||
return io_read_partial(conn, d->buf, sizeof(d->buf), &d->buflen, |
|||
read_done, d); |
|||
} |
|||
|
|||
|
|||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d) |
|||
{ |
|||
d->num_clients++; |
|||
if (d->num_clients == 2) { |
|||
/* Free listener so when conns close we exit io_loop */ |
|||
io_close_listener(d->l); |
|||
/* Set priority to second connection. */ |
|||
ok1(io_conn_exclusive(conn, true) == true); |
|||
} |
|||
return read_more(conn, d); |
|||
} |
|||
|
|||
|
|||
static int make_listen_fd(const char *port, struct addrinfo **info) |
|||
{ |
|||
int fd, on = 1; |
|||
struct addrinfo *addrinfo, hints; |
|||
|
|||
memset(&hints, 0, sizeof(hints)); |
|||
hints.ai_family = AF_UNSPEC; |
|||
hints.ai_socktype = SOCK_STREAM; |
|||
hints.ai_flags = AI_PASSIVE; |
|||
hints.ai_protocol = 0; |
|||
|
|||
if (getaddrinfo(NULL, port, &hints, &addrinfo) != 0) |
|||
return -1; |
|||
|
|||
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, |
|||
addrinfo->ai_protocol); |
|||
if (fd < 0) |
|||
return -1; |
|||
|
|||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
|||
if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) { |
|||
close(fd); |
|||
return -1; |
|||
} |
|||
if (listen(fd, 1) != 0) { |
|||
close(fd); |
|||
return -1; |
|||
} |
|||
*info = addrinfo; |
|||
return fd; |
|||
} |
|||
|
|||
int main(void) |
|||
{ |
|||
struct addrinfo *addrinfo = NULL; |
|||
int fd, status; |
|||
struct data d; |
|||
|
|||
d.num_clients = 0; |
|||
|
|||
/* This is how many tests you plan to run */ |
|||
plan_tests(8); |
|||
fd = make_listen_fd(PORT, &addrinfo); |
|||
ok1(fd >= 0); |
|||
d.l = io_new_listener(NULL, fd, init_conn, &d); |
|||
ok1(d.l); |
|||
fflush(stdout); |
|||
|
|||
if (!fork()) { |
|||
int fd1, fd2; |
|||
|
|||
io_close_listener(d.l); |
|||
fd1 = socket(addrinfo->ai_family, addrinfo->ai_socktype, |
|||
addrinfo->ai_protocol); |
|||
if (fd1 < 0) |
|||
exit(1); |
|||
if (connect(fd1, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) |
|||
exit(2); |
|||
if (write(fd1, "1hellothere", strlen("1hellothere")) != strlen("1hellothere")) |
|||
exit(3); |
|||
fd2 = socket(addrinfo->ai_family, addrinfo->ai_socktype, |
|||
addrinfo->ai_protocol); |
|||
if (fd2 < 0) |
|||
exit(1); |
|||
if (connect(fd2, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) |
|||
exit(2); |
|||
signal(SIGPIPE, SIG_IGN); |
|||
|
|||
sleep(1); |
|||
if (write(fd1, "1helloagain", strlen("1helloagain")) != strlen("1helloagain")) |
|||
exit(4); |
|||
sleep(1); |
|||
if (write(fd2, "2hellonew", strlen("2hellonew")) != strlen("2hellonew")) |
|||
exit(5); |
|||
close(fd1); |
|||
close(fd2); |
|||
freeaddrinfo(addrinfo); |
|||
exit(0); |
|||
} |
|||
freeaddrinfo(addrinfo); |
|||
|
|||
d.pattern = tal_arrz(NULL, char, 1); |
|||
ok1(io_loop(NULL, NULL) == NULL); |
|||
if (!ok1(strcmp(d.pattern, "1hellothere2hellonew1helloagain") == 0)) |
|||
printf("d.patterns = %s\n", d.pattern); |
|||
tal_free(d.pattern); |
|||
|
|||
ok1(wait(&status)); |
|||
ok1(WIFEXITED(status)); |
|||
ok1(WEXITSTATUS(status) == 0); |
|||
|
|||
/* This exits depending on whether all tests passed */ |
|||
return exit_status(); |
|||
} |
@ -0,0 +1,144 @@ |
|||
#include <ccan/io/io.h> |
|||
/* Include the C files directly. */ |
|||
#include <ccan/io/poll.c> |
|||
#include <ccan/io/io.c> |
|||
#include <ccan/tap/tap.h> |
|||
#include <sys/wait.h> |
|||
#include <stdio.h> |
|||
|
|||
#define PORT "65047" |
|||
|
|||
struct data { |
|||
struct io_listener *l; |
|||
char *pattern; |
|||
char buf[30]; |
|||
size_t buflen; |
|||
}; |
|||
|
|||
static struct io_plan *read_more(struct io_conn *conn, struct data *d); |
|||
static struct io_plan *write_more(struct io_conn *conn, struct data *d); |
|||
|
|||
static struct io_plan *read_done(struct io_conn *conn, struct data *d) |
|||
{ |
|||
tal_resize(&d->pattern, tal_count(d->pattern) + 1 + strlen(d->buf)); |
|||
strcat(d->pattern, "<"); |
|||
strcat(d->pattern, d->buf); |
|||
return read_more(conn, d); |
|||
} |
|||
|
|||
static struct io_plan *read_more(struct io_conn *conn, struct data *d) |
|||
{ |
|||
memset(d->buf, 0, sizeof(d->buf)); |
|||
return io_read_partial(conn, d->buf, sizeof(d->buf), &d->buflen, |
|||
read_done, d); |
|||
} |
|||
|
|||
static struct io_plan *write_done(struct io_conn *conn, struct data *d) |
|||
{ |
|||
tal_resize(&d->pattern, tal_count(d->pattern) + 1); |
|||
strcat(d->pattern, ">"); |
|||
return write_more(conn, d); |
|||
} |
|||
|
|||
static struct io_plan *write_more(struct io_conn *conn, struct data *d) |
|||
{ |
|||
return io_write_partial(conn, d->buf, 1, &d->buflen, |
|||
write_done, d); |
|||
} |
|||
|
|||
static struct io_plan *read_priority_init(struct io_conn *conn, struct data *d) |
|||
{ |
|||
/* This should suppress the write */ |
|||
ok1(io_conn_exclusive(conn, true)); |
|||
return read_more(conn, d); |
|||
} |
|||
|
|||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d) |
|||
{ |
|||
/* Free listener so when conns close we exit io_loop */ |
|||
io_close_listener(d->l); |
|||
|
|||
return io_duplex(conn, read_priority_init(conn, d), write_more(conn, d)); |
|||
} |
|||
|
|||
|
|||
static int make_listen_fd(const char *port, struct addrinfo **info) |
|||
{ |
|||
int fd, on = 1; |
|||
struct addrinfo *addrinfo, hints; |
|||
|
|||
memset(&hints, 0, sizeof(hints)); |
|||
hints.ai_family = AF_UNSPEC; |
|||
hints.ai_socktype = SOCK_STREAM; |
|||
hints.ai_flags = AI_PASSIVE; |
|||
hints.ai_protocol = 0; |
|||
|
|||
if (getaddrinfo(NULL, port, &hints, &addrinfo) != 0) |
|||
return -1; |
|||
|
|||
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, |
|||
addrinfo->ai_protocol); |
|||
if (fd < 0) |
|||
return -1; |
|||
|
|||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
|||
if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) { |
|||
close(fd); |
|||
return -1; |
|||
} |
|||
if (listen(fd, 1) != 0) { |
|||
close(fd); |
|||
return -1; |
|||
} |
|||
*info = addrinfo; |
|||
return fd; |
|||
} |
|||
|
|||
int main(void) |
|||
{ |
|||
struct addrinfo *addrinfo = NULL; |
|||
int fd, status; |
|||
struct data d; |
|||
|
|||
/* This is how many tests you plan to run */ |
|||
plan_tests(8); |
|||
fd = make_listen_fd(PORT, &addrinfo); |
|||
ok1(fd >= 0); |
|||
d.l = io_new_listener(NULL, fd, init_conn, &d); |
|||
ok1(d.l); |
|||
fflush(stdout); |
|||
|
|||
if (!fork()) { |
|||
io_close_listener(d.l); |
|||
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, |
|||
addrinfo->ai_protocol); |
|||
if (fd < 0) |
|||
exit(1); |
|||
if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) |
|||
exit(2); |
|||
signal(SIGPIPE, SIG_IGN); |
|||
|
|||
if (write(fd, "1hellothere", strlen("1hellothere")) != strlen("1hellothere")) |
|||
exit(3); |
|||
sleep(1); |
|||
if (write(fd, "1helloagain", strlen("1helloagain")) != strlen("1helloagain")) |
|||
exit(4); |
|||
close(fd); |
|||
freeaddrinfo(addrinfo); |
|||
exit(0); |
|||
} |
|||
freeaddrinfo(addrinfo); |
|||
|
|||
d.pattern = tal_arrz(NULL, char, 1); |
|||
ok1(io_loop(NULL, NULL) == NULL); |
|||
/* No trace of writes */ |
|||
ok1(strcmp(d.pattern, "<1hellothere<1helloagain") == 0); |
|||
tal_free(d.pattern); |
|||
|
|||
ok1(wait(&status)); |
|||
ok1(WIFEXITED(status)); |
|||
ok1(WEXITSTATUS(status) == 0); |
|||
|
|||
/* This exits depending on whether all tests passed */ |
|||
return exit_status(); |
|||
} |
@ -0,0 +1,144 @@ |
|||
#include <ccan/io/io.h> |
|||
/* Include the C files directly. */ |
|||
#include <ccan/io/poll.c> |
|||
#include <ccan/io/io.c> |
|||
#include <ccan/tap/tap.h> |
|||
#include <sys/wait.h> |
|||
#include <stdio.h> |
|||
|
|||
#define PORT "65048" |
|||
|
|||
struct data { |
|||
struct io_listener *l; |
|||
char *pattern; |
|||
char buf[30]; |
|||
size_t buflen; |
|||
}; |
|||
|
|||
static struct io_plan *read_more(struct io_conn *conn, struct data *d); |
|||
static struct io_plan *write_more(struct io_conn *conn, struct data *d); |
|||
|
|||
static struct io_plan *read_done(struct io_conn *conn, struct data *d) |
|||
{ |
|||
tal_resize(&d->pattern, tal_count(d->pattern) + 1 + strlen(d->buf)); |
|||
strcat(d->pattern, "<"); |
|||
strcat(d->pattern, d->buf); |
|||
return read_more(conn, d); |
|||
} |
|||
|
|||
static struct io_plan *read_more(struct io_conn *conn, struct data *d) |
|||
{ |
|||
memset(d->buf, 0, sizeof(d->buf)); |
|||
return io_read_partial(conn, d->buf, sizeof(d->buf), &d->buflen, |
|||
read_done, d); |
|||
} |
|||
|
|||
static struct io_plan *write_done(struct io_conn *conn, struct data *d) |
|||
{ |
|||
tal_resize(&d->pattern, tal_count(d->pattern) + 1); |
|||
strcat(d->pattern, ">"); |
|||
return write_more(conn, d); |
|||
} |
|||
|
|||
static struct io_plan *write_more(struct io_conn *conn, struct data *d) |
|||
{ |
|||
return io_write_partial(conn, d->buf, 1, &d->buflen, |
|||
write_done, d); |
|||
} |
|||
|
|||
static struct io_plan *write_priority_init(struct io_conn *conn, struct data *d) |
|||
{ |
|||
/* This should suppress the read */ |
|||
ok1(io_conn_out_exclusive(conn, true)); |
|||
return write_more(conn, d); |
|||
} |
|||
|
|||
static struct io_plan *init_conn(struct io_conn *conn, struct data *d) |
|||
{ |
|||
/* Free listener so when conns close we exit io_loop */ |
|||
io_close_listener(d->l); |
|||
|
|||
return io_duplex(conn, read_more(conn, d), write_priority_init(conn, d)); |
|||
} |
|||
|
|||
|
|||
static int make_listen_fd(const char *port, struct addrinfo **info) |
|||
{ |
|||
int fd, on = 1; |
|||
struct addrinfo *addrinfo, hints; |
|||
|
|||
memset(&hints, 0, sizeof(hints)); |
|||
hints.ai_family = AF_UNSPEC; |
|||
hints.ai_socktype = SOCK_STREAM; |
|||
hints.ai_flags = AI_PASSIVE; |
|||
hints.ai_protocol = 0; |
|||
|
|||
if (getaddrinfo(NULL, port, &hints, &addrinfo) != 0) |
|||
return -1; |
|||
|
|||
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, |
|||
addrinfo->ai_protocol); |
|||
if (fd < 0) |
|||
return -1; |
|||
|
|||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
|||
if (bind(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) { |
|||
close(fd); |
|||
return -1; |
|||
} |
|||
if (listen(fd, 1) != 0) { |
|||
close(fd); |
|||
return -1; |
|||
} |
|||
*info = addrinfo; |
|||
return fd; |
|||
} |
|||
|
|||
int main(void) |
|||
{ |
|||
struct addrinfo *addrinfo = NULL; |
|||
int fd, status; |
|||
struct data d; |
|||
|
|||
/* This is how many tests you plan to run */ |
|||
plan_tests(8); |
|||
fd = make_listen_fd(PORT, &addrinfo); |
|||
ok1(fd >= 0); |
|||
d.l = io_new_listener(NULL, fd, init_conn, &d); |
|||
ok1(d.l); |
|||
fflush(stdout); |
|||
|
|||
if (!fork()) { |
|||
io_close_listener(d.l); |
|||
fd = socket(addrinfo->ai_family, addrinfo->ai_socktype, |
|||
addrinfo->ai_protocol); |
|||
if (fd < 0) |
|||
exit(1); |
|||
if (connect(fd, addrinfo->ai_addr, addrinfo->ai_addrlen) != 0) |
|||
exit(2); |
|||
signal(SIGPIPE, SIG_IGN); |
|||
|
|||
if (write(fd, "1hellothere", strlen("1hellothere")) != strlen("1hellothere")) |
|||
exit(3); |
|||
sleep(1); |
|||
if (write(fd, "1helloagain", strlen("1helloagain")) != strlen("1helloagain")) |
|||
exit(4); |
|||
close(fd); |
|||
freeaddrinfo(addrinfo); |
|||
exit(0); |
|||
} |
|||
freeaddrinfo(addrinfo); |
|||
|
|||
d.pattern = tal_arrz(NULL, char, 1); |
|||
ok1(io_loop(NULL, NULL) == NULL); |
|||
/* No trace of reads */ |
|||
ok1(strspn(d.pattern, ">") == strlen(d.pattern)); |
|||
tal_free(d.pattern); |
|||
|
|||
ok1(wait(&status)); |
|||
ok1(WIFEXITED(status)); |
|||
ok1(WEXITSTATUS(status) == 0); |
|||
|
|||
/* This exits depending on whether all tests passed */ |
|||
return exit_status(); |
|||
} |
@ -0,0 +1,73 @@ |
|||
#define CCAN_TIMER_DEBUG |
|||
/* Include the C files directly. */ |
|||
#include <ccan/timer/timer.c> |
|||
#include <ccan/tap/tap.h> |
|||
|
|||
struct timers_with_counters { |
|||
struct timers timers; |
|||
size_t num_alloc, num_free; |
|||
}; |
|||
|
|||
|
|||
static void *test_alloc(struct timers *timers, size_t len) |
|||
{ |
|||
((struct timers_with_counters *)timers)->num_alloc++; |
|||
return malloc(len); |
|||
} |
|||
|
|||
static void test_free(struct timers *timers, void *p) |
|||
{ |
|||
if (p) { |
|||
((struct timers_with_counters *)timers)->num_free++; |
|||
free(p); |
|||
} |
|||
} |
|||
|
|||
static struct timemono timemono_from_nsec(unsigned long long nsec) |
|||
{ |
|||
struct timemono epoch = { { 0, 0 } }; |
|||
return timemono_add(epoch, time_from_nsec(nsec)); |
|||
} |
|||
|
|||
int main(void) |
|||
{ |
|||
struct timers_with_counters tc; |
|||
struct timer t[64]; |
|||
const struct timemono epoch = { { 0, 0 } }; |
|||
|
|||
tc.num_alloc = tc.num_free = 0; |
|||
plan_tests(12); |
|||
|
|||
timers_set_allocator(test_alloc, test_free); |
|||
timers_init(&tc.timers, epoch); |
|||
timer_init(&t[0]); |
|||
|
|||
timer_addmono(&tc.timers, &t[0], |
|||
timemono_from_nsec(TIMER_GRANULARITY << TIMER_LEVEL_BITS)); |
|||
timers_expire(&tc.timers, timemono_from_nsec(1)); |
|||
ok1(tc.num_alloc == 1); |
|||
ok1(tc.num_free == 0); |
|||
timer_del(&tc.timers, &t[0]); |
|||
ok1(tc.num_alloc == 1); |
|||
ok1(tc.num_free == 0); |
|||
timers_cleanup(&tc.timers); |
|||
ok1(tc.num_alloc == 1); |
|||
ok1(tc.num_free == 1); |
|||
|
|||
/* Should restore defaults */ |
|||
timers_set_allocator(NULL, NULL); |
|||
ok1(timer_alloc == timer_default_alloc); |
|||
ok1(timer_free == timer_default_free); |
|||
|
|||
timers_init(&tc.timers, epoch); |
|||
timer_addmono(&tc.timers, &t[0], |
|||
timemono_from_nsec(TIMER_GRANULARITY << TIMER_LEVEL_BITS)); |
|||
ok1(tc.num_alloc == 1); |
|||
ok1(tc.num_free == 1); |
|||
timers_cleanup(&tc.timers); |
|||
ok1(tc.num_alloc == 1); |
|||
ok1(tc.num_free == 1); |
|||
|
|||
/* This exits depending on whether all tests passed */ |
|||
return exit_status(); |
|||
} |
Loading…
Reference in new issue