#if __STDC_VERSION__ < 201112L
#   error Needs a C compiler which support the C11 standard or later!
#elif defined(__STDC_NO_THREADS__)
#   error Needs the C compiler and its runtime library support the C11 thread functions!
#endif

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>

#ifdef _WIN32
#   include <windows.h>
#else
#   include <unistd.h>
#endif

static
void SleepMs(unsigned ms)
{
#ifdef _WIN32
    Sleep(ms);
#else
    usleep( 1000 * ms );
#endif
}

static
unsigned GetMsTime(void)
{
#ifdef _WIN32
    return GetTickCount();
#else
    struct timespec ts;
    int err = clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
    assert( err == 0 );

    return ts.tv_sec * 1000 + ts.tv_nsec / (1000*1000);
#endif
}

struct Message
{
    // 用來將多個訊息組成串列而需要的資料結構
    struct Message *prev;
    struct Message *next;

    // 記錄訊息內容以及產生訊息的時間
    unsigned ctime;
    char str[128];
};

struct Factory
{
    // 記錄要產生的消息數量和時間見隔
    unsigned period;
    unsigned total_num;

    // 用來堆放消息的練表頭尾指標(以及用來保護相關操作內容的互斥子)
    mtx_t mutex;
    struct Message *first;
    struct Message *last;

    // 執行緒本身有關的資源
    thrd_t thrd;
};

struct Consumer
{
    // 記錄自己是第幾號執行緒、以及要向誰索取消息的目標
    unsigned idx;
    struct Factory *msg_src;

    // 執行緒本身有關的資源
    thrd_t thrd;
    bool go_term;
};

static
int FactoryThread(struct Factory *data)
{
    for(unsigned i = 0; i < data->total_num; ++i)
    {
        struct Message *msg = malloc(sizeof(*msg));
        msg->ctime = GetMsTime();
        snprintf(msg->str, sizeof(msg->str), "message-%u", i);
        printf("Factory: Generate \"%s\"\n", msg->str);

        mtx_lock(&data->mutex);

        msg->prev = data->last;
        msg->next = NULL;

        if(!data->first)
            data->first = msg;
        data->last = msg;

        mtx_unlock(&data->mutex);

        SleepMs(data->period);
    }

    return 0;
}

static
int ConsumerThread(struct Consumer *data)
{
    while(!data->go_term)
    {
        struct Factory *src = data->msg_src;

        mtx_lock(&src->mutex);

        struct Message *msg = src->first;
        if(msg)
        {
            src->first = msg->next;
            if(!src->first)
                src->last = NULL;

            if(msg->next)
                msg->next->prev = NULL;
        }

        mtx_unlock(&src->mutex);

        if(msg)
        {
            unsigned delay = GetMsTime() - msg->ctime;
            printf("Consumer[%u]: Process \"%s\", delay=%u\n",
                data->idx, msg->str, delay);
            free(msg);
        }
    }

    return 0;
}

int main(int argc, char *argv[])
{
    // 建立並啟動定時產生資料(工廠)的執行緒

    struct Factory factory =
    {
        .period = 50,
        .total_num = 1200,
    };

    mtx_init(&factory.mutex, mtx_plain);

    thrd_create(&factory.thrd, (int(*)(void*)) FactoryThread, &factory);

    // 建立並啟動數個用來索取和處理資料(消費者)的執行緒

    static const int consumer_num = 4;
    struct Consumer consumer_list[consumer_num];
    memset(consumer_list, 0, sizeof(consumer_list));

    for(int i = 0; i < consumer_num; ++i)
    {
        consumer_list[i].idx = i;
        consumer_list[i].msg_src = &factory;

        thrd_create(
            &consumer_list[i].thrd,
            (int(*)(void*)) ConsumerThread,
            &consumer_list[i]);
    }

    // 等待工廠執行緒完成工作自然結束

    int res;
    thrd_join(factory.thrd, &res);

    // 停止消費者執行緒

    for(int i = 0; i < consumer_num; ++i)
        consumer_list[i].go_term = true;

    for(int i = 0; i < consumer_num; ++i)
        thrd_join(consumer_list[i].thrd, &res);

    mtx_destroy(&factory.mutex);

    return 0;
}
