2021-10-11 18:37:13 -03:00

417 lines
10 KiB
C

#ifndef lint
static char sccsid[] = "@(#)winlock.c 1.1 94/10/31 SMI";
#endif
/*
* Copyright 1987-1989 Sun Microsystems, Inc.
*/
/*
* SunWindows locking primitives.
*/
#include <sunwindowdev/wintree.h>
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);
}
}
}
}
}
}