Inform code: priority-daemons


Sun, 17 Sep 1995 14:41:10 -0400

! Priority-Daemons
! Release 1
! Andrew Plotkin (erkyrath@cmu.edu)
! This file is in the public domain.

! This is a module which you can include to make the daemons and timers
! of the Inform libraries behave predictably. They will execute in the
! order defined by the "daemon_priority" property of each object in the
! daemon/timer list. Higher priorities go first; if priorities are equal,
! they execute in the order in which they were started up. The default
! daemon_priority is zero, but you can assign any numerical value to
! the property, including negative ones. However, you cannot use an
! embedded routine which returns a number, as is usual in Inform. This
! is because it would be very bad if the priority changed in mid-stream;
! priorities are only checked when a daemon starts up. For the same
! reason, you should not assign a new value to a priority property. If
! you must do it, do it while the object's daemon/timer is *not* running.
!
! These routines have one restriction that the standard library routines
! do not: It is illegal to call StartDaemon or StartTimer from inside a
! daemon or timer routine, if the daemon you're starting has a priority
! greater than or equal to the calling daemon. It *is* legal to start a
! daemon/timer of lower priority (and that daemon will be called for the
! first time that same turn.) It is also legal to call StopDaemon or
! StopTimer at any time.
!
! To use this thing, include this file after you include the Inform
! "parser" file. Before the "parser" include, drop in these four lines
! (uncommented):
!
! Replace StartDaemon;
! Replace StartTimer;
! Replace StopDaemon;
! Replace StopTimer;

! Here's the code:

! Define the property.
Property daemon_priority 0;

! StartDaemon inserts the given object in the_timers list, at the position
! defined by its daemon_priority. That is, the object is inserted after all
! objects with a greater-or-equal priority, and before all objects with a
! lesser priority.
! This is not complicated, just a little ugly.

[ StartDaemon obj i j;
! check to make sure it's not already running
for (i=0:i<active_timers:i++)
if (the_timers-->i==obj)
{ if (timer_flags->i==1) TimerE3(obj);
rfalse;
}

! find a slot
j = (-1);
for (i=0:i<active_timers:i++) {
if (the_timers-->i ~= 0
&& (the_timers-->i).daemon_priority >= obj.daemon_priority) {
j = i;
}
if (the_timers-->i ~= 0
&& (the_timers-->i).daemon_priority < obj.daemon_priority) {
break;
}
}
! we must now put obj between j (-1 .. a_t-1) and i (0 .. a_t). Both
! are objects (not empty spaces.) j has >= priority, i has < priority.
! If there's a blank space between them, that's perfect.
! Otherwise we'll have to shift i upwards.
j++;
while (the_timers-->j ~= 0 && j < i)
j++;
if (j < i) {
! j is open. Use it.
i = j;
}
else {
! We'll use j, but we have to move i up first.
while (i < active_timers && the_timers-->i ~= 0)
i++;
if (i == active_timers) {
if (active_timers*2 >= MAX_TIMERS) {
TimerE();
return;
}
active_timers++;
}
while (i > j) {
the_timers-->i = the_timers-->(i-1);
timer_flags->i = timer_flags->(i-1);
i--;
}
}
! i is now set correctly (in both cases.)

the_timers-->i=obj; timer_flags->i=2;
];

! StartTimer is exactly parallel to StartDaemon; see above for comments.

[ StartTimer obj timer i j;
for (i=0:i<active_timers:i++)
if (the_timers-->i==obj)
{ if (timer_flags->i==2) TimerE3(obj);
rfalse;
}

j = (-1);
for (i=0:i<active_timers:i++) {
if (the_timers-->i ~= 0
&& (the_timers-->i).daemon_priority >= obj.daemon_priority) {
j = i;
}
if (the_timers-->i ~= 0
&& (the_timers-->i).daemon_priority < obj.daemon_priority) {
break;
}
}
j++;
while (the_timers-->j ~= 0 && j < i)
j++;
if (j < i) {
i = j;
}
else {
while (i < active_timers && the_timers-->i ~= 0)
i++;
if (i == active_timers) {
if (active_timers*2 >= MAX_TIMERS) {
TimerE();
return;
}
active_timers++;
}
while (i > j) {
the_timers-->i = the_timers-->(i-1);
timer_flags->i = timer_flags->(i-1);
i--;
}
}

if (obj.&time_left==0) TimerE2(obj);
the_timers-->i=obj; timer_flags->i=1; obj.time_left=timer;
];

! StopTimer and StopDaemon are exactly the same as in the standard
! library (from release 8 up to at least release 11.) I include them
! here just in case you're using a really old (or distant future)
! library.

[ StopTimer obj i;
for (i=0:i<active_timers:i++)
if (the_timers-->i==obj) jump FoundTSlot2;
rfalse;
.FoundTSlot2;
if (obj.&time_left==0) TimerE2(obj);
the_timers-->i=0; obj.time_left=0;
];

[ StopDaemon obj i;
for (i=0:i<active_timers:i++)
if (the_timers-->i==obj) jump FoundTSlot4;
rfalse;
.FoundTSlot4;
the_timers-->i=0;
];

! end of file ---------------------------------------

"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the borogoves..."