#include <pthread.h>
#include <time.h>
#include <stdio.h>


//Measure overhead of calling std::condition_variable::notify_one() if noone is waiting

#define NUM_CALLS 1000000

static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
static bool exit_touch_thread = false;

static void *touch_thread(void *) {
	pthread_mutex_lock(&mtx);
	while(!exit_touch_thread)
		pthread_cond_wait(&cv,&mtx);
	pthread_mutex_unlock(&mtx);
	return nullptr;
}


int main(void) {
	//First spawn a thread and make it wait on the CV. This is to
	//ensure that any user/kernel resources lazily allocated is done.
	pthread_t tid;
	pthread_create(&tid,nullptr,&touch_thread,nullptr);
	
	timespec ts_wait{0,1000000000};
	nanosleep(&ts_wait,nullptr);
	
		{
	pthread_mutex_lock(&mtx);
		exit_touch_thread = true;
		pthread_cond_signal(&cv);
		pthread_mutex_unlock(&mtx);
	}
	
	pthread_join(tid,nullptr);
	
	timespec ts_start;
	clock_gettime(CLOCK_REALTIME,&ts_start);
	
	//then call pthread_cond_signal() a million times
	for(int i=0; i<NUM_CALLS; i++)
		pthread_cond_signal(&cv);
	
	timespec ts_end;
	clock_gettime(CLOCK_REALTIME,&ts_end);
	
	double time_taken = (ts_end.tv_sec-ts_start.tv_sec) +
	                    (ts_end.tv_nsec-ts_start.tv_nsec)/1000000000.0;
	
	printf("%.3f seconds to 1000000 pthread_cond_signal() calls\n", time_taken);
	printf(" = %.6f nanoseconds/call\n", time_taken/NUM_CALLS*1000000000.0);
	
	return 0;
}
