summaryrefslogtreecommitdiffstats
path: root/tests/ioq.c
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 /tests/ioq.c
parent39ce13d45c5881d84e2de98243052811e3648224 (diff)
downloadbfs-f0418655db6a344afd5c26efd04a4e4d87128233.tar.xz
tests/ioq: New unit test
Diffstat (limited to 'tests/ioq.c')
-rw-r--r--tests/ioq.c73
1 files changed, 73 insertions, 0 deletions
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;
+}