aboutsummaryrefslogtreecommitdiff
path: root/test/unit/buf_writer.c
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2025-02-05 23:20:13 +0000
committerWarner Losh <imp@FreeBSD.org>2025-02-05 23:20:13 +0000
commit48ec896efb0b78141df004eaa21288b84590c9da (patch)
tree33799792fd95c266d472ab1ae51d50ab4f942eb3 /test/unit/buf_writer.c
parentd28d7fbede216494aa3942af042cc084fcd6098a (diff)
jemalloc: Import 5.3.0 54eaed1d8b56b1aa528be3bdd1877e59c56fa90cvendor/jemalloc/5.3.0vendor/jemalloc
Import jemalloc 5.3.0. This import changes how manage the jemalloc vendor branch (which was just started anyway). Starting with 5.3.0, we import a clean tree from the upstream github, removing all the old files that are no longer upstream, or that we've kept around for some reason. We do this because we merge from this raw version of jemalloc into the FreeBSD contrib/jemalloc, then we run autogen stuff, generate all the generated .h files with gmake, then finally remove much of the generated files in contrib/jemalloc using an update script. Sponsored by: Netflix
Diffstat (limited to 'test/unit/buf_writer.c')
-rw-r--r--test/unit/buf_writer.c196
1 files changed, 196 insertions, 0 deletions
diff --git a/test/unit/buf_writer.c b/test/unit/buf_writer.c
new file mode 100644
index 000000000000..d5e63a0e3f78
--- /dev/null
+++ b/test/unit/buf_writer.c
@@ -0,0 +1,196 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/buf_writer.h"
+
+#define TEST_BUF_SIZE 16
+#define UNIT_MAX (TEST_BUF_SIZE * 3)
+
+static size_t test_write_len;
+static char test_buf[TEST_BUF_SIZE];
+static uint64_t arg;
+static uint64_t arg_store;
+
+static void
+test_write_cb(void *cbopaque, const char *s) {
+ size_t prev_test_write_len = test_write_len;
+ test_write_len += strlen(s); /* only increase the length */
+ arg_store = *(uint64_t *)cbopaque; /* only pass along the argument */
+ assert_zu_le(prev_test_write_len, test_write_len,
+ "Test write overflowed");
+}
+
+static void
+test_buf_writer_body(tsdn_t *tsdn, buf_writer_t *buf_writer) {
+ char s[UNIT_MAX + 1];
+ size_t n_unit, remain, i;
+ ssize_t unit;
+
+ assert(buf_writer->buf != NULL);
+ memset(s, 'a', UNIT_MAX);
+ arg = 4; /* Starting value of random argument. */
+ arg_store = arg;
+ for (unit = UNIT_MAX; unit >= 0; --unit) {
+ /* unit keeps decreasing, so strlen(s) is always unit. */
+ s[unit] = '\0';
+ for (n_unit = 1; n_unit <= 3; ++n_unit) {
+ test_write_len = 0;
+ remain = 0;
+ for (i = 1; i <= n_unit; ++i) {
+ arg = prng_lg_range_u64(&arg, 64);
+ buf_writer_cb(buf_writer, s);
+ remain += unit;
+ if (remain > buf_writer->buf_size) {
+ /* Flushes should have happened. */
+ assert_u64_eq(arg_store, arg, "Call "
+ "back argument didn't get through");
+ remain %= buf_writer->buf_size;
+ if (remain == 0) {
+ /* Last flush should be lazy. */
+ remain += buf_writer->buf_size;
+ }
+ }
+ assert_zu_eq(test_write_len + remain, i * unit,
+ "Incorrect length after writing %zu strings"
+ " of length %zu", i, unit);
+ }
+ buf_writer_flush(buf_writer);
+ expect_zu_eq(test_write_len, n_unit * unit,
+ "Incorrect length after flushing at the end of"
+ " writing %zu strings of length %zu", n_unit, unit);
+ }
+ }
+ buf_writer_terminate(tsdn, buf_writer);
+}
+
+TEST_BEGIN(test_buf_write_static) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ test_buf, TEST_BUF_SIZE),
+ "buf_writer_init() should not encounter error on static buffer");
+ test_buf_writer_body(tsdn, &buf_writer);
+}
+TEST_END
+
+TEST_BEGIN(test_buf_write_dynamic) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ NULL, TEST_BUF_SIZE), "buf_writer_init() should not OOM");
+ test_buf_writer_body(tsdn, &buf_writer);
+}
+TEST_END
+
+TEST_BEGIN(test_buf_write_oom) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM");
+ assert(buf_writer.buf == NULL);
+
+ char s[UNIT_MAX + 1];
+ size_t n_unit, i;
+ ssize_t unit;
+
+ memset(s, 'a', UNIT_MAX);
+ arg = 4; /* Starting value of random argument. */
+ arg_store = arg;
+ for (unit = UNIT_MAX; unit >= 0; unit -= UNIT_MAX / 4) {
+ /* unit keeps decreasing, so strlen(s) is always unit. */
+ s[unit] = '\0';
+ for (n_unit = 1; n_unit <= 3; ++n_unit) {
+ test_write_len = 0;
+ for (i = 1; i <= n_unit; ++i) {
+ arg = prng_lg_range_u64(&arg, 64);
+ buf_writer_cb(&buf_writer, s);
+ assert_u64_eq(arg_store, arg,
+ "Call back argument didn't get through");
+ assert_zu_eq(test_write_len, i * unit,
+ "Incorrect length after writing %zu strings"
+ " of length %zu", i, unit);
+ }
+ buf_writer_flush(&buf_writer);
+ expect_zu_eq(test_write_len, n_unit * unit,
+ "Incorrect length after flushing at the end of"
+ " writing %zu strings of length %zu", n_unit, unit);
+ }
+ }
+ buf_writer_terminate(tsdn, &buf_writer);
+}
+TEST_END
+
+static int test_read_count;
+static size_t test_read_len;
+static uint64_t arg_sum;
+
+ssize_t
+test_read_cb(void *cbopaque, void *buf, size_t limit) {
+ static uint64_t rand = 4;
+
+ arg_sum += *(uint64_t *)cbopaque;
+ assert_zu_gt(limit, 0, "Limit for read_cb must be positive");
+ --test_read_count;
+ if (test_read_count == 0) {
+ return -1;
+ } else {
+ size_t read_len = limit;
+ if (limit > 1) {
+ rand = prng_range_u64(&rand, (uint64_t)limit);
+ read_len -= (size_t)rand;
+ }
+ assert(read_len > 0);
+ memset(buf, 'a', read_len);
+ size_t prev_test_read_len = test_read_len;
+ test_read_len += read_len;
+ assert_zu_le(prev_test_read_len, test_read_len,
+ "Test read overflowed");
+ return read_len;
+ }
+}
+
+static void
+test_buf_writer_pipe_body(tsdn_t *tsdn, buf_writer_t *buf_writer) {
+ arg = 4; /* Starting value of random argument. */
+ for (int count = 5; count > 0; --count) {
+ arg = prng_lg_range_u64(&arg, 64);
+ arg_sum = 0;
+ test_read_count = count;
+ test_read_len = 0;
+ test_write_len = 0;
+ buf_writer_pipe(buf_writer, test_read_cb, &arg);
+ assert(test_read_count == 0);
+ expect_u64_eq(arg_sum, arg * count, "");
+ expect_zu_eq(test_write_len, test_read_len,
+ "Write length should be equal to read length");
+ }
+ buf_writer_terminate(tsdn, buf_writer);
+}
+
+TEST_BEGIN(test_buf_write_pipe) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ test_buf, TEST_BUF_SIZE),
+ "buf_writer_init() should not encounter error on static buffer");
+ test_buf_writer_pipe_body(tsdn, &buf_writer);
+}
+TEST_END
+
+TEST_BEGIN(test_buf_write_pipe_oom) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM");
+ test_buf_writer_pipe_body(tsdn, &buf_writer);
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_buf_write_static,
+ test_buf_write_dynamic,
+ test_buf_write_oom,
+ test_buf_write_pipe,
+ test_buf_write_pipe_oom);
+}