/* The following program provides an example of using the getcontext(2), setcontext(2) and makecontext(3C) functions introduced in IRIX 5.x. These functions are useful for implementing user level context switching between multiple threads of control within a single IRIX process. Each thread's context is defined in ucontext(5). A thread's context is maintained and created by the operating system primarily because it includes the signal mask (which is maintained by the operating system). Compile this example as: cc context1.c -o context1 You must supply 3 numerical parameters: context1 a b c a = Number of threads to create and execute. b = Time slice for each thread, how many milliseconds a thread will be allowed to execute before another thread is allowed to run. These units must be in units of 10. c = Number of loop iterations to perform, per thread, in the workfunc() function. Sample execution on a 100 MHZ IP20 processor, IRIX 5.3: ./context1 13 20 1000000 thread number '3' finished first total number of dispatches '131' thread num_loops ------------------ 0 945927 1 943358 2 933348 3 1000000 4 940210 5 937799 6 964359 7 956506 8 962444 9 966233 10 962684 11 965896 12 968186 */ #include #include #include #include #include #include #include #define QWORD 16 /* bytes per Quadword */ #define DSTKSIZ 1024*QWORD /* Align stack to QWORD boundary */ int num_thrds; /* number of threads to create */ long looptims; /* times to loop in work loop */ int num_disp; /* current number of dispatches */ int disp_cycle; /* current dispatch cycle */ clock_t tslice_ms; /* time slice per thread in milliseconds */ int cte; /* current thread executing */ int nttd; /* next thread to dispatch */ int done_early; /* indicates if program terminated with control-C */ ucontext_t *ca; /* global context array, used by all threads */ ucontext_t when_done; /* context used when a thread completes all work */ long *gblary; /* work loop iteration array, 1 entry per thread */ /* This function is called when the first thread completes its loop execution in the workfunc function. */ void all_done(void) { int i; if (done_early) { printf("\nprogram terminated before any threads completed\n\n"); printf("below are current loop iterations for each thread\n"); } else { printf("\nthread number '%d' finished first\n",nttd); printf("total number of dispatches '%d'\n\n",num_disp); } printf("thread num_loops\n"); printf("------------------\n"); for(i=0;iss_sp = (char *)memalign(QWORD,size+QWORD)) == NULL) { perror("malloc"); exit(-1); } /* Initialize stack environment within context structure of thread. */ uc_stack->ss_sp += size+QWORD; uc_stack->ss_size = size; uc_stack->ss_flags = 0; } /* This is the initialization function. It allocates space for num_thrds ucontext_t types; allocates space for global array; sets up a context for each thread to execute. */ void init1(ucontext_t *when_done, void *workfunc) { int i; /* Allocate space for num_thrds ucontext_t types. */ if ((ca = (ucontext_t*)calloc(num_thrds,sizeof(ucontext_t)))==NULL) { perror("calloc failed\n"); exit(-1); } /* Allocate space for global array, 1 entry per thread (num_thrds). */ if ((gblary = (long *)calloc(num_thrds,sizeof(long)))==NULL) { perror("calloc failed\n"); exit(-1); } /* Setup context (a function's execution entry point) in case one of the threads complete its work before SIGINT is sent to the process. Allocate a stack, too. The function all_done will be invoked if any thread completes its work before SIGINT is sent to the process. */ if (getcontext(when_done)!=0) { perror("getcontext 1 failed"); exit(-1); } makestack(DSTKSIZ, &when_done->uc_stack); when_done->uc_link = NULL; makecontext(when_done,all_done,0); /* Setup contexts for num_thrds threads. Allocate a stacks, too. The uc_link field is pointed at the context that will be invoked if any thread completes its work before SIGINT is sent to the process. */ for(i=0;i