From f0418655db6a344afd5c26efd04a4e4d87128233 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 28 Feb 2024 12:31:11 -0500 Subject: tests/ioq: New unit test --- GNUmakefile | 1 + tests/ioq.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/main.c | 1 + tests/tests.h | 3 +++ 4 files changed, 78 insertions(+) create mode 100644 tests/ioq.c diff --git a/GNUmakefile b/GNUmakefile index b0314d8..852621e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -283,6 +283,7 @@ $(BIN)/tests/units: \ $(OBJ)/tests/alloc.o \ $(OBJ)/tests/bfstd.o \ $(OBJ)/tests/bit.o \ + $(OBJ)/tests/ioq.o \ $(OBJ)/tests/main.o \ $(OBJ)/tests/trie.o \ $(OBJ)/tests/xtime.o \ diff --git a/tests/ioq.c b/tests/ioq.c new file mode 100644 index 0000000..3d35650 --- /dev/null +++ b/tests/ioq.c @@ -0,0 +1,73 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "tests.h" +#include "../src/ioq.h" +#include "../src/bfstd.h" +#include "../src/diag.h" +#include "../src/dir.h" +#include +#include + +/** + * Test for blocking within ioq_slot_push(). + * + * struct ioqq only supports non-blocking reads; if a write encounters a full + * slot, it must block until someone pops from that slot: + * + * Reader Writer + * ────────────────────────── ───────────────────────── + * tail: 0 → 1 + * slots[0]: empty → full + * tail: 1 → 0 + * slots[1]: empty → full + * tail: 0 → 1 + * slots[0]: full → full* (IOQ_BLOCKED) + * ioq_slot_wait() ... + * head: 0 → 1 + * slots[0]: full* → empty + * ioq_slot_wake() + * ... + * slots[0]: empty → full + * + * To reproduce this unlikely scenario, we must fill up the ready queue, then + * call ioq_cancel() which pushes an additional sentinel IOQ_STOP operation. + */ +static void check_ioq_push_block(void) { + // Must be a power of two to fill the entire queue + const size_t depth = 2; + + struct ioq *ioq = ioq_create(depth, 1); + bfs_verify(ioq, "ioq_create(): %s", xstrerror(errno)); + + // Push enough operations to fill the queue + for (size_t i = 0; i < depth; ++i) { + struct bfs_dir *dir = bfs_allocdir(); + bfs_verify(dir, "bfs_allocdir(): %s", xstrerror(errno)); + + int ret = ioq_opendir(ioq, dir, AT_FDCWD, ".", 0, NULL); + bfs_verify(ret == 0, "ioq_opendir(): %s", xstrerror(errno)); + } + bfs_verify(ioq_capacity(ioq) == 0); + + // Now cancel the queue, pushing an additional IOQ_STOP message + ioq_cancel(ioq); + + // Drain the queue + struct ioq_ent *ent; + while ((ent = ioq_pop(ioq, true))) { + bfs_verify(ent->op == IOQ_OPENDIR); + if (ent->result >= 0) { + bfs_closedir(ent->opendir.dir); + } + free(ent->opendir.dir); + ioq_free(ioq, ent); + } + + ioq_destroy(ioq); +} + +bool check_ioq(void) { + check_ioq_push_block(); + return true; +} diff --git a/tests/main.c b/tests/main.c index b7292b4..e69fe35 100644 --- a/tests/main.c +++ b/tests/main.c @@ -98,6 +98,7 @@ int main(int argc, char *argv[]) { run_test(&ctx, "alloc", check_alloc); run_test(&ctx, "bfstd", check_bfstd); run_test(&ctx, "bit", check_bit); + run_test(&ctx, "ioq", check_ioq); run_test(&ctx, "trie", check_trie); run_test(&ctx, "xtime", check_xtime); diff --git a/tests/tests.h b/tests/tests.h index d2f3611..d8adbb9 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -22,6 +22,9 @@ bool check_bfstd(void); /** Bit manipulation tests. */ bool check_bit(void); +/** I/O queue tests. */ +bool check_ioq(void); + /** Trie tests. */ bool check_trie(void); -- cgit v1.2.3