/* * Copyright (c) 2017-2025, Patrick Pelissier * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * + Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * + Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "m-buffer.h" #include "test-obj.h" #include "coverage.h" START_COVERAGE // Define a fixed queue of unsigned int BUFFER_DEF(buffer_uint, unsigned int, 10, BUFFER_QUEUE|BUFFER_BLOCKING) QUEUE_MPMC_DEF(queue_uint, unsigned int, BUFFER_QUEUE) QUEUE_SPSC_DEF(squeue_uint, unsigned int, BUFFER_QUEUE) END_COVERAGE // Define a variable stack of float BUFFER_DEF(buffer_floats, float, 0, BUFFER_STACK|BUFFER_BLOCKING) // Define a fixed stack of char BUFFER_DEF(buffer_char, char, 10, BUFFER_STACK|BUFFER_UNBLOCKING) // Define a fixed queue of long long BUFFER_DEF(buffer_llong, long long, 16, BUFFER_QUEUE|BUFFER_THREAD_UNSAFE|BUFFER_UNBLOCKING) // Define a buffer of complex structure. BUFFER_DEF(buffer_mpz, testobj_t, 32, BUFFER_QUEUE, TESTOBJ_OPLIST) QUEUE_MPMC_DEF(queue_z, testobj_t, BUFFER_QUEUE, TESTOBJ_OPLIST) QUEUE_SPSC_DEF(squeue_a, testobj_t, BUFFER_QUEUE, TESTOBJ_OPLIST) // Define other buffers BUFFER_DEF_AS(BufferDouble1, BufferDouble1, double, 4, BUFFER_QUEUE) QUEUE_MPMC_DEF_AS(BufferDouble2, BufferDouble2, double, BUFFER_QUEUE) QUEUE_SPSC_DEF_AS(BufferDouble3, BufferDouble3, double, BUFFER_QUEUE) #define M_OPL_BufferDouble1() BUFFER_OPLIST(BufferDouble1, M_BASIC_OPLIST) #define M_OPL_BufferDouble2() BUFFER_OPLIST(BufferDouble2, M_BASIC_OPLIST) #define M_OPL_BufferDouble3() BUFFER_OPLIST(BufferDouble3, M_BASIC_OPLIST) // Define buffer with ENUM oplist (with API_1 usage) typedef enum { OK, KO } state_t; #define M_OPL_state_t() M_ENUM_OPLIST(state_t, OK) BUFFER_DEF(array_state, state_t, 0, M_BUFFER_QUEUE) buffer_uint_t g_buff; buffer_uint_t g_buffB; // Number of thread created by the test (twice this amount). #define MAX_TEST_THREAD 100 #define MAX_TEST_THREAD2 (MAX_TEST_THREAD*2/3) #define MAX_COUNT 1024 /* Global counter. Each thread has its own row of the matrix (exclusive use). */ unsigned int g_count[MAX_TEST_THREAD][MAX_COUNT]; static void conso(void *arg) { unsigned int j; uintptr_t thread_id = (uintptr_t) arg; assert(thread_id < MAX_TEST_THREAD); for(int i = 0; i < MAX_COUNT;i++) { buffer_uint_pop(&j, g_buff); assert (j < MAX_COUNT); // Each thread fill in its own row of the matrix, ensuring separate access. // We don't want atomic access which may perturbate the test // by adding memory barries that will hide some issues in the code. g_count[thread_id][j] ++; } } static void prod(void *arg) { assert (arg == NULL); for(unsigned int i = 0; i < MAX_COUNT;i++) buffer_uint_push(g_buff, i); } static void test_global(void) { m_thread_t idx_p[MAX_TEST_THREAD]; m_thread_t idx_c[MAX_TEST_THREAD]; buffer_uint_init(g_buff, 10); assert (buffer_uint_capacity(g_buff) == 10); assert (buffer_uint_empty_p(g_buff)); assert (!buffer_uint_full_p(g_buff)); assert (buffer_uint_size(g_buff) == 0); // Run multiple producer & consummer threads in parallel for(int i = 0; i < MAX_TEST_THREAD; i++) { m_thread_create (idx_p[i], conso, (void*)(uintptr_t) i); m_thread_create (idx_c[i], prod, NULL); } for(int i = 0; i < MAX_TEST_THREAD;i++) { m_thread_join(idx_p[i]); m_thread_join(idx_c[i]); } // Check if the data were correctly transmitted // from the producer threads to the consummer threads // First, consolidate the data for(int i = 1 ; i < MAX_TEST_THREAD; i++) { for(int j = 0 ; j < MAX_COUNT; j++) { g_count[0][j] += g_count[i][j]; } } // Then check then for(int j = 0 ; j < MAX_COUNT; j++) { assert(g_count[0][j] == MAX_TEST_THREAD); } assert (buffer_uint_empty_p(g_buff)); assert (!buffer_uint_full_p(g_buff)); assert (buffer_uint_size(g_buff) == 0); buffer_uint_reset(g_buff); assert (buffer_uint_empty_p(g_buff)); assert (!buffer_uint_full_p(g_buff)); assert (buffer_uint_size(g_buff) == 0); for(unsigned int i = 0; i < 5;i++) buffer_uint_push(g_buff, i); assert (!buffer_uint_empty_p(g_buff)); assert (!buffer_uint_full_p(g_buff)); assert (buffer_uint_size(g_buff) == 5); for(unsigned int i = 0; i < 5;i++) buffer_uint_push(g_buff, i); assert (!buffer_uint_empty_p(g_buff)); assert (buffer_uint_full_p(g_buff)); assert (buffer_uint_size(g_buff) == 10); assert (buffer_uint_push_blocking(g_buff, 15, false) == false); buffer_uint_reset(g_buff); assert (buffer_uint_empty_p(g_buff)); assert (!buffer_uint_full_p(g_buff)); assert (buffer_uint_size(g_buff) == 0); unsigned int s; assert (buffer_uint_pop_blocking(&s, g_buff, false) == false); assert (buffer_uint_overwrite(g_buff) == 0); buffer_uint_init_set(g_buffB, g_buff); assert (buffer_uint_empty_p(g_buffB)); buffer_uint_clear(g_buffB); for(unsigned int i = 0; i < 5;i++) buffer_uint_push(g_buff, i); buffer_uint_init_set(g_buffB, g_buff); assert (!buffer_uint_empty_p(g_buffB)); assert (!buffer_uint_full_p(g_buffB)); assert (buffer_uint_size(g_buffB) == 5); for(unsigned int i = 0; i < 5;i++) { unsigned int j; bool b= buffer_uint_pop(&j, g_buffB); assert(b); assert(j == i); } assert (buffer_uint_empty_p(g_buffB)); buffer_uint_set(g_buffB, g_buff); assert (!buffer_uint_empty_p(g_buffB)); assert (!buffer_uint_full_p(g_buffB)); assert (buffer_uint_size(g_buffB) == 5); for(unsigned int i = 0; i < 5;i++) { unsigned int j; bool b= buffer_uint_pop(&j, g_buffB); assert(b); assert(j == i); } assert (buffer_uint_empty_p(g_buffB)); buffer_uint_set(g_buffB, g_buff); buffer_uint_reset(g_buff); assert (buffer_uint_empty_p(g_buff)); assert (buffer_uint_size(g_buff) == 0); buffer_uint_set(g_buff, g_buffB); assert (!buffer_uint_empty_p(g_buff)); assert (!buffer_uint_full_p(g_buff)); assert (buffer_uint_size(g_buff) == 5); buffer_uint_clear(g_buffB); buffer_uint_clear(g_buff); } static void test_stack(void) { buffer_floats_t buff; buffer_floats_init(buff, 100); assert (buffer_floats_capacity(buff) == 100); for(unsigned int i = 0; i < 100; i++) { float f = (float) i; buffer_floats_push(buff, f); assert (buffer_floats_empty_p(buff) == false); } assert (buffer_floats_full_p(buff) == true); for(unsigned int i = 0; i < 100; i++) { float j; buffer_floats_pop(&j, buff); assert(j == (float) (99-i) ); assert (buffer_floats_full_p(buff) == false); } assert (buffer_floats_empty_p(buff) == true); buffer_floats_clear(buff); } static void test_stack2(void) { buffer_char_t buff; bool b; char c; buffer_char_init(buff, 10); for(int i = 0; i < 10; i++) { c = (char) i; b = buffer_char_push(buff, c); assert (b == true); assert (buffer_char_empty_p(buff) == false); } b = buffer_char_push(buff, c); assert (b == false); assert (buffer_char_full_p(buff) == true); for(int i = 0; i < 10; i++) { buffer_char_pop(&c, buff); assert(c == 9-i); assert (buffer_char_full_p(buff) == false); } b = buffer_char_pop(&c, buff); assert (b == false); assert (buffer_char_empty_p(buff) == true); buffer_char_clear(buff); } static void test_no_thread(void) { buffer_llong_t buff; bool b; long long c; buffer_llong_init(buff, 16); for(int i = 0; i < 16; i++) { c = i; b = buffer_llong_push(buff, c); assert (b == true); assert (buffer_llong_empty_p(buff) == false); } b = buffer_llong_push(buff, c); assert (b == false); assert (buffer_llong_full_p(buff) == true); for(int i = 0; i < 16; i++) { buffer_llong_pop(&c, buff); assert(c == i); assert (buffer_llong_full_p(buff) == false); } b = buffer_llong_pop(&c, buff); assert (b == false); assert (buffer_llong_empty_p(buff) == true); b = buffer_llong_push(buff, c); assert (b == true); buffer_llong_reset(buff); assert (buffer_llong_empty_p(buff) == true); buffer_llong_clear(buff); } static void test_emplace(void) { buffer_mpz_t b; buffer_mpz_init(b, 32); buffer_mpz_emplace_str(b, "56"); testobj_t o; testobj_init(o); buffer_mpz_pop(&o, b); assert( testobj_cmp_ui(o, 56) == 0 ); testobj_clear(o); buffer_mpz_clear(b); } /********************************************************************************************/ /* Test intrusive shared pointer + buffer */ #include "m-i-shared.h" // Tiny test structure typedef struct test_s { ISHARED_PTR_INTERFACE(ishared_itest, struct test_s); char buffer[52]; char bigbuffer[1000000]; } test_t; static void test_init(test_t *p) { memset(p->buffer, 0x00, 52); } static void test_clear(test_t *p) { memset(p->buffer, 0xFF, 52); } ISHARED_PTR_DEF(ishared_itest, test_t, (INIT(test_init M_IPTR), CLEAR(test_clear M_IPTR), DEL(free))) typedef struct test2_s { ISHARED_PTR_INTERFACE(ishared_itest2, struct test_s); char buffer[52]; } test2_t; static inline void ishared_itest2_init_data(test2_t *d) { memset(d->buffer, 0, sizeof (d->buffer)); } ISHARED_PTR_DEF(ishared_itest2, test2_t, (INIT(API_2(ishared_itest2_init_data)), CLEAR(M_NOTHING_DEFAULT))) typedef struct test3_s { ISHARED_PTR_INTERFACE(ishared_itest3, struct test_s); char buffer[52]; } test3_t; ISHARED_PTR_DEF(ishared_itest3, test3_t, (CLEAR(M_NOTHING_DEFAULT))) typedef struct test4_s { ISHARED_PTR_INTERFACE(ishared_itest4, struct test_s); char buffer[52]; } test4_t; ISHARED_PTR_DEF(ishared_itest4, test4_t, (INIT(0), DEL(0), INIT(M_MEMSET_DEFAULT), CLEAR(M_NOTHING_DEFAULT))) static test_t *test_new(void) { test_t *p = M_ASSIGN_CAST(test_t*, malloc (sizeof (test_t))); assert (p != NULL); test_init (p); return ishared_itest_init(p); } BUFFER_DEF(buffer_itest, test_t *, 16, BUFFER_PUSH_INIT_POP_MOVE, ISHARED_PTR_OPLIST(ishared_itest)) static buffer_itest_t comm1; static buffer_itest_t comm2; static void test_conso1(void *arg) { test_t *p; assert (arg == NULL); for(int i = 0; i < 10;i++) { buffer_itest_pop(&p, comm1); for(int j = 0; j < 52; j++) assert (p->buffer[j] == (char) ((j * j * 17) + j * 42 + 1)); ishared_itest_clear(p); } } static void test_conso2(void *arg) { test_t *p; assert (arg == NULL); for(int i = 0; i < 10;i++) { buffer_itest_pop(&p, comm2); for(int j = 0; j < 52; j++) assert (p->buffer[j] == (char) ((j * j * 17) + j * 42 + 1)); ishared_itest_clear(p); } } static void test_prod(void *arg) { assert (arg == NULL); for(unsigned int i = 0; i < 10;i++) { test_t *p = test_new(); for(int j = 0; j < 52; j++) { p->buffer[j] = (char) ((j * j * 17) + j * 42 + 1); } buffer_itest_push(comm1, p); buffer_itest_push(comm2, p); ishared_itest_clear(p); } } static void test_global_ishared(void) { m_thread_t idx_p[MAX_TEST_THREAD2]; m_thread_t idx_c1[MAX_TEST_THREAD2]; m_thread_t idx_c2[MAX_TEST_THREAD2]; buffer_itest_init(comm1, 16); buffer_itest_init(comm2, 16); for(int i = 0; i < MAX_TEST_THREAD2; i++) { m_thread_create (idx_c1[i], test_conso1, NULL); m_thread_create (idx_c2[i], test_conso2, NULL); m_thread_create (idx_p[i], test_prod, NULL); } for(int i = 0; i < MAX_TEST_THREAD2;i++) { m_thread_join(idx_p[i]); m_thread_join(idx_c1[i]); m_thread_join(idx_c2[i]); } buffer_itest_clear(comm1); buffer_itest_clear(comm2); } /********************************************************************************************/ QUEUE_MPMC_DEF(queue_ull, unsigned long long, BUFFER_QUEUE) queue_uint_t g_buff2; queue_ull_t g_final2; unsigned long long g_result; static void final2(void *arg) { size_t *p_n = M_ASSIGN_CAST(size_t *, arg); size_t n = *p_n; unsigned long long j, s = 0; for(unsigned int i = 0; i < n;i++) { while (!queue_ull_pop(&j, g_final2)); s += j; } g_result = s; } static void conso2(void *arg) { unsigned int j; size_t *p_n = M_ASSIGN_CAST(size_t *, arg); size_t n = *p_n; unsigned long long s = 0; for(unsigned int i = 0; i < n;i++) { while (!queue_uint_pop(&j, g_buff2)); s += j; } while (!queue_ull_push(g_final2, s)); } static void prod2(void *arg) { size_t *p_n = M_ASSIGN_CAST(size_t *, arg); size_t n = *p_n; size_t r = n; for(unsigned int i = 0; i < n;i++) { while (!queue_uint_push(g_buff2, (unsigned int) r )); r = r * 31421U + 6927U; } } static void test_queue(size_t n, int cpu_count, unsigned long long ref) { cpu_count = M_MIN(cpu_count, 64); const int prod_count = cpu_count / 2; const int conso_count = cpu_count - prod_count; // Init queue_uint_init(g_buff2, 64*2); queue_ull_init (g_final2, 64*2); assert (queue_uint_capacity(g_buff2) == 64*2); assert (queue_ull_capacity(g_final2) == 64*2); // Create thread m_thread_t idx_p[64]; m_thread_t idx_c[64]; m_thread_t idx_final; for(int i = 0; i < prod_count; i++) { m_thread_create (idx_p[i], prod2, &n); } for(int i = 0; i < conso_count; i++) { m_thread_create (idx_c[i], conso2, &n); } size_t n2 = (size_t) conso_count; m_thread_create(idx_final, final2, &n2); // Wait for jobs to be done. for(int i = 0; i < prod_count; i++) { m_thread_join(idx_p[i]); } for(int i = 0; i < conso_count; i++) { m_thread_join(idx_c[i]); } m_thread_join(idx_final); assert(g_result == ref); // Clear & quit queue_ull_clear(g_final2); queue_uint_clear(g_buff2); } /********************************************************************************************/ static void test_spsc(void) { squeue_uint_t q; bool b; unsigned j; squeue_uint_init(q, 256); assert(squeue_uint_empty_p(q)); for(unsigned i = 0; i < 256; i++) { b = squeue_uint_push(q, i); assert(b); } assert(!squeue_uint_empty_p(q)); assert(squeue_uint_full_p(q)); assert(squeue_uint_size(q) == 256); assert(squeue_uint_capacity(q) == 256); b = squeue_uint_push(q, 256); assert (!b); for(unsigned i = 0; i < 256; i++) { b = squeue_uint_pop(&j, q); assert(b); assert(j == i); } assert(squeue_uint_empty_p(q)); assert(!squeue_uint_full_p(q)); assert(squeue_uint_size(q) == 0); assert(squeue_uint_capacity(q) == 256); for(unsigned i = 0; i < 256; i++) { b = squeue_uint_push(q, i); assert(b); } for(unsigned i = 256; i < 256+128; i++) { assert(squeue_uint_full_p(q)); squeue_uint_push_force(q, i); } for(unsigned i = 128; i < 256+128; i++) { b = squeue_uint_pop(&j, q); assert(b); assert(j == i); } unsigned tab[16]; for(unsigned i = 0; i < 16; i++) tab[i] = i * i; for(unsigned i = 0; i < 15; i++) { j = squeue_uint_push_bulk(q, 16, tab); assert(j == 16); } b = squeue_uint_push(q, 1024); assert(b); j = squeue_uint_push_bulk(q, 16, tab); assert(j == 15); for(unsigned i = 0; i < 16; i++) tab[i] = 0; j = squeue_uint_pop_bulk(16, tab, q); assert(j == 16); for(unsigned i = 0; i < 16; i++) assert( tab[i] == i * i); do { b = squeue_uint_pop(&j, q); } while (b); assert(squeue_uint_empty_p(q)); unsigned l = 65536; b = squeue_uint_push_move(q, &l); assert(b); b = squeue_uint_pop(&j, q); assert(b); assert(j == 65536); do { l = 189; b = squeue_uint_push_move(q, &l); } while (b); assert( squeue_uint_size(q) == 256); squeue_uint_clear(q); } /********************************************************************************************/ static void test_double1(void) { bool b; M_LET(buffer, BufferDouble1) { b = BufferDouble1_push(buffer, 0.0); assert(b); b= BufferDouble1_push(buffer, 1.0); assert(b); double d; b = BufferDouble1_pop(&d, buffer); assert(b); assert(d == 0.0); b = BufferDouble1_pop(&d, buffer); assert(b); assert(d == 1.0); } } static void test_double2(void) { bool b; BufferDouble2 buffer; BufferDouble2_init(buffer, 2); b = BufferDouble2_push(buffer, 0.0); assert(b); b= BufferDouble2_push(buffer, 1.0); assert(b); double d; b = BufferDouble2_pop(&d, buffer); assert(b); assert(d == 0.0); b = BufferDouble2_pop(&d, buffer); assert(b); assert(d == 1.0); BufferDouble2_clear(buffer); } static void test_double3(void) { bool b; BufferDouble3 buffer; BufferDouble3_init(buffer, 2); b = BufferDouble3_push(buffer, 0.0); assert(b); b= BufferDouble3_push(buffer, 1.0); assert(b); double d; b = BufferDouble3_pop(&d, buffer); assert(b); assert(d == 0.0); b = BufferDouble3_pop(&d, buffer); assert(b); assert(d == 1.0); BufferDouble3_clear(buffer); } /********************************************************************************************/ static void test_uint(void) { unsigned int x, y; buffer_uint_t v; buffer_uint_init(v, 10); assert (buffer_uint_empty_p(v) == true); assert (buffer_uint_full_p(v) == false); x = 10; buffer_uint_push (v, x); assert (buffer_uint_empty_p(v) == false); assert (buffer_uint_full_p(v) == false); buffer_uint_pop (&y, v); assert (buffer_uint_empty_p(v) == true); assert (buffer_uint_full_p(v) == false); assert (y == 10); buffer_uint_clear(v); } int main(void) { test_uint(); test_global(); test_stack(); test_stack2(); test_no_thread(); test_emplace(); test_global_ishared(); test_queue(1000000, 2, 2148371710223136ULL); test_spsc(); test_double1(); test_double2(); test_double3(); testobj_final_check(); exit(0); }