release 0.0.0.20 and thoughts
spc@gate.net
spc@gate.net
Thu, 17 Aug 1995 01:52:07 -0400 (EDT)
WARNING! This is quite long, and includes sample code. You have been
warned.
On some network somewhere in cyberspace, Billy Tanksley transmitted:
>
> > > >From my point of view, it is. If you force virtual memory on everything,
> > > many things will have trouble. If you only provide virtual memory as a
> > > service to things that need it, nothing will break. (Simplified case.
> > > Murphy's law may apply otherwise in some finite states.)
>
> > Care to name what virtual memory causes troubles with? About the only
> > argument I can think of is that under certain systems, virtual memory
> > comsumes space for the tranlation tables (not all system have this trouble,
> > notably those where the virtual memory is handled externally to the CPU) and
> > are *slightly* slower than non-virtual memory systems (I'm excluding
> > swapping out to disk here in this case).
>
> Virtual memory includes swapping to disk. That's the meaning of
> virtual. If you mean memory protection...
>
Okay. But still can you answer the question? This time with BOTH memory
protection AND virtual memory.
> > > Okay. I'll accept that. Do you accept my point, that what we can
> > > discover about this does have a certain amount of utility?
>
> > I'll accept that.
>
> Then it is clear that there is some benifit in proving such things about
> it, and thereby reducing the supervisory burden on the system.
>
> Actually, the total amount of supervision is the same; the difference is
> that by proving those things about the task the system can ignore those
> aspects of the task during runtime. You lose a constant amount of time
> at startup, you gain a linear amout of time during running.
>
Okay, that I'll buy.
> > > > > As I understand it, the reverse is also true. Coop multi does not
> > > > > prevent an object from preempting its own decendants.
>
> > > > Explain how that is done.
>
> > > Cooperative multitasking, simply defined, is allowing tasks to return
> > > control on their own time rather than taking it away from them at any
> > > time.
>
> > Right.
>
> > > If computing time is distributed to objects through some sort of tree,
> > > with the processor being the trunk, you can easily define a 'branch' of
> > > that tree that takes back control from all its decendants at fixed
> > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > > intervals.
> > ^^^^^^^^^^
>
> > Thank you for defining preemptive multitasking. So kind of you.
>
> Sarcasm will get us nowhere.
>
Sorry, but I didn't notice the (very) subtle shift in what you were doing.
> My point! You can implement preemptive multitasking for a subset of a
> cooperative system.
>
> I'm saying that cooperative multitasking is more flexible than preemptive
> multitasking. See my above statement for what coop can do, and notice
> that on the other hand, preemptive cannot ever fail to preempt or it
> completely fails.
>
That last bit about preemptive cannot ever fail seems redundant to me.
But to impliment preemptive multitasking within a cooperative multitasking
system there has to be some facility to interrupt a currently running
execution unit, else you can't preempt it! And if this isn't the case, then
you very well CAN'T do preemptive multitasking. Well, you can, but it'll be
a horrible kludge.
But I contend that preemptive multitask is more flexible than cooperative
because you CAN do cooperative multitasking in a preemptive multitasking
system. And to prove it, I'll do it!
The following is demonstration code that impliments a cooperative
multitasking system (must like the joyous people at TUNES want) withing a
preemptive multiask system, in this case, the Amiga. Sorry, but I resorted
to low level C and Assembly to do this, and I was able to do this using only
one (1) Exec (kernel) hack to keep Exec in sync with the currently running
cooperative task (and with some rewriting, even this can be done away with).
This done in 71 lines of C code and 112 lines of Assembly. Any routines you
don't see code for are system routines.
The code creates six cooperative tasks within the framework of the
preemptive system. To make it simulate a complete cooperative system,
uncomment the calls to Forbid() and Permit().
WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
Due to the implimentation of IO under AmgiaDOS, you cannot do IO with the
code as it stands right now. Even under the full preemptive system, there
are restrictions as to what form of IO tasks can do. This code is for
demonstrating that coooperative multitasking CAN BE DONE IN THE MANNER THAT
TUNES CURRENTLY DEFINES IT is possible. Doing IO is left as an exercise for
the reader.
WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING! WARNING!
-spc (not bad for some two hours work 8-)
--------------[ C code ]----------------------------------------------
/* include any headers required */
struct List tasks_run;
struct List tasks_wait;
#define Yield() (*defield)()
void (*defield)(void) = NullYield;
#define STACKSIZE 4096
#define INITSTACK STACKSIZE-4
void TaskStart(void (*r)(void),char *name)
{
struct Task *pt;
unsigned long *pl;
pt = AllocMem(sizeof(struct Task),MEMF_PUBLIC | MEMF_CLEAR);
pt->tc_SPLower = AllocMem(STACKSIZE,MEMF_CLEAR);
pt->tc_SPUpper = STACKSIZE + pt->tc_SPLower;
pt->tc_SPReg = INITSTACK + pt->tc_SPLower;
pt->tc_Node.ln_Type = NT_TASK;
pt->tc_Node.ln_Pri = 0; /* not used here currently */
pt->tc_Node.ln_Name = name;
TaskRun(pt,r);
AddHead(&tasks_run,pt);
}
int timer_irq(void)
{
static unsigned int timeslice = 0;
struct Task *ptnext;
if ((timeslice++ & 0x0001) == 0)
{
ptnext = RemHead(&tasks_run);
if (ptnext == NULL) return(0);
AddHead(&tasks_run,ptnext);
AddTail(&tasks_run,FindTask(NULL));
defyield = FullYield;
}
return(0);
}
void task(void)
{
unsigned long l;
for(l = 0 ; l < ULONG_MAX ; l++ , Yield())
{
}
}
int main(void)
{
InitList(&tasks_run);
InitList(&tasks_wait);
AddIntServer(INTB_VERTB,timer_irq);
/* Disable(); /* uncomment to disable multitasking system wide */
TaskStart(task,"Coop task 1");
TaskStart(task,"Coop task 2");
TaskStart(task,"Coop task 3");
TaskStart(task,"Coop task 4");
TaskStart(task,"Coop task 5");
TaskWait(); /* wait for 5 tasks to finish */
/* Enable(); /* uncomment to enable multitasking system wide */
RemIntServer(INTB_VERTB,timer_irq);
return(0);
}
------------[ 68K Assembly Code ]--------------------------------
; include any headers required
;-------------------------------------------------------------------
public _NullYield
public _FullYield
public _TaskEnd
public _TaskRun
public _defyield
public _tasks_run
public _tasks_wait
;-------------------------------------------------------------------
;*******************************************************************
section _code,code
_NullYield rts
_FullYield movem.l a2-a6/d2-d7,-(a7) ;save state info
move.l ExecBase,a6
jsr _LVODisable(a6) ;disable IRQs
move.l #_NullYield,_defyield
sub.l a1,a1
jsr _LVOFindTask(a6)
move.l d0,a1
move.l a7,TC_SPREG(a1) ;save stack pointer
move.l #tasks_run,a0
jsr _LVOAddTail(a6) ;put back into run queue
taskkickoff move.l #tasks_run,a0 ;get next task to run
jsr _LVORemHead(a6) ;should always return valid
move.l d0,a0
move.l TC_SPREG(a0),a7 ;restore stack pointer
move.l a0,ThisTask(a6) ;hack to keep Exec in sync
jsr _LVOEnable(a6) ;enable IRQs
movem.l (a7)+,a2-a6/d2-d7 ;restore state info
rts
_TaskEnd move.l ExecBase,a6
jsr _LVODisable(a6) ;disable IRQs
sub.l a1,a1
jsr _LVOFindTask(a6)
move.l d0,a2 ;save this task pinter
subq.l #1,taskcount
bne.s taskend.10
move.l #tasks_wait,a0
bra.s taskend.20
taskend.10 move.l #tasks_run,a0
taskend.20 jsr _LVORemHead(a6)
move.l d0,a1
move.l TC_SPREG(a1),a0 ;set it up so we return
movem.l a2-a6/d2-d7,-(a0) ;back here to finish up
pea taskend.resume(pc),-(a0)
move.l a0,TC_SPREG(a1)
move.l #tasks_run,a0 ;put this task in the running
jsr _LVOAddHead(a6) ;queue and make sure we're
move.l #tasks_run,a0
move.l a2,a1
jsr _LVOAddHead(a6)
bsr.w _FullYield
taskend.resume move.l TC_SPLOWER(a2),a1 ;free stack
move.l #4096,d0
jsr _LVOFreeMem(a6)
move.l a2,a1 ;free task structure
move.l #TC_SIZE,d0
jsr _LVOFreeMem(a6)
movem.l (a7)+,a2-a6/d2-d7
rts
_TaskRun move.l 4(a7),a0 ;get task pointer
move.l TC_SPREG(a0),a1
move.l 8(a7),-(a1) ;push initial PC
movem.l a2-a6/d2-d7,-(a1) ;inherit registers
addq.l #1,taskcount
rts ;that's all!
_TaskWait movem.l a2-a6/d2-d7,-(a7)
move.l ExecBase,a6
jsr _LVODisable(a6)
sub.l a1,a1
jsr _LVOFindTask(a6)
move.l d0,a2
move.l d0,a1
move.l #tasks_wait,a0
jsr _LVOAddHead(a6)
pea taskwait.resume(pc),-(a7)
movem.l a2-a6/d2-d7,-(a7)
move.l a7,TC_SPREG(a2) ;save stack pointer
bra.w taskkickoff ;get next thread to run
;--------------------------------------------
; welcome back! IRQs are enabled at this point. Also, the system
; has already popped the second set of a2-a6/d2-d7 off the stack,
; so do any clean up, pop the first set of registers off the stack,
; and off we go!
;--------------------------------------------
taskwait.resume movem.l (a7)+,a2-a6/d2-d7
rts
;**********************************************************************
section _data,data
taskcount dc.l 0
;----------------------------------------------------------------------
end