/************************************************************************* * * (c) 1996 California Institute of Technology * Department of Computer Science * Pasadena, CA 91125. * All Rights Reserved * * Author: Rajit Monohar * Last Modified: 17 May 2003 * History: * - modified for Linux 2.x.x (tested on 2.4.20), kko * - see context_disable(), as a consequence uses * sigprocmask() to be more POSIX happy, kko * *************************************************************************/ #include #include #include #include "contexts.h" struct process_record { context_t c; }; /* current process */ process_t *current_process = NULL; static process_t *terminated_process = NULL; static struct itimerval tm; static struct itimerval old_tm; static struct itimerval saved_tm; static int unfair = 0; static sigset_t vtalrmset; /* * Initialize timer data structure */ static void context_internal_init (void) { static int first = 1; if (first) { first = 0; sigemptyset (&vtalrmset); sigaddset (&vtalrmset, SIGVTALRM); sigprocmask (SIG_BLOCK, &vtalrmset, NULL); signal (SIGVTALRM, context_timeout); tm.it_interval.tv_sec = 0; tm.it_interval.tv_usec = 100; tm.it_value.tv_sec = 0; tm.it_value.tv_usec = 100; saved_tm.it_interval.tv_sec = tm.it_interval.tv_sec; saved_tm.it_interval.tv_usec = tm.it_interval.tv_usec; saved_tm.it_value.tv_sec = tm.it_value.tv_sec; saved_tm.it_value.tv_usec = tm.it_value.tv_usec; setitimer (ITIMER_VIRTUAL, &tm, &old_tm); } } void context_unfair (void) { unfair = 1; context_internal_init (); old_tm.it_interval.tv_sec =0; old_tm.it_interval.tv_usec =0; old_tm.it_value.tv_sec = 0; old_tm.it_value.tv_usec = 0; setitimer (ITIMER_VIRTUAL, &old_tm, &saved_tm); } /* * Reset process time-slice */ void context_reset_timer (void) { setitimer (ITIMER_VIRTUAL, &tm, &old_tm); } /* * Disable timer interrupts: must be called with interrupts enabled. * * - this is inherently unfair, since the timers keep ticking. * unfortunately, tv_usec resolution is actually > 100us under i386 * Linux circa 2.4.* kernel series (see jiffies to tv conversion in * itimer.c, viz. HZ). * * Originally, Rajit used getitimer() to save the timer and restored * it under context_enable() with a call to setitimer(). I don't * think this works because a call to getitimer() can return a * saved_tm with saved_tm.it_value.tv_usec > (the initial) 100 due to * above conversion issues. The natural thing then would be to upper * bound this value. However, this would raise issues with subsequent * calls to setitimer() as it would simply mimic a * context_reset_timer() call, destroying any possibility of a * SIGVTALRM signal => context_timeout() would never occur. * * As a quick remedy, I just mask out the timers and let them reset on * their own. [kko] */ void context_disable (void) { context_internal_init (); sigprocmask (SIG_BLOCK, &vtalrmset, NULL); } /* * Enable timer interrupts: must be called with interrupts disabled. * * - Note comments for context_disable(). [kko] */ void context_enable (void) { if (!unfair) { context_internal_init (); sigprocmask (SIG_UNBLOCK, &vtalrmset, NULL); } } /* * Called with interrupts disabled. Enables interrupts on termination. */ void context_switch (process_t *p) { if (!current_process || !_setjmp (current_process->c.buf)) { current_process = p; context_reset_timer (); _longjmp (p->c.buf,1); } if (terminated_process) { free (terminated_process->c.stack); context_destroy (terminated_process); terminated_process = NULL; } context_enable (); } /* * Called with interrupts disabled; * Enables interrupts on exit. * */ static void context_stub (void) { context_internal_init (); if (terminated_process) { free (terminated_process->c.stack); context_destroy (terminated_process); terminated_process = NULL; } context_reset_timer (); context_enable (); (*current_process->c.start)(); context_disable (); terminated_process = current_process; /* * It would be nice to delete the process here, but we can't do that * because we're deleting the stack we're executing on! Some other * process must free our stack. */ context_switch (context_select ()); } /* * Non-portable code */ void context_init (process_t *p, void (*f)(), int n) { void *stack; if (n <= 0) n = DEFAULT_STACK_SIZE; stack = p->c.stack = (char *)malloc(sizeof(char)*n); if (!stack) { printf ("MALLOC failed, out of stack space.\n"); exit (1); } p->c.start = f; _setjmp (p->c.buf); /* SPARC SunOS 4.1.x p->c.buf[3] = p->c.buf[4] = (int)context_stub; p->c.buf[2] = (int)((double*)stack + n/sizeof(double)-11); */ /* i386 NetBSD 1.1 p->c.buf[0] = (int)context_stub; p->c.buf[2] = (int)((char*)stack+n-4); */ /* ??? Linux p->c.buf[0].__pc = (__ptr_t)context_stub; p->c.buf[0].__sp = (__ptr_t)((char*)stack+n-4); */ /* i386 Linux 2.4.x */ p->c.buf[0].__jmpbuf[JB_PC] = (__ptr_t)context_stub; p->c.buf[0].__jmpbuf[JB_SP] = (__ptr_t)((char*)stack+n-4); }