#include #include #include #include #include #include "utils.h" #include "xstring.h" /* ------------------------------------------------------------------ */ /* Concurrent-test helpers */ /* ------------------------------------------------------------------ */ #define NTHREADS (8) /* Thread argument for the same-key test */ typedef struct { const char* key; char* result; } same_key_arg_t; static void* thread_make_same_key(void* arg) { same_key_arg_t* a = (same_key_arg_t*)arg; a->result = makeString((char*)a->key); return a->result; } /* Thread argument for the distinct-key test */ typedef struct { char key[16]; char* result; } distinct_key_arg_t; static void* thread_make_distinct_key(void* arg) { distinct_key_arg_t* a = (distinct_key_arg_t*)arg; a->result = makeString(a->key); return a->result; } /* ------------------------------------------------------------------ */ /* Concurrent tests */ /* ------------------------------------------------------------------ */ /* * test_xstring_concurrent_same_key * * Spawn NTHREADS threads, each racing to intern the same key. * With the TOCTOU fix the lock is held from key_find through key_add, * so every thread must receive the identical interned pointer. * Without the fix, two threads could both miss key_find, both call * makeInstance, and one would leak — and the pointers would differ. */ static void test_xstring_concurrent_same_key(void) { pthread_t tids[NTHREADS]; same_key_arg_t args[NTHREADS]; int rc; int i; rc = xstring_table_init(139); TEST_ASSERT_TRUE(rc >= 0); for (i = 0; i < NTHREADS; i++) { args[i].key = "concurrent_key"; args[i].result = NULL; rc = pthread_create(&tids[i], NULL, thread_make_same_key, &args[i]); TEST_ASSERT_TRUE(rc == 0); } for (i = 0; i < NTHREADS; i++) { void* retval = NULL; rc = pthread_join(tids[i], &retval); TEST_ASSERT_TRUE(rc == 0); } /* All threads must have received a non-NULL pointer */ for (i = 0; i < NTHREADS; i++) { TEST_ASSERT_TRUE(args[i].result != NULL); } /* All pointers must be the same interned object */ for (i = 1; i < NTHREADS; i++) { TEST_ASSERT_EQUAL_PTR(args[0].result, args[i].result); } /* * makeString increments the refcount on a cache hit. Each thread * got one reference; drop NTHREADS - 1 extras so xstring_table_fini * can release the final one cleanly. */ for (i = 1; i < NTHREADS; i++) { dereference((var)args[i].result); } xstring_table_fini(); } /* * test_xstring_concurrent_distinct_keys * * Spawn NTHREADS threads, each interning a unique key. * Every returned pointer must be non-NULL and distinct from all others, * confirming that concurrent inserts of different keys do not collide. */ static void test_xstring_concurrent_distinct_keys(void) { pthread_t tids[NTHREADS]; distinct_key_arg_t args[NTHREADS]; int rc; int i; int j; rc = xstring_table_init(139); TEST_ASSERT_TRUE(rc >= 0); for (i = 0; i < NTHREADS; i++) { snprintf(args[i].key, sizeof(args[i].key), "key_%d", i); args[i].result = NULL; rc = pthread_create(&tids[i], NULL, thread_make_distinct_key, &args[i]); TEST_ASSERT_TRUE(rc == 0); } for (i = 0; i < NTHREADS; i++) { void* retval = NULL; rc = pthread_join(tids[i], &retval); TEST_ASSERT_TRUE(rc == 0); } /* Every pointer must be non-NULL */ for (i = 0; i < NTHREADS; i++) { TEST_ASSERT_TRUE(args[i].result != NULL); } /* Every pointer must be distinct from all others */ for (i = 0; i < NTHREADS; i++) { for (j = i + 1; j < NTHREADS; j++) { TEST_ASSERT_NOT_EQUAL(args[i].result, args[j].result); } } /* Release each thread's reference */ for (i = 0; i < NTHREADS; i++) { dereference((var)args[i].result); } xstring_table_fini(); } static void test_xstring_create(void) { char* s = "Hello, world"; char* d = NULL; int rc; rc = xstring_table_init(139); TEST_ASSERT_TRUE(rc >= 0); d = makeString(s); TEST_ASSERT_TRUE(d != NULL); TEST_ASSERT_TRUE(strcmp(d, s) == 0); TEST_ASSERT_TRUE(length(d) == strlen(s)); TEST_ASSERT_TRUE(hash(d) == hash_string(s)); xstring_table_fini(); } static void test_xstring_share(void) { char* s = "Hello, world"; char* d = NULL; char* d1 = NULL; int rc; rc = xstring_table_init(139); TEST_ASSERT_TRUE(rc >= 0); d = makeString(s); TEST_ASSERT_TRUE(d != NULL); TEST_ASSERT_TRUE(strcmp(d, s) == 0); TEST_ASSERT_TRUE(length(d) == strlen(s)); TEST_ASSERT_TRUE(hash(d) == hash_string(s)); d1 = makeString(s); TEST_ASSERT_TRUE(d1 != NULL); TEST_ASSERT_TRUE(d == d1); xstring_table_fini(); } static void test_xstring_deref(void) { char* s = "Hello, world"; char* d = NULL; int rc; rc = xstring_table_init(139); TEST_ASSERT_TRUE(rc >= 0); d = makeString(s); TEST_ASSERT_TRUE(d != NULL); TEST_ASSERT_TRUE(strcmp(d, s) == 0); TEST_ASSERT_TRUE(length(d) == strlen(s)); TEST_ASSERT_TRUE(hash(d) == hash_string(s)); dereference((var)d); xstring_table_fini(); } void test_xstrings(void) { printf("\n>> xstring test\n\n"); RUN_TEST(test_xstring_create); RUN_TEST(test_xstring_deref); RUN_TEST(test_xstring_share); RUN_TEST(test_xstring_concurrent_same_key); RUN_TEST(test_xstring_concurrent_distinct_keys); return; }