#include "test/jemalloc_test.h"

#include "jemalloc/internal/seq.h"

typedef struct data_s data_t;
struct data_s {
       int arr[10];
};

static void
set_data(data_t *data, int num) {
       for (int i = 0; i < 10; i++) {
               data->arr[i] = num;
       }
}

static void
expect_data(data_t *data) {
       int num = data->arr[0];
       for (int i = 0; i < 10; i++) {
               expect_d_eq(num, data->arr[i], "Data consistency error");
       }
}

seq_define(data_t, data)

typedef struct thd_data_s thd_data_t;
struct thd_data_s {
       seq_data_t data;
};

static void *
seq_reader_thd(void *arg) {
       thd_data_t *thd_data = (thd_data_t *)arg;
       int iter = 0;
       data_t local_data;
       while (iter < 1000 * 1000 - 1) {
               bool success = seq_try_load_data(&local_data, &thd_data->data);
               if (success) {
                       expect_data(&local_data);
                       expect_d_le(iter, local_data.arr[0],
                           "Seq read went back in time.");
                       iter = local_data.arr[0];
               }
       }
       return NULL;
}

static void *
seq_writer_thd(void *arg) {
       thd_data_t *thd_data = (thd_data_t *)arg;
       data_t local_data;
       memset(&local_data, 0, sizeof(local_data));
       for (int i = 0; i < 1000 * 1000; i++) {
               set_data(&local_data, i);
               seq_store_data(&thd_data->data, &local_data);
       }
       return NULL;
}

TEST_BEGIN(test_seq_threaded) {
       thd_data_t thd_data;
       memset(&thd_data, 0, sizeof(thd_data));

       thd_t reader;
       thd_t writer;

       thd_create(&reader, seq_reader_thd, &thd_data);
       thd_create(&writer, seq_writer_thd, &thd_data);

       thd_join(reader, NULL);
       thd_join(writer, NULL);
}
TEST_END

TEST_BEGIN(test_seq_simple) {
       data_t data;
       seq_data_t seq;
       memset(&seq, 0, sizeof(seq));
       for (int i = 0; i < 1000 * 1000; i++) {
               set_data(&data, i);
               seq_store_data(&seq, &data);
               set_data(&data, 0);
               bool success = seq_try_load_data(&data, &seq);
               expect_b_eq(success, true, "Failed non-racing read");
               expect_data(&data);
       }
}
TEST_END

int main(void) {
       return test_no_reentrancy(
           test_seq_simple,
           test_seq_threaded);
}