#ifndef lint static char sccsid[] = "@(#)winlock.c 1.1 94/10/31 SMI"; #endif /* * Copyright 1987-1989 Sun Microsystems, Inc. */ /* * SunWindows locking primitives. */ #include extern int ws_check_lock; /* Check locks every #of hz if pid */ extern int ws_no_pid_check_lock; /* Check locks every #of hz if no pid */ int ws_set_pri; /* Reschedule every time set priority */ int ws_set_nice = 0; /* Set nice every time lock */ int ws_nice = NZERO; /* Nice used when ws_set_nice */ extern struct timeval ws_check_time; /* Check locks every this # usec */ int ws_set_favor = 0; /* Control favoring feature */ /* * ws_lock_limit is the process virtual time limit for a data or display * lock. The check for this amount of virtual time doesn't start for * wlock->lok_max_time amount of real time after the lock is set. This is * done to avoid the overhead of mapping the user structure for normal * short lock intervals. */ #if defined(AT386) #define WS_LOCK_LIMIT_SEC 10 /* Break locks in this # of seconds */ #else #define WS_LOCK_LIMIT_SEC 2 /* Break locks in this # of seconds */ #endif #define WS_LOCK_LIMIT_USEC 0 /* plus this # of micro seconds */ struct timeval ws_lock_limit = {WS_LOCK_LIMIT_SEC, WS_LOCK_LIMIT_USEC}; /* * Generic access control */ int wlok_waitunlocked(wlock) Winlock *wlock; { while ((wlock->lok_flags) && (*wlock->lok_flags & wlock->lok_bit) && (wlock->lok_pid != u.u_procp->p_pid)) if (sleep((caddr_t)wlock->lok_wakeup, LOCKPRI|PCATCH)) return (-1); return (0); } wlok_running_check(wlock) Winlock *wlock; { char stat; /* lok_proc may not be set yet */ if (!wlock->lok_proc) return; stat = wlock->lok_proc->p_stat; if ((stat != SRUN && stat != SSLEEP) || (wlock->lok_proc->p_pid != wlock->lok_pid)) { if (!(wlock->lok_options & WLOK_SILENT)) printf( "Window %s lock broken as process %D not running\n", (wlock->lok_string) ? wlock->lok_string : "Null", wlock->lok_pid); wlok_forceunlock(wlock); } } /*ARGSUSED*/ wlok_lockcheck(wlock, poll_rate, charge_sleep_against_limit) Winlock *wlock; int poll_rate; int charge_sleep_against_limit; { extern struct proc *pfind(); register struct proc *process; register int pid; struct timeval utime, stime; wlock->lok_time += poll_rate; if (wlock->lok_time <= wlock->lok_max_time) return; pid = wlock->lok_pid; /* See if process still exists */ process = pfind(pid); if (process == (struct proc *)0) { /* Process does not exist */ if (!(wlock->lok_options & WLOK_SILENT)) printf( "Window %s lock broken after process %D went away\n", (wlock->lok_string) ? wlock->lok_string : "Null", pid); goto force; } /* See if process is blocked on an interruptible device */ if ((process->p_stat == SSLEEP) && (process->p_pri >= PZERO) && !(process->p_flag & SRPC)) { if (!charge_sleep_against_limit) { /* Only print message if not being traced (debugged) */ if ((process->p_stat != SSTOP) && (!(wlock->lok_options & WLOK_SILENT))) printf( "Window %s lock broken as process %D blocked\n", wlock->lok_string ? wlock->lok_string : "Null", pid); goto force; } /* * Don't start timing process until gets loaded * into core (see SLOAD below). */ if (process->p_flag & SLOAD) { /* * Subtract ws_check_time from limit to give * affect of charging as real time elasped. */ if (timercmp((&wlock->lok_limit), (&ws_check_time), >)) { timevalsub(&wlock->lok_limit, &ws_check_time); } else { timerclear(&wlock->lok_limit); } } } if ((process->p_stat != SSLEEP) && (process->p_stat != SRUN)) { if (!(wlock->lok_options & WLOK_SILENT)) printf( "Window %s lock broken as process %D not running\n", (wlock->lok_string) ? wlock->lok_string : "Null", pid); goto force; } /* * Don't start timing process until gets loaded into core. */ if ((~process->p_flag) & SLOAD) goto done; else /* * We may have set the SULOCK bit already but go ahead and * do it again. When it finally gets into core then it should * stay put. * * There is a 2.0 bug in which the scheduler can turn off the * SULOCK. If this happens then we just turn it on again. */ process->p_flag |= SULOCK; /* * Do the process virtual time tracking. */ if (process->p_uarea == (struct user *)0) { if (!(wlock->lok_options & WLOK_SILENT)) printf( "Window %s lock broken as process %D uarea 0\n", (wlock->lok_string) ? wlock->lok_string : "Null", pid); goto force; } utime = wlock->lok_utime = process->p_uarea->u_ru.ru_utime; stime = wlock->lok_stime = process->p_uarea->u_ru.ru_stime; if ((!timerisset(&wlock->lok_utime) || !timerisset(&wlock->lok_stime))) goto done; /* Subtract saved time from current time */ timevalsub(&utime, &wlock->lok_utime); timevalsub(&stime, &wlock->lok_stime); /* See if combined time exceeds threshold */ timevaladd(&utime, &stime); if (wlock->lok_timeout_action && ((!timerisset(&wlock->lok_limit)) || timercmp((&utime),(&wlock->lok_limit), >))) { /* Lock timed out */ if (!(wlock->lok_options & WLOK_SILENT)) printf( "Window %s lock broken: time exceeded by pid %D\n", wlock->lok_string, pid); wlock->lok_timeout_action(wlock); goto force; } done: /* Reset time until next check */ wlock->lok_time = 0; return; force: wlok_forceunlock(wlock); return; } wlok_forceunlock(wlock) Winlock *wlock; { if (wlock->lok_count) *(wlock->lok_count) = 1; if (wlock->lok_force_action) wlock->lok_force_action(wlock); wlok_unlock(wlock); } /* Most callers will use u.u_procp for proc */ wlok_setlock(wlock, flags, lockbit, lock_count, process) register Winlock *wlock; int *flags; int lockbit; int *lock_count; register struct proc *process; { /* * If the lock bit is already set, assume that whoever set it * will set the count also. */ if (!(*flags & lockbit)) { *lock_count = 1; } *flags |= lockbit; wlock->lok_flags = flags; wlock->lok_bit = lockbit; wlock->lok_count = lock_count; wlock->lok_time = 0; wlock->lok_limit = ws_lock_limit; /* Default, can override */ if (process) { wlock->lok_max_time = ws_check_lock*2; wlock->lok_pid = process->p_pid; wlock->lok_proc = process; /* Pin process so wouldn't swap (only if currently loaded) */ if (process->p_flag & SLOAD) process->p_flag |= SULOCK; if (ws_set_nice) { /* Boost priority */ wlock->lok_nice = process->p_nice; process->p_nice = ws_nice; if (ws_set_pri) (void) setpri(process); } } else wlock->lok_max_time = ws_no_pid_check_lock; /* Will set up rest of structure when first check lock */ } wlok_unlock(wlock) register Winlock *wlock; { register struct proc *process; caddr_t wakeup_handle; if ((wlock->lok_flags) && (*wlock->lok_flags & wlock->lok_bit)) { if (!(wlock->lok_count) || (*(wlock->lok_count) <= 1)) { #ifdef WINDEVDEBUG if (!(wlock->lok_count)) printf("Warning: window lock count pointer NULL\n"); #endif /* Reset lock state */ *wlock->lok_flags &= ~wlock->lok_bit; /* * Reset process if process ids match and no other * locks outstanding (they all should be on the same * pid). */ process = wlock->lok_proc; if (process && (process->p_pid == wlock->lok_pid)) { /* see if this process has any other locks */ if (!wlock->lok_other_check || !(wlock->lok_other_check)(wlock->lok_client)) { if (ws_set_nice) { process->p_nice = wlock->lok_nice; if (ws_set_pri) (void) setpri(process); } process->p_flag &= ~SULOCK; } } /* Do lock specific action */ if (wlock->lok_unlock_action) wlock->lok_unlock_action(wlock); /* * Zero wlock after noting who to wakeup, * but preserve user_addr. */ wakeup_handle = wlock->lok_wakeup; wlok_clear(wlock); wakeup((caddr_t)wakeup_handle); } else *(wlock->lok_count) -= 1; } } wlok_clear(wlock) register Winlock *wlock; { /* * Zero wlock but preserve user_addr. */ if (wlock->lok_count) *(wlock->lok_count) = 0; bzero((caddr_t)wlock, sizeof (Winlock)); } /* * Ws_favor will boost w->w_pid's favor if ws->ws_favor_pid is not * equal to w->w_pid. A null w means that no process is in favor; * ws->ws_favor_pid will be zeroed in this case. */ ws_favor(ws, w) register Workstation *ws; register Window *w; { if (w == WINDOW_NULL) { ws->ws_favor_pid = 0; } else { if ((w->w_pid != ws->ws_favor_pid) && ws_set_favor) { favor_pid_and_children(w->w_pid); ws->ws_favor_pid = w->w_pid; } } } /* * Process pid_to_favor now is the interactive process. * Adjust its favor and its children's favor up. * Adjust old process(es) (if any) favor down. * * The correct way to do this is to recursively favor all the children * of the tool that has the input focus. But we know that the interesting * case is the C shell and its children. Csh makes each of its children * a process group leader, and their children in turn keep that process * group. It would be nice if we knew which was the foreground process * group, but the controlling pty is pointed by the u area which is not * only hard to get to, it may be swapped out. So we punt and do all * the children. */ #define FAVOR(p) ((p)->p_flag |= SFAVORD) #define UNFAVOR(p) ((p)->p_flag &= ~SFAVORD) #define ISFAVORED(p) ((p)->p_flag & SFAVORD) #ifdef WINDEVDEBUG int favor_debug; #endif static favor_pid_and_children(pid_to_favor) int pid_to_favor; { register struct proc *p, *pp; int w_pid, sh_pid, pgrp; p = pfind(pid_to_favor); #ifdef WINDEVDEBUG if (favor_debug) printf("%D favored (%X)\n", pid_to_favor, p); #endif if (p == 0) { return; } FAVOR(p); w_pid = p->p_pid; sh_pid = 0; for (p = allproc; p != NULL; p = p->p_nxt) { /* * Take away niceness from processes that got * it before. */ if (ISFAVORED(p) && p->p_pid != w_pid) { UNFAVOR(p); } /* * Check for direct child of window process. */ if (p->p_ppid == w_pid) { FAVOR(p); sh_pid = p->p_pid; } } if (sh_pid == 0) { return; } /* * Assuming each child of the shell is a process group leader, * do each process group. * XXX inefficient O(nproc**2) algorithm. Should also use child * pointer (cptr) stuff to get children. */ for (p = allproc; p != NULL; p = p->p_nxt) { if (p->p_ppid == sh_pid) { /* child of shell */ if (!ISFAVORED(p)) { /* got it already? */ FAVOR(p); /* no, favor it and pgrp */ pgrp = p->p_pgrp; for (pp = allproc; pp != NULL; pp = p->p_nxt) { if (pp->p_pgrp == pgrp) { FAVOR(pp); } } } } } }