Return to Snippet

Revision: 17742
at August 9, 2010 12:41 by keigoi


Updated Code
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>

#define TIMEOUT 50000 // context switch rate, in microseconds
#define WAIT_COUNT 0x10000 // busy wait count
#define EXIT_AFTER 1  // exit after 1 sec


// monitor
pthread_mutex_t mutex;
pthread_cond_t cond;

volatile int locked; // the `world' is locked or not
volatile int waiters; // number of waiting threads
volatile int signal_has_come; // 1 if context switch signal has come

// thread that fire periodic signal for context switch
void* thread_tick(void* arg) {
	while(1) {
		// fprintf(stdout, "tick\n"); fflush(stdout); 
		signal_has_come = 1;
		struct timespec to;
		to.tv_sec = 0;
		to.tv_nsec = TIMEOUT * 1000;
		nanosleep(&to, 0);
	}
}

// release the global lock
void unlock_world() {
	pthread_mutex_lock(&mutex);
	locked = 0;
	pthread_mutex_unlock(&mutex);
	// fprintf(stdout, "released global lock (%d)\n", (int)pthread_self()); fflush(stdout);
	pthread_cond_signal(&cond);
}

// obtain the global lock
void lock_world() {
	pthread_mutex_lock(&mutex);
	while(locked) {
		// fprintf(stdout, "locked. sleeping.. (%d)\n", (int)pthread_self()); fflush(stdout);
		waiters++;
		pthread_cond_wait(&cond, &mutex);
		waiters--;
	}
	locked = 1;
	pthread_mutex_unlock(&mutex);
	// fprintf(stdout,"got global lock. restart. (%d)\n", (int)pthread_self());
}

// yield to switch to the other thread
void yield() {
	if(0==waiters) return;
	unlock_world();
	sched_yield();
	lock_world();
}

void check_signal_and_yield() {
	if(signal_has_come) {
		signal_has_come=0;
		yield();
	}
}

int hit_count[2]; // count how many times the loop is executed, for each thread
int prev;
int switch_count;

void work(int i) {
	while(1) {
		
		// count
		hit_count[i]++;
		if(i!=prev) { prev=i; switch_count++;}
		
		// yield
		check_signal_and_yield();
		
		// fprintf(stdout,"working. %d\n", (int)pthread_self());
		
		// busy waiting
		int i;
		for(i=0; i<WAIT_COUNT; i++);
	}
}


void* thread_start(void* arg) {
	// fprintf(stdout, "new thread start. %d\n", (int)pthread_self()); fflush(stdout); 
	lock_world();
	work(1);
	return 0;
}


// exit after specified second.
void interrupt_handler(int sig) {
	fprintf(stdout, "SIGALRM. thread0: %d, thread1: %d, switch:%d\n", hit_count[0], hit_count[1], switch_count); fflush(stdout);
	exit(0);
}

// set up the stop watch
void set_stop_alarm() {
	void(*oldact)(int);
	oldact = signal(SIGALRM, interrupt_handler);
	assert(SIG_ERR!=oldact);
	struct itimerval itv;
	itv.it_value.tv_sec = EXIT_AFTER;
	itv.it_value.tv_usec = 0;
	itv.it_interval = itv.it_value;
	setitimer(ITIMER_REAL, &itv, 0);
}

int main(int argc, char** argv) {
	
	// set up the stop watch
	set_stop_alarm();
	
	// set up mutex and condition variable for monitor
	int r = pthread_mutex_init(&mutex, 0);
	assert(0==r);
	r = pthread_cond_init(&cond, 0);
	assert(0==r);
	
	// start the sub thread
	pthread_t th;
	pthread_create(&th, 0, thread_start, 0);
	
	// start the tick thread
	pthread_t th2;
	pthread_create(&th2, 0, thread_tick, 0);
	
	// start the main thread
	lock_world();
	work(0);
	return 0;
}

Revision: 17741
at August 9, 2010 12:35 by keigoi


Updated Code
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>

#define TIMEOUT 50000 // context switch rate, in microseconds
#define WAIT_COUNT 0x10000 // busy wait count
#define EXIT_AFTER 1  // exit after 1 sec


// monitor
pthread_mutex_t mutex;
pthread_cond_t cond;

volatile int locked; // the `world' is locked or not
volatile int waiters; // number of waiting threads
volatile int signal_has_come; // 1 if context switch signal has come

// thread that fire periodic signal for context switch
void* thread_tick(void* arg) {
	while(1) {
		// fprintf(stdout, "tick\n"); fflush(stdout); 
		signal_has_come = 1;
		struct timespec to;
		to.tv_sec = 0;
		to.tv_nsec = TIMEOUT * 1000;
		nanosleep(&to, 0);
	}
}

// release the global lock
void unlock_world() {
	pthread_mutex_lock(&mutex);
	locked = 0;
	pthread_mutex_unlock(&mutex);
	// fprintf(stdout, "released global lock (%d)\n", (int)pthread_self()); fflush(stdout);
	pthread_cond_signal(&cond);
}

// obtain the global lock
void lock_world() {
	pthread_mutex_lock(&mutex);
	while(locked) {
		// fprintf(stdout, "locked. sleeping.. (%d)\n", (int)pthread_self()); fflush(stdout);
		waiters++;
		pthread_cond_wait(&cond, &mutex);
		waiters--;
	}
	locked = 1;
	pthread_mutex_unlock(&mutex);
	// fprintf(stdout,"got global lock. restart. (%d)\n", (int)pthread_self());
}

// yield to switch to the other thread
void yield() {
	if(0==waiters) return;
	unlock_world();
	sched_yield();
	lock_world();
}

void check_signal_and_yield() {
	if(signal_has_come) {
		signal_has_come=0;
		yield();
	}
}

int hit_count[2]; // count how many times the loop is executed, for each thread
int prev;
int switch_count;

void work(int i) {
	while(1) {
		
		// count
		hit_count[i]++;
		if(i!=prev) { prev=i; switch_count++;}
		
		// yield
		check_signal_and_yield();
		
		// fprintf(stdout,"working. %d\n", (int)pthread_self());
		
		// busy waiting
		int i;
		for(i=0; i<WAIT_COUNT; i++);
	}
}


void* thread_start(void* arg) {
	// fprintf(stdout, "new thread start. %d\n", (int)pthread_self()); fflush(stdout); 
	lock_world();
	work(1);
	return 0;
}


// exit after specified second.
void interrupt_handler(int sig) {
	fprintf(stdout, "SIGALRM. thread0: %d, thread1: %d, switch:%d\n", hit_count[0], hit_count[1], switch_count); fflush(stdout);
	exit(0);
}

// set up the stop watch
void set_stop_alarm() {
	void(*oldact)(int);
	oldact = signal(SIGALRM, interrupt_handler);
	assert(SIG_ERR!=oldact);
	struct itimerval itv;
	itv.it_value.tv_sec = EXIT_AFTER;
	itv.it_value.tv_usec = 0;
	itv.it_interval = itv.it_value;
	int r = setitimer(ITIMER_REAL, &itv, 0);
}

int main(int argc, char** argv) {
	
	// set up the stop watch
	set_stop_alarm();
	
	// set up mutex and condition variable for monitor
	int r = pthread_mutex_init(&mutex, 0);
	assert(0==r);
	r = pthread_cond_init(&cond, 0);
	assert(0==r);
	
	// start the sub thread
	pthread_t th;
	pthread_create(&th, 0, thread_start, 0);
	
	// start the tick thread
	pthread_t th2;
	pthread_create(&th2, 0, thread_tick, 0);
	
	// start the main thread
	lock_world();
	work(0);
	
}

Revision: 17740
at September 13, 2009 09:12 by keigoi


Initial Code
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <sys/time.h>

#define TIMEOUT 50000 // context switch rate, in microseconds
#define WAIT_COUNT 0x10000 // busy wait count
#define EXIT_AFTER 1  // exit after 1 sec


// monitor
pthread_mutex_t mutex;
pthread_cond_t cond;

int locked; // the `world' is locked or not
int waiters; // number of waiting threads
int signal_has_come; // 1 if context switch signal has come

// thread that fire periodic signal for context switch
void* thread_tick(void* arg) {
	while(1) {
		// fprintf(stdout, "tick\n"); fflush(stdout); 
		signal_has_come = 1;
		struct timespec to;
		to.tv_sec = 0;
		to.tv_nsec = TIMEOUT * 1000;
		nanosleep(&to, 0);
	}
}

// release the global lock
void unlock_world() {
	pthread_mutex_lock(&mutex);
	locked = 0;
	pthread_mutex_unlock(&mutex);
	// fprintf(stdout, "released global lock (%d)\n", (int)pthread_self()); fflush(stdout);
	pthread_cond_signal(&cond);
}

// obtain the global lock
void lock_world() {
	pthread_mutex_lock(&mutex);
	while(locked) {
		// fprintf(stdout, "locked. sleeping.. (%d)\n", (int)pthread_self()); fflush(stdout);
		waiters++;
		pthread_cond_wait(&cond, &mutex);
		waiters--;
	}
	locked = 1;
	pthread_mutex_unlock(&mutex);
	// fprintf(stdout,"got global lock. restart. (%d)\n", (int)pthread_self());
}

// yield to switch to the other thread
void yield() {
	if(0==waiters) return;
	unlock_world();
	sched_yield();
	lock_world();
}

void check_signal_and_yield() {
	if(signal_has_come) {
		signal_has_come=0;
		yield();
	}
}

int hit_count[2]; // count how many times the loop is executed, for each thread
int prev;
int switch_count;

void work(int i) {
	while(1) {
		
		// count
		hit_count[i]++;
		if(i!=prev) { prev=i; switch_count++;}
		
		// yield
		check_signal_and_yield();
		
		// fprintf(stdout,"working. %d\n", (int)pthread_self());
		
		// busy waiting
		int i;
		for(i=0; i<WAIT_COUNT; i++);
	}
}


void* thread_start(void* arg) {
	// fprintf(stdout, "new thread start. %d\n", (int)pthread_self()); fflush(stdout); 
	lock_world();
	work(1);
}


// exit after specified second.
void interrupt_handler(int sig) {
	fprintf(stdout, "SIGALRM. thread0: %d, thread1: %d, switch:%d\n", hit_count[0], hit_count[1], switch_count); fflush(stdout);
	exit(0);
}

// set up the stop watch
void set_stop_alarm() {
	void(*oldact)(int);
	oldact = signal(SIGALRM, interrupt_handler);
	assert(SIG_ERR!=oldact);
	struct itimerval itv;
	itv.it_value.tv_sec = EXIT_AFTER;
	itv.it_value.tv_usec = 0;
	itv.it_interval = itv.it_value;
	int r = setitimer(ITIMER_REAL, &itv, 0);
}

int main(int argc, char** argv) {
	
	// set up the stop watch
	set_stop_alarm();
	
	// set up mutex and condition variable for monitor
	int r = pthread_mutex_init(&mutex, 0);
	assert(0==r);
	r = pthread_cond_init(&cond, 0);
	assert(0==r);
	
	// start the sub thread
	pthread_t th;
	pthread_create(&th, 0, thread_start, 0);
	
	// start the tick thread
	pthread_t th2;
	pthread_create(&th2, 0, thread_tick, 0);
	
	// start the main thread
	lock_world();
	work(0);
	
}

Initial URL


Initial Description
<p>this snippet is for checking fairness of context switching in your environment.</p><p>the snippet does not runs two threads directly, but it switches two threads explicitly, by using a monitor (a mutex and a condition variable).</p><p>Some programming languges take such indirect concurrent execution mechanism  for native threads. Programming languges which support \"native threads\" but does not support conccurent GC must stop the whole process in order to collect garbages safely. Since native threads generally are not capable of disabling preemption, the runtime must pause all threads except for one that runs GC. </p><p>compile with:<br/><code>gcc -lpthread filename.c</code></p><p>there is a `global lock\' and two threads. each thread runs work() in the same way, only except its parameter. each of two threads increments the counter hit_count[i] where i is the id of the thread.</p><p>after 1 second, the program outputs statistics and terminates.</p><p><code>SIGALRM. thread0: 1899, thread1: 2333, switch:20</code></p><p>if your pthread library is fair, the first two numbers are nearly equal to each other, and the third number is 20 or so.</p><p>unfortunately, my Mac OS X (10.5.8) has turned to be unfair:</p><p><code>SIGALRM. thread0: 4086, thread1: 215, switch:2</code></p><p>on the contrary, linux (debian 5.0, kenel 2.6.26) has shown to be fair. please post your results in the comments.</p><p>The linux kernel 2.6 (>= 2.6.23) is likely to be fair, because it is equipped with the <a href=\"http://en.wikipedia.org/wiki/Completely_Fair_Scheduler\">Completely Fair Scheduler</a>. This scheduler is aware of \'sleeper fairness\'.</p><p><strong>see also:</strong><a href=\"http://snipplr.com/view/19509/\">http://snipplr.com/view/19509/</a> if your pthread is shown to be \'unfair\', this O\'Caml snippet will also stops for a while.</p><p><strong>UPDATE: </strong> added <code>volatile</code> to the shared variables.</p>

Initial Title
pthread sleeper fairness

Initial Tags


Initial Language
C