	!=====================================================================+
	! DYNPRI - Dynamically change interactive process base priorities     |
	!=====================================================================+
	! Author: Harry Flowers
	!
	! Logicals for this program:
	!
	! DYNPRI_MINPRI		Minimum priority that DYNPRI will use or
	! Default: 1		alter; processes below this point will not
	! (pri=0 ignored)	be changed, and CPU-bound processes will
	!			be lowered to this level.  Checked only
	!			at program start.
	!
	! DYNPRI_MAXPRI		Maximum priority that DYNPRI will alter;
	! Default: 4		processes above this point will not be
	! (pri>4 ignored)	changed, but this is not used as the cap
	!			for raising priorities; AUTHPRI is.  Checked
	!			only at program start.
	!
	! DYNPRI_STOP		Causes the program to stop cleanly; restores
	!			all interactive processes to their authorized
	!			base priorities before exiting.  Checked every
	!			thirty (30) seconds.
	!
	!======================================================================
	OPTION TYPE = EXPLICIT
	MARGIN 80%
	!
	! Set up system services
	EXTERNAL LONG FUNCTION	SYS$GETSYIW,			&
				SYS$PROCESS_SCAN,		&
				SYS$GETJPIW,			&
				SYS$GETTIM,			&
				SYS$SETPRI,			&
				SYS$TRNLNM,			&
				LIB$STOP,			&
				LIB$SUB_TIMES,			&
				LIB$CVTF_FROM_INTERNAL_TIME,	&
				LIB$DELETE_LOGICAL
	!
	%INCLUDE "$SSDEF" %FROM %LIBRARY "SYS$LIBRARY:BASIC$STARLET.TLB"
	%INCLUDE "$LNMDEF" %FROM %LIBRARY "SYS$LIBRARY:BASIC$STARLET.TLB"
	%INCLUDE "$JPIDEF" %FROM %LIBRARY "SYS$LIBRARY:BASIC$STARLET.TLB"
	%INCLUDE "$SYIDEF" %FROM %LIBRARY "SYS$LIBRARY:BASIC$STARLET.TLB"
	%INCLUDE "$PSCANDEF" %FROM %LIBRARY "SYS$LIBRARY:BASIC$STARLET.TLB"
	%INCLUDE "$LIBDTDEF" %FROM %LIBRARY "SYS$LIBRARY:BASIC$STARLET.TLB"
	!
	!
	! Use $GETSYI to get MAXPROCESSCNT and dimension array PROC to that
	! size by the number of data cells for each process.
	!
	!		PROC_INDEX	Index
	!		PID		PID
	!		CPUTIM		CPU time
	!		AUTHPRI		Authorized base priority
	!		PRIB		Current base priority
	!
	! Declare constants for PROC array indices
	DECLARE BYTE CONSTANT PID = 0%		! PID
	DECLARE BYTE CONSTANT CPU = 1%		! CPU time
	!
	!
	DECLARE LONG	MINPRI,		&
			MAXPRI,		&
			MAXCPU,		&
			MINCPU
	MAP(FIXED_STRINGS)		&
		STRING	MINPRIS = 2%,	&
			MAXPRIS = 2%
	!
	! Use $PROCESS_SCAN to select interactive processes and $GETJPI to get
	! the process information.
	!
	DECLARE LONG	JPI_PROC_INDEX,		&
			JPI_PID,		&
			JPI_CPUTIM,		&
			JPI_AUTHPRI,		&
			JPI_PRIB,		&
			JPI_STATE
	DECLARE BYTE CONSTANT JPI_ITEMS = 7%
	!
	RECORD ITMLST
	    GROUP ITEM(JPI_ITEMS)
		VARIANT
		    CASE
			WORD	BUFFER_LEN
			WORD	ITEM_CODE
			LONG	BUFFER_ADDR
			LONG	LENGTH_ADDR
		    CASE
			LONG	TERMINATOR
		END VARIANT
	    END GROUP
	END RECORD
	!
	DECLARE ITMLST	ITEM_LIST
	DECLARE ITMLST	PSCAN_ITEM_LIST
	DECLARE ITMLST	JPI_ITEM_LIST
	DECLARE WORD	RETLEN(JPI_ITEMS)
	DECLARE	LONG	IOSB(1%),	&
			CURTIM(1%),	&
			OLDTIM(1%),	&
			DELTIM(1%),	&
			CPUDELTA(1%)	&
	!
	DECLARE	LONG	CONTEXT,	&
			STOPPING,	&
			JPICONTROL,	&
			OPERATION,	&
			CHANGE_PRI,	&
			CPU_BOUND
	DECLARE SINGLE	CPU_SECS,	&
			CLOCK_SECS,	&
			PERCENT
	DECLARE LONG STAT, MAXIDX, NUM_CPUS, IDX
	!
	!=====================================================================
	!
	ITEM_LIST::ITEM(0%)::BUFFER_LEN	= 4%
	ITEM_LIST::ITEM(0%)::ITEM_CODE	= SYI$_MAXPROCESSCNT
	ITEM_LIST::ITEM(0%)::BUFFER_ADDR = LOC(MAXIDX)
	ITEM_LIST::ITEM(0%)::LENGTH_ADDR = LOC(RETLEN(0%))
	ITEM_LIST::ITEM(1%)::BUFFER_LEN	= 4%
	ITEM_LIST::ITEM(1%)::ITEM_CODE	= SYI$_ACTIVECPU_CNT
	ITEM_LIST::ITEM(1%)::BUFFER_ADDR = LOC(NUM_CPUS)
	ITEM_LIST::ITEM(1%)::LENGTH_ADDR = LOC(RETLEN(1%))
	ITEM_LIST::ITEM(2%)::TERMINATOR  = 0%
	!
	STAT = SYS$GETSYIW(,,,ITEM_LIST BY REF, IOSB(0%) BY REF,,)
	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
	CALL LIB$STOP(IOSB(0%) BY VALUE) IF (IOSB(0%) AND 1%) = 0%
	!
	DIMENSION LONG PROC(MAXIDX,1%)
	!
	! Get the current time
	STAT = SYS$GETTIM(OLDTIM(0%) BY REF)
	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
	!
	JPICONTROL = JPI$M_NO_TARGET_INSWAP
	OPERATION = LIB$K_DELTA_SECONDS_F
	!
	MAXPRIS = "  "
	ITEM_LIST::ITEM(0%)::BUFFER_LEN	= 2%
	ITEM_LIST::ITEM(0%)::ITEM_CODE	= LNM$_STRING
	ITEM_LIST::ITEM(0%)::BUFFER_ADDR = LOC(MAXPRIS)
	ITEM_LIST::ITEM(0%)::LENGTH_ADDR = LOC(RETLEN(0%))
	ITEM_LIST::ITEM(1%)::TERMINATOR  = 0%
	STAT = SYS$TRNLNM(,"LNM$SYSTEM_TABLE","DYNPRI_MAXPRI",,ITEM_LIST)
	IF STAT = SS$_NOLOGNAM
	THEN	MAXPRI = 4%
	ELSE	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
		MAXPRI = INTEGER(MAXPRIS)
	END IF
	MINPRIS = "  "
	ITEM_LIST::ITEM(0%)::BUFFER_LEN	= 2%
	ITEM_LIST::ITEM(0%)::ITEM_CODE	= LNM$_STRING
	ITEM_LIST::ITEM(0%)::BUFFER_ADDR = LOC(MINPRIS)
	ITEM_LIST::ITEM(0%)::LENGTH_ADDR = LOC(RETLEN(0%))
	ITEM_LIST::ITEM(1%)::TERMINATOR  = 0%
	STAT = SYS$TRNLNM(,"LNM$SYSTEM_TABLE","DYNPRI_MINPRI",,ITEM_LIST)
	IF STAT = SS$_NOLOGNAM
	THEN	MINPRI = 1%
	ELSE	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
		MINPRI = INTEGER(MINPRIS)
	END IF
	!
	! Set up PROCESS_SCAN
	PSCAN_ITEM_LIST::ITEM(0%)::BUFFER_LEN	= 0%
	PSCAN_ITEM_LIST::ITEM(0%)::ITEM_CODE	= PSCAN$_MODE
	PSCAN_ITEM_LIST::ITEM(0%)::BUFFER_ADDR	= JPI$K_INTERACTIVE
	PSCAN_ITEM_LIST::ITEM(0%)::LENGTH_ADDR	= PSCAN$M_EQL
	!
	PSCAN_ITEM_LIST::ITEM(1%)::BUFFER_LEN	= 0%
	PSCAN_ITEM_LIST::ITEM(1%)::ITEM_CODE	= PSCAN$_PRIB
	PSCAN_ITEM_LIST::ITEM(1%)::BUFFER_ADDR	= MAXPRI
	PSCAN_ITEM_LIST::ITEM(1%)::LENGTH_ADDR	= PSCAN$M_LEQ
	!
	PSCAN_ITEM_LIST::ITEM(2%)::TERMINATOR	= 0%
	!
	! Set up GETJPI
	JPI_ITEM_LIST::ITEM(0%)::BUFFER_LEN	= 4%
	JPI_ITEM_LIST::ITEM(0%)::ITEM_CODE	= JPI$_GETJPI_CONTROL_FLAGS
	JPI_ITEM_LIST::ITEM(0%)::BUFFER_ADDR	= LOC(JPICONTROL)
	JPI_ITEM_LIST::ITEM(0%)::LENGTH_ADDR	= LOC(RETLEN(0%))
	!
	JPI_ITEM_LIST::ITEM(1%)::BUFFER_LEN	= 4%
	JPI_ITEM_LIST::ITEM(1%)::ITEM_CODE	= JPI$_PROC_INDEX
	JPI_ITEM_LIST::ITEM(1%)::BUFFER_ADDR	= LOC(JPI_PROC_INDEX)
	JPI_ITEM_LIST::ITEM(1%)::LENGTH_ADDR	= LOC(RETLEN(1%))
	!
	JPI_ITEM_LIST::ITEM(2%)::BUFFER_LEN	= 4%
	JPI_ITEM_LIST::ITEM(2%)::ITEM_CODE	= JPI$_PID
	JPI_ITEM_LIST::ITEM(2%)::BUFFER_ADDR	= LOC(JPI_PID)
	JPI_ITEM_LIST::ITEM(2%)::LENGTH_ADDR	= LOC(RETLEN(2%))
	!
	JPI_ITEM_LIST::ITEM(3%)::BUFFER_LEN	= 4%
	JPI_ITEM_LIST::ITEM(3%)::ITEM_CODE	= JPI$_CPUTIM
	JPI_ITEM_LIST::ITEM(3%)::BUFFER_ADDR	= LOC(JPI_CPUTIM)
	JPI_ITEM_LIST::ITEM(3%)::LENGTH_ADDR	= LOC(RETLEN(3%))
	!
	JPI_ITEM_LIST::ITEM(4%)::BUFFER_LEN	= 4%
	JPI_ITEM_LIST::ITEM(4%)::ITEM_CODE	= JPI$_AUTHPRI
	JPI_ITEM_LIST::ITEM(4%)::BUFFER_ADDR	= LOC(JPI_AUTHPRI)
	JPI_ITEM_LIST::ITEM(4%)::LENGTH_ADDR	= LOC(RETLEN(4%))
	!
	JPI_ITEM_LIST::ITEM(5%)::BUFFER_LEN	= 4%
	JPI_ITEM_LIST::ITEM(5%)::ITEM_CODE	= JPI$_PRIB
	JPI_ITEM_LIST::ITEM(5%)::BUFFER_ADDR	= LOC(JPI_PRIB)
	JPI_ITEM_LIST::ITEM(5%)::LENGTH_ADDR	= LOC(RETLEN(5%))
	!
	JPI_ITEM_LIST::ITEM(6%)::BUFFER_LEN	= 4%
	JPI_ITEM_LIST::ITEM(6%)::ITEM_CODE	= JPI$_STATE
	JPI_ITEM_LIST::ITEM(6%)::BUFFER_ADDR	= LOC(JPI_STATE)
	JPI_ITEM_LIST::ITEM(6%)::LENGTH_ADDR	= LOC(RETLEN(6%))
	!
	JPI_ITEM_LIST::ITEM(7%)::TERMINATOR	= 0%
	!
	! Setup DYNPRI_STOP logical translation item list
	ITEM_LIST::ITEM(0%)::BUFFER_LEN	= 2%
	ITEM_LIST::ITEM(0%)::ITEM_CODE	= LNM$_LENGTH
	ITEM_LIST::ITEM(0%)::BUFFER_ADDR = LOC(RETLEN(1%))
	ITEM_LIST::ITEM(0%)::LENGTH_ADDR = LOC(RETLEN(0%))
	ITEM_LIST::ITEM(1%)::TERMINATOR  = 0%
	!
	!=====================================================================
	!
	START_LOOP:
	!
	STAT = SYS$TRNLNM(,"LNM$SYSTEM_TABLE","DYNPRI_STOP",,ITEM_LIST)
	IF STAT = SS$_NOLOGNAM
	THEN	STOPPING = 0%
	ELSE	STOPPING = -1%
	END IF
	!
	! Calculate percentages based on CPU usage
	! MAXCPU is from 5 to 99 percent (based on COM, COMO, CUR, and #CPUs)
	! MINCPU is from 2 to 49 percent (half of MAXCPU)
	CPU_BOUND = MAX(CPU_BOUND,NUM_CPUS)	! Count at least one per CPU
	MAXCPU = MAX(((50%*NUM_CPUS)/CPU_BOUND),5%)
	MINCPU = MAXCPU/2%
	!PRINT TIME$(0%)
	!PRINT "Cpu procs:";CPU_BOUND;"  MaxCPU:";MAXCPU;"  MinCPU:";MINCPU
	!
	! Get the current time
	STAT = SYS$GETTIM(CURTIM(0%) BY REF)
	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
	!
	! Subtract the last time from it
	STAT = LIB$SUB_TIMES(CURTIM(0%) BY REF, OLDTIM(0%) BY REF,	&
				DELTIM(0%) BY REF)
	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
	!
	! Convert it to seconds
	STAT = LIB$CVTF_FROM_INTERNAL_TIME(OPERATION BY REF,		&
				CLOCK_SECS BY REF, DELTIM(0%) BY REF)
	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
	!
	CONTEXT = 0%
	STAT = SYS$PROCESS_SCAN(CONTEXT BY REF,	&
	                        PSCAN_ITEM_LIST BY REF)
	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
	!
	CPU_BOUND = 0%
	STAT = 0%
	WHILE STAT <> SS$_NOMOREPROC
	!
	STAT = SYS$GETJPIW(,CONTEXT BY REF,,	&
	                   JPI_ITEM_LIST BY REF,	&
	                   IOSB(0%) BY REF,,)
	IF STAT = SS$_NOMOREPROC THEN ITERATE \ END IF
	ITERATE IF (STAT AND 1%) = 0%
	IF IOSB(0%) = SS$_NOMOREPROC THEN ITERATE \ END IF
	ITERATE IF (IOSB(0%) AND 1%) = 0%
	!
	CPU_BOUND = CPU_BOUND + 1% IF (JPI_STATE > 11%) AND (JPI_STATE < 15%)
	ITERATE IF JPI_PRIB < MINPRI	! JPI_PRIB > MAXPRI filtered by PSCAN
	IDX = JPI_PROC_INDEX
	IF PROC(IDX,PID) <> JPI_PID		! Brand new process
	THEN	PROC(IDX,PID)	= JPI_PID
		PROC(IDX,CPU)	= JPI_CPUTIM
		ITERATE				! Go get the next one
	END IF
	CPU_SECS = JPI_CPUTIM - PROC(IDX,CPU)
	PERCENT = CPU_SECS/CLOCK_SECS
	PROC(IDX,CPU) = JPI_CPUTIM
	!
	CHANGE_PRI = 0%
	IF STOPPING
	THEN	IF JPI_PRIB <> JPI_AUTHPRI
		THEN	JPI_PRIB = JPI_AUTHPRI
			CHANGE_PRI = -1%
		END IF
	ELSE	IF (PERCENT < MINCPU) AND (JPI_PRIB < MIN(MAXPRI,JPI_AUTHPRI))
		THEN	JPI_PRIB = JPI_PRIB + 1%
			CHANGE_PRI = -1%
		ELSE	IF (PERCENT > MAXCPU) AND (JPI_PRIB > MINPRI)
			THEN	JPI_PRIB = JPI_PRIB - 1%
				CHANGE_PRI = -1%
			END IF
		END IF
	END IF
	!
	STAT = SYS$SETPRI(JPI_PID BY REF,,JPI_PRIB BY VALUE,) IF CHANGE_PRI
	!
	NEXT	! STAT <> SS$_NOMOREPROC
	!
	!======================================================================
	!
	GOTO THE_END IF STOPPING
	OLDTIM(0%) = CURTIM(0%)
	OLDTIM(1%) = CURTIM(1%)
	SLEEP 30%
	GOTO START_LOOP
	!
	THE_END:
	STAT = LIB$DELETE_LOGICAL("DYNPRI_STOP","LNM$SYSTEM_TABLE")
	CALL LIB$STOP(STAT BY VALUE) IF (STAT AND 1%) = 0%
	END
