summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-02-28 12:31:11 -0500
committerTavian Barnes <tavianator@tavianator.com>2024-02-28 22:11:55 -0500
commitf0418655db6a344afd5c26efd04a4e4d87128233 (patch)
treefb171b019ebc95d3f71466476f4f10703cd582b2
parent39ce13d45c5881d84e2de98243052811e3648224 (diff)
downloadbfs-f0418655db6a344afd5c26efd04a4e4d87128233.tar.xz
tests/ioq: New unit test
-rw-r--r--GNUmakefile1
-rw-r--r--tests/ioq.c73
-rw-r--r--tests/main.c1
-rw-r--r--tests/tests.h3
4 files changed, 78 insertions, 0 deletions
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 <tavianator@tavianator.com>
+// SPDX-License-Identifier: 0BSD
+
+#include "tests.h"
+#include "../src/ioq.h"
+#include "../src/bfstd.h"
+#include "../src/diag.h"
+#include "../src/dir.h"
+#include <errno.h>
+#include <fcntl.h>
+
+/**
+ * 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);