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 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