#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
#include <pthread.h>

timespec start, running, joined, sorted, sent, received;
std::vector<int> v(1000, 1);
pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
int msg, ready;

template<typename C>
double avg(const C& v)
{
   return std::accumulate(v.begin(), v.end(), 0.0)/v.size();
}

// result in us
double diff(timespec x, timespec y)
{
    return  (y.tv_sec*1000000.0 + y.tv_nsec/1000.0) 
          - (x.tv_sec*1000000.0 + x.tv_nsec/1000.0);
}

extern "C" void* f1(void*)
{
    clock_gettime(CLOCK_REALTIME, &running);
    return NULL;
}

extern "C" void* f2(void*)
{
    pthread_mutex_lock(&mx);
    ready = 1;
    pthread_cond_signal(&cv);
    
    while(msg == 0)
        pthread_cond_wait(&cv, &mx);
    clock_gettime(CLOCK_REALTIME, &received);
    pthread_mutex_unlock(&mx);
    return NULL;
}

int main()
{
    std::partial_sum(v.begin(), v.end(), v.begin()); // poor man's std::iota

    pthread_t hndl;
    std::vector<double> starts, joins, sorts, sends;
    for(int n = 0; n < 100000; ++n)
    {
        std::random_shuffle(v.begin(), v.end());

        clock_gettime(CLOCK_REALTIME, &start);
        pthread_create(&hndl, NULL, f1, NULL);
        pthread_join(hndl, NULL);
        clock_gettime(CLOCK_REALTIME, &joined);
        std::sort(v.begin(), v.end());
        clock_gettime(CLOCK_REALTIME, &sorted);

        pthread_create(&hndl, NULL, f2, NULL);
        pthread_mutex_lock(&mx);
        while(!ready)
            pthread_cond_wait(&cv, &mx);
        msg=1;
        clock_gettime(CLOCK_REALTIME, &sent);
        pthread_cond_signal(&cv);
        pthread_mutex_unlock(&mx);
        pthread_join(hndl, NULL);
        msg=ready=0;

        starts.push_back(diff(start, running));
        joins.push_back(diff(running, joined));
        sorts.push_back(diff(joined, sorted));
        sends.push_back(diff(sent, received));
    }
    std::cout << "average time to spawn a thread: " << avg(starts) << " us\n"
              << "average time to join a thread: " << avg(joins) << " us\n"
              << "average time to sort 1000 ints: " << avg(sorts) << " us\n"
              << "average time to message a thread: " << avg(sends) << " us\n";
    pthread_mutex_destroy(&mx);
    pthread_cond_destroy(&cv);
}
