#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 <stdio.h>
#include <stdlib.h>
#include <threads.h>

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

// 這裡同樣定義一個普通的全域變數和 thread-local 全域變數，
// 但由於這個範例要演示手動做維護 thread-local 的關係，
// 所以這裡使用的是 tss_t ，可以理解為 thread-local 變數的標籤。
int thread_share_value = 0;
tss_t thread_local_storage;

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

static
int* CreateCutomisedVariable(int init_val)
{
    int *val = malloc(sizeof(*val));
    assert(val);
    *val = init_val;

    printf("# Create customised variable with initial-value=%d\n", *val);
    return val;
}

static
void ReleaseCustomisedVariable(int *val)
{
    assert(val);
    printf("# Release customised variable with current-value=%d\n", *val);

    free(val);
}

static
int ThrdFunc1(void *userarg)
{
    // 其實這個動作不是必要的，
    // 因為這個執行緒啟動時的 thread-local 變數初始肯定是 NULL，
    // 不過為了證明這件事，所以這裡還是演示一下。
    int *thread_local_value = tss_get(thread_local_storage);
    assert( thread_local_value == NULL );

    // 建立一個給這個執行緒專用的變數，
    // 並將其指派給那個 thread-local 標籤。
    int e = tss_set(
        thread_local_storage, CreateCutomisedVariable(0));
    assert( e == thrd_success );

    SleepMs(600);
    for(int i = 0; i < 5; ++i)
    {
        // 在執行緒執行的任何階段，
        // 都可以向 thread-local 標籤索取它所關聯的變數。
        // 並且這個變數是這個執行緒所專用的(就是上面才剛設定的那個)。
        int *thread_local_value = tss_get(thread_local_storage);
        assert(thread_local_value);

        thread_share_value += 1;
        *thread_local_value += 1;

        printf("| Thrd-1:\t%d\t%d\t(+1)\n",
            thread_share_value, *thread_local_value);
        SleepMs(1000);
    }

    // 執行緒要結束了。
    // 在 thread-local 有設定了銷毀函式的情況下，
    // 可以選擇手動銷毀 thread-local 變數，也可以讓系統自動處理。
    // 這裡選擇讓系統自動處理，於是不做任何事。

    printf("# Thrd-1 is finished\n");
    return 0;
}

static
int ThrdFunc2(void *userarg)
{
    // 其實這個動作不是必要的，
    // 因為這個執行緒啟動時的 thread-local 變數初始肯定是 NULL，
    // 不過為了證明這件事，所以這裡還是演示一下。
    int *thread_local_value = tss_get(thread_local_storage);
    assert( thread_local_value == NULL );

    // 建立一個給這個執行緒專用的變數，
    // 並將其指派給那個 thread-local 標籤。
    int e = tss_set(
        thread_local_storage, CreateCutomisedVariable(0));
    assert( e == thrd_success );

    SleepMs(100);
    for(int i = 0; i < 5; ++i)
    {
        // 在執行緒執行的任何階段，
        // 都可以向 thread-local 標籤索取它所關聯的變數。
        // 並且這個變數是這個執行緒所專用的(就是上面才剛設定的那個)。
        int *thread_local_value = tss_get(thread_local_storage);
        assert(thread_local_value);

        thread_share_value += 2;
        *thread_local_value += 2;

        printf("| Thrd-2:\t%d\t%d\t(+2)\n",
            thread_share_value, *thread_local_value);
        SleepMs(1000);
    }
    printf("\n");

    // 執行緒要結束了。
    // 在 thread-local 有設定了銷毀函式的情況下，
    // 可以選擇手動銷毀 thread-local 變數，也可以讓系統自動處理。
    // 這裡選擇手動銷毀 thread-local 變數。
    printf("# Thrd-2 manually releasing thread-local variable\n");
    {
        int *thread_local_value = tss_get(thread_local_storage);
        assert(thread_local_value);

        ReleaseCustomisedVariable(thread_local_value);

        // 手動銷毀了原本設定的 thread-local 變數之後，
        // 要記得將 thread-local 標籤的關聯設定為 NULL，
        // 免得觸發系統自動銷毀，造成重複銷毀的問題！
        tss_set(thread_local_storage, NULL);
    }

    printf("# Thrd-2 is finished\n");
    return 0;
}

int main(int argc, char *argv[])
{
    // 在開始使用 thread-local 標籤之前，
    // 這個標籤必須要先被初始化。
    // 其中這裡可以指派一個用來銷毀 thread-local 變數的函式，
    // 讓系統在執行緒結束時自動呼叫它來銷毀所屬的 thread-local 變數。
    // 當然也可以不指派銷毀函式(設定為 NULL)，
    // 不過這要就會需要在執行緒退出前自己手動銷毀所屬的 thread-local 變數。
    int e = tss_create(
        &thread_local_storage,
        (void(*)(void*)) ReleaseCustomisedVariable);
    assert( e == thrd_success );

    printf("| Thread\tShare\tLocal\tComment\n");
    printf("|---------------------------------------\n");

    thrd_t thrd1;
    thrd_create(&thrd1, ThrdFunc1, NULL);

    thrd_t thrd2;
    thrd_create(&thrd2, ThrdFunc2, NULL);

    thrd_join(thrd1, NULL);
    thrd_join(thrd2, NULL);
    printf("# Threads are terminated\n");

    // 最後要銷毀 thread-local 標籤。
    // 並且注意這件事必須在 thread-local 標籤所關聯的
    // 所有 thread-local 變數都被妥善銷毀之後，
    // 才能執行這個銷毀標籤的動作！
    tss_delete(thread_local_storage);

    return 0;
}
