!-----------------------------------------------------------------------
! Priority-Daemons
! Release 1
! Andrew Plotkin (
[email protected])
! 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;
!--------------------------------------------------------------------------
Property daemon_priority 0;
! StartDaemonOrTimer 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.
[ StartDaemonOrTimer obj flag 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~=flag) 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=flag;
];
[ StartDaemon obj;
StartDaemonOrTimer(obj,1);
];
[ StartTimer obj timer;
if (obj.&time_left==0) TimerE2(obj);
else {
StartDaemonOrTimer(obj,2);
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 -------------------------------------------------------------