| /* Copyright (c) 2014 The Regents of the University of California | 
 |  * Barret Rhoden <brho@cs.berkeley.edu> | 
 |  * See LICENSE for details. | 
 |  * | 
 |  * Basic pthread switcher, bypassing the 2LS.  Use for benchmarking and | 
 |  * 2LS-inspiration. */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <pthread.h> | 
 | #include <stdlib.h> | 
 | #include <unistd.h> | 
 | #include <sys/time.h> | 
 |  | 
 | pthread_t th1, th2; | 
 | int nr_switch_loops = 100; | 
 | pthread_barrier_t barrier; | 
 | bool should_exit = FALSE; | 
 |  | 
 | static void __pth_switch_cb(struct uthread *uthread, void *target) | 
 | { | 
 | 	/* by not returning, this bypasses vcore entry and event checks, though | 
 | 	 * when we pop back out of the 2LS, we'll check notif pending.  think | 
 | 	 * about this if you put this into your 2LS. */ | 
 | 	current_uthread = NULL; | 
 | 	run_uthread((struct uthread*)target); | 
 | 	assert(0); | 
 | } | 
 |  | 
 | static void pth_switch_to(struct pthread_tcb *target) | 
 | { | 
 | 	uthread_yield(TRUE, __pth_switch_cb, target); | 
 | } | 
 |  | 
 | void *switch_thread(void *arg) | 
 | {	 | 
 | 	pthread_t other_thr = *(pthread_t*)arg; | 
 |  | 
 | 	pthread_barrier_wait(&barrier); | 
 | 	for (int i = 0; i < nr_switch_loops; i++) { | 
 | 		cmb(); | 
 | 		if (should_exit) | 
 | 			return 0; | 
 | 		pth_switch_to(other_thr); | 
 | 	} | 
 | 	if (pthread_self() == th1) { | 
 | 		/* we need to break out of the switching cycle.  when th2 runs | 
 | 		 * again, it'll know to stop.  but th1 needs to both exit and | 
 | 		 * switch to th2.  we do this by making th2 runnable by the pth | 
 | 		 * schedop, then exiting */ | 
 | 		should_exit = TRUE; | 
 | 		/* we also need to do this to th2 before it tries to exit, o/w | 
 | 		 * we'll PF in __pthread_generic_yield. */ | 
 | 		sched_ops->thread_runnable((struct uthread*)th2); | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | int main(int argc, char** argv)  | 
 | { | 
 | 	struct timeval start_tv = {0}; | 
 | 	struct timeval end_tv = {0}; | 
 | 	long usec_diff; | 
 | 	long nr_ctx_switches; | 
 | 	void *join_ret; | 
 |  | 
 | 	if (argc > 1) | 
 | 		nr_switch_loops = strtol(argv[1], 0, 10); | 
 | 	printf("Making 2 threads of %d switches each\n", nr_switch_loops); | 
 |  | 
 | 	parlib_never_yield = TRUE; | 
 | 	parlib_never_vc_request = TRUE; | 
 | 	pthread_need_tls(FALSE); | 
 | 	pthread_mcp_init();		/* gives us one vcore */ | 
 |  | 
 | 	pthread_barrier_init(&barrier, NULL, 2); | 
 | 	/* each is passed the other's pthread_t.  th1 starts the switching. */ | 
 | 	if (pthread_create(&th1, NULL, &switch_thread, &th2)) | 
 | 		perror("pth_create 1 failed"); | 
 | 	/* thread 2 is created, but not put on the runnable list */ | 
 | 	if (__pthread_create(&th2, NULL, &switch_thread, &th1)) | 
 | 		perror("pth_create 2 failed"); | 
 |  | 
 | 	if (gettimeofday(&start_tv, 0)) | 
 | 		perror("Start time error..."); | 
 |  | 
 | 	pthread_join(th1, &join_ret); | 
 | 	pthread_join(th2, &join_ret); | 
 |  | 
 | 	if (gettimeofday(&end_tv, 0)) | 
 | 		perror("End time error..."); | 
 | 	nr_ctx_switches = 2 * nr_switch_loops; | 
 | 	usec_diff = (end_tv.tv_sec - start_tv.tv_sec) * 1000000 + | 
 | 	            (end_tv.tv_usec - start_tv.tv_usec); | 
 | 	printf("Done: %d loops\n", nr_switch_loops); | 
 | 	printf("Nr context switches: %ld\n", nr_ctx_switches); | 
 | 	printf("Time to run: %ld usec\n", usec_diff); | 
 | 	printf("Context switch latency: %d nsec\n", | 
 | 	       (int)(1000LL*usec_diff / nr_ctx_switches)); | 
 | 	printf("Context switches / sec: %d\n\n", | 
 | 	       (int)(1000000LL*nr_ctx_switches / usec_diff)); | 
 | }  |