| | #include "unity/unity.h" |
| | #include "zlib.h" |
| |
|
| | #include <stdlib.h> |
| | #include <string.h> |
| | #include <stdint.h> |
| | #include <pthread.h> |
| |
|
| | |
| | |
| | #if defined(__STDC__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) |
| | #define HAVE_C11_ATOMICS 1 |
| | #else |
| | #define HAVE_C11_ATOMICS 0 |
| | #endif |
| |
|
| | |
| | #ifndef ONCE_TEST_THREADS |
| | #define ONCE_TEST_THREADS 8 |
| | #endif |
| |
|
| | |
| | typedef struct { |
| | volatile int start_flag; |
| | } start_sync_t; |
| |
|
| | typedef struct { |
| | start_sync_t *sync; |
| | const unsigned char *buf; |
| | size_t len; |
| | const void *table_ptr; |
| | unsigned long crc_out; |
| | } thread_arg_t; |
| |
|
| | static void busy_wait_until_start(start_sync_t *sync) { |
| | |
| | while (!sync->start_flag) { } |
| | } |
| |
|
| | static void *thread_get_table(void *arg) { |
| | thread_arg_t *a = (thread_arg_t *)arg; |
| | busy_wait_until_start(a->sync); |
| | |
| | a->table_ptr = (const void *)get_crc_table(); |
| | |
| | if (a->buf && a->len > 0) { |
| | a->crc_out = crc32(0L, a->buf, (uInt)a->len); |
| | } else { |
| | a->crc_out = 0UL; |
| | } |
| | return NULL; |
| | } |
| |
|
| | static void *thread_do_crc(void *arg) { |
| | thread_arg_t *a = (thread_arg_t *)arg; |
| | busy_wait_until_start(a->sync); |
| | |
| | unsigned long crc = 0L; |
| | for (int i = 0; i < 1000; i++) { |
| | crc = crc32(0L, a->buf, (uInt)a->len); |
| | } |
| | a->crc_out = crc; |
| | |
| | a->table_ptr = (const void *)get_crc_table(); |
| | return NULL; |
| | } |
| |
|
| | void setUp(void) { |
| | |
| | |
| | } |
| |
|
| | void tearDown(void) { |
| | |
| | } |
| |
|
| | |
| | |
| | void test_once_concurrent_initialization_via_get_crc_table(void) { |
| | #if HAVE_C11_ATOMICS |
| | pthread_t tids[ONCE_TEST_THREADS]; |
| | thread_arg_t args[ONCE_TEST_THREADS]; |
| | start_sync_t sync; |
| | sync.start_flag = 0; |
| |
|
| | static const unsigned char data[] = "zlib once() concurrent init"; |
| | for (int i = 0; i < ONCE_TEST_THREADS; i++) { |
| | args[i].sync = &sync; |
| | args[i].buf = data; |
| | args[i].len = strlen((const char *)data); |
| | args[i].table_ptr = NULL; |
| | args[i].crc_out = 0; |
| | int rc = pthread_create(&tids[i], NULL, thread_get_table, &args[i]); |
| | TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "pthread_create failed"); |
| | } |
| |
|
| | |
| | sync.start_flag = 1; |
| |
|
| | for (int i = 0; i < ONCE_TEST_THREADS; i++) { |
| | pthread_join(tids[i], NULL); |
| | } |
| |
|
| | |
| | const void *ptr0 = args[0].table_ptr; |
| | TEST_ASSERT_NOT_NULL(ptr0); |
| | for (int i = 1; i < ONCE_TEST_THREADS; i++) { |
| | TEST_ASSERT_EQUAL_PTR(ptr0, args[i].table_ptr); |
| | } |
| |
|
| | |
| | const void *ptr_after = (const void *)get_crc_table(); |
| | TEST_ASSERT_EQUAL_PTR(ptr0, ptr_after); |
| | #else |
| | TEST_IGNORE_MESSAGE("Concurrent initialization is not supported without C11 atomics; skipping."); |
| | #endif |
| | } |
| |
|
| | |
| | void test_once_crc_known_value_single_thread(void) { |
| | const unsigned char msg[] = "123456789"; |
| | unsigned long crc = crc32(0L, msg, (uInt)strlen((const char *)msg)); |
| | TEST_ASSERT_EQUAL_HEX32(0xCBF43926UL, crc); |
| | } |
| |
|
| | |
| | void test_once_pointer_stability_single_thread(void) { |
| | const void *p1 = (const void *)get_crc_table(); |
| | TEST_ASSERT_NOT_NULL(p1); |
| |
|
| | for (int i = 0; i < 1000; i++) { |
| | const void *pi = (const void *)get_crc_table(); |
| | TEST_ASSERT_EQUAL_PTR(p1, pi); |
| | } |
| | } |
| |
|
| | |
| | |
| | void test_once_concurrent_crc_after_preinit(void) { |
| | |
| | const void *ptable = (const void *)get_crc_table(); |
| | TEST_ASSERT_NOT_NULL(ptable); |
| |
|
| | pthread_t tids[ONCE_TEST_THREADS]; |
| | thread_arg_t args[ONCE_TEST_THREADS]; |
| | start_sync_t sync; |
| | sync.start_flag = 0; |
| |
|
| | static const unsigned char msg[] = "The quick brown fox jumps over the lazy dog"; |
| | |
| | unsigned long expected = crc32(0L, msg, (uInt)strlen((const char *)msg)); |
| |
|
| | for (int i = 0; i < ONCE_TEST_THREADS; i++) { |
| | args[i].sync = &sync; |
| | args[i].buf = msg; |
| | args[i].len = strlen((const char *)msg); |
| | args[i].table_ptr = NULL; |
| | args[i].crc_out = 0; |
| | int rc = pthread_create(&tids[i], NULL, thread_do_crc, &args[i]); |
| | TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "pthread_create failed"); |
| | } |
| |
|
| | |
| | sync.start_flag = 1; |
| |
|
| | for (int i = 0; i < ONCE_TEST_THREADS; i++) { |
| | pthread_join(tids[i], NULL); |
| | } |
| |
|
| | for (int i = 0; i < ONCE_TEST_THREADS; i++) { |
| | TEST_ASSERT_EQUAL_HEX32(expected, args[i].crc_out); |
| | TEST_ASSERT_EQUAL_PTR(ptable, args[i].table_ptr); |
| | } |
| | } |
| |
|
| | int main(void) { |
| | UNITY_BEGIN(); |
| | RUN_TEST(test_once_concurrent_initialization_via_get_crc_table); |
| | RUN_TEST(test_once_crc_known_value_single_thread); |
| | RUN_TEST(test_once_pointer_stability_single_thread); |
| | RUN_TEST(test_once_concurrent_crc_after_preinit); |
| | return UNITY_END(); |
| | } |