|  | /* 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> | 
|  | #include "misc-compat.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)); | 
|  | } |