summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonathan Bradley <jcb@pikum.xyz>2024-11-22 14:32:51 -0500
committerJonathan Bradley <jcb@pikum.xyz>2024-11-22 16:35:43 -0500
commit31275f4cee6f0593fc39fecef04477d4a26e8df4 (patch)
tree5ae3dfc297fb3729ecf74b0e8a52bb7cf3ddc574
parentf23b4ce2dd648174a5df0e259faf7209c5d4c653 (diff)
pkmem.h: test for expected assert failures
-rw-r--r--pk.h.in11
-rw-r--r--pkmem.h14
-rw-r--r--test/pkmem.cpp135
3 files changed, 153 insertions, 7 deletions
diff --git a/pk.h.in b/pk.h.in
index 0115872..e3af304 100644
--- a/pk.h.in
+++ b/pk.h.in
@@ -52,9 +52,14 @@
********************************************************************************
* pkmem.h: def PK_IMPL_MEM before including pk.h to enable ad-hoc.
*
-* A bucketed memory manager. Allows the creation and management of up to a well
-* known number of buckets. While this is thread-safe, the threading safety is
-* a Grug implementation via a single mutex. PRs welcome.
+* A bucketed memory manager. Allows for the creation and management of up to a
+* well-defined number of buckets.
+*
+* Thread safety: Bucket creation and destruction is *not* thread-safe. On the
+* other hand, the "pk_new" and "pk_delete" methods *are* thread-safe, but
+* thread-safety is implemented per-bucket via a single mutex with long-running
+* lock times. PRs for a more performant thread-safe strategy are welcome,
+* complexity and benchmark depending.
*
* The following definitions (shown with defaults) can be overridden:
* PK_DEFAULT_BUCKET_SIZE 256MB (used when bkt is NULL on first call)
diff --git a/pkmem.h b/pkmem.h
index 1a94ba2..4687638 100644
--- a/pkmem.h
+++ b/pkmem.h
@@ -221,9 +221,9 @@ pk_memory_flush()
void
pk_memory_teardown_all()
{
- for (int64_t i = pk_bucket_head - 1; i > 0; --i) {
- if (pk_buckets[i].ptr == nullptr) continue;
- pk_bucket_destroy(&pk_buckets[i]);
+ for (int64_t i = pk_bucket_head; i > 0; --i) {
+ if (pk_buckets[i - 1].ptr == nullptr) continue;
+ pk_bucket_destroy(&pk_buckets[i - 1]);
}
pk_bucket_head = 0;
}
@@ -231,6 +231,7 @@ pk_memory_teardown_all()
static int64_t
pk_bucket_create_inner(int64_t sz, bool transient, const char* description)
{
+ assert(pk_bucket_head < PK_MAX_BUCKET_COUNT && "pkmem.h: reserved bucket count exceeded");
#ifdef PK_MEMORY_DEBUGGER
if (has_init_debug == false) {
has_init_debug = true;
@@ -246,6 +247,7 @@ pk_bucket_create_inner(int64_t sz, bool transient, const char* description)
bkt->lastEmptyBlockIndex = 0;
bkt->maxBlockCount = blockCount < 10 ? 10 : blockCount;
bkt->blocks = (struct pk_memblock*)malloc(sz);
+ mtx_init(&bkt->mtx, mtx_plain);
assert(bkt->blocks != nullptr && "failed to allocate memory");
#if 1
memset(bkt->blocks, 0, sz);
@@ -292,6 +294,7 @@ pk_bucket_destroy(struct pk_membucket* bkt)
bkt->blocks = CAFE_BABE(struct pk_memblock);
bkt->ptr = CAFE_BABE(char);
bkt->transient = false;
+ mtx_destroy(&bkt->mtx);
#ifdef PK_MEMORY_DEBUGGER
for (i = debug_alloc_head; i > -1; --i) {
if (debug_all_allocs[i].bkt == bkt) {
@@ -394,7 +397,10 @@ pk_new_bkt(size_t sz, size_t alignment, struct pk_membucket* bkt)
break;
}
}
- assert(block != nullptr && "memory corruption: failed to find bucket with enough space");
+ if (block == nullptr) {
+ mtx_unlock(&bkt->mtx);
+ assert(block != nullptr && "memory corruption: not enough space in chosen bkt");
+ }
data = block->data + misalignment;
#ifdef PK_MEMORY_DEBUGGER
bool handled = bkt->transient;
diff --git a/test/pkmem.cpp b/test/pkmem.cpp
index cc7cf9c..8dc429a 100644
--- a/test/pkmem.cpp
+++ b/test/pkmem.cpp
@@ -1,10 +1,41 @@
#include "../pkmem.h"
+#include <csetjmp>
+#include <signal.h>
+#include <thread>
+
+static bool expected_exit = false;
+static bool caught = false;
+static jmp_buf jmp_env;
+void
+exit(int code)
+{
+ if (expected_exit) {
+ caught = true;
+ longjmp(jmp_env, 1);
+ } else {
+ _exit(code);
+ }
+}
+// assert() calls abort(), handle
+void
+handle_assert_abort(int sig)
+{
+ if (expected_exit) {
+ caught = true;
+ longjmp(jmp_env, 1);
+ } else {
+ _exit(1);
+ }
+}
+
int main(int argc, char *argv[])
{
+ signal(SIGABRT, handle_assert_abort);
(void)argc;
(void)argv;
+ int i;
// pk_new<T>
{
@@ -12,6 +43,110 @@ int main(int argc, char *argv[])
fprintf(stdout, "some_dang_string: %p\n", some_dang_string);
pk_delete<char>(some_dang_string, 64);
}
+ pk_memory_teardown_all();
+
+ // assert bucket count
+ do
+ {
+ int r;
+ r = setjmp(jmp_env);
+ if (r == 1) {
+ if (expected_exit == true && caught == true) {
+ expected_exit = false;
+ caught = false;
+ PK_LOGV_INF("%s: successfully caught err.\n", __FILE__);
+ fflush(stdout);
+ fflush(stderr);
+ break;
+ } else {
+ goto uncaught_err;
+ }
+ }
+ for (i = 0; i < PK_MAX_BUCKET_COUNT + 1; ++i) {
+ caught = false;
+ expected_exit = true;
+ pk_membucket *bkt = pk_bucket_create("lol", 1024, false);
+ expected_exit = false;
+ (void)bkt;
+ }
+ goto uncaught_err;
+ }
+ while(false);
+ pk_memory_teardown_all();
+
+ // assert no buckets available on pk_new with full memory
+ do
+ {
+ int r;
+ caught = false;
+ expected_exit = false;
+ for (i = 0; i < PK_MAX_BUCKET_COUNT; ++i) {
+ pk_membucket *bkt = pk_bucket_create("lol2", 1024, false);
+ char *asdf = pk_new<char>(768, bkt);
+ (void)asdf;
+ }
+ r = setjmp(jmp_env);
+ if (r == 1) {
+ if (expected_exit == true && caught == true) {
+ expected_exit = false;
+ caught = false;
+ PK_LOGV_INF("%s: successfully caught err.\n", __FILE__);
+ fflush(stdout);
+ fflush(stderr);
+ break;
+ } else {
+ goto uncaught_err;
+ }
+ }
+ caught = false;
+ expected_exit = true;
+ char *asdf = pk_new<char>(768);
+ (void)asdf;
+ expected_exit = false;
+ goto uncaught_err;
+ }
+ while(false);
+ pk_memory_teardown_all();
+
+ // assert no buckets available on pk_new with full memory
+ do
+ {
+ int r;
+ caught = false;
+ expected_exit = false;
+ pk_membucket *bkt = nullptr;
+ for (i = 0; i < PK_MAX_BUCKET_COUNT; ++i) {
+ bkt = pk_bucket_create("lol3", 1024, false);
+ char *asdf = pk_new<char>(768, bkt);
+ (void)asdf;
+ }
+ r = setjmp(jmp_env);
+ if (r == 1) {
+ if (expected_exit == true && caught == true) {
+ expected_exit = false;
+ caught = false;
+ PK_LOGV_INF("%s: successfully caught err.\n", __FILE__);
+ fflush(stdout);
+ fflush(stderr);
+ break;
+ } else {
+ goto uncaught_err;
+ }
+ }
+ caught = false;
+ expected_exit = true;
+ char *asdf = pk_new<char>(768, bkt);
+ (void)asdf;
+ expected_exit = false;
+ goto uncaught_err;
+ }
+ while(false);
+ pk_memory_teardown_all();
return 0;
+uncaught_err:
+ PK_LOGV_ERR("%s: failed to catch err.\n", __FILE__);
+ fflush(stdout);
+ fflush(stderr);
+ return 1;
}