225 lines
5.8 KiB
C
225 lines
5.8 KiB
C
#include <pthread.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unity/unity.h>
|
|
|
|
#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;
|
|
}
|