[comp.sys.amiga.tech] Task Switching Problem

utoddl@uncecs.edu (Todd M. Lewis) (09/22/90)

[ I tried to post this once before, but I never saw it, so...]

Hello, net.

I'm writing a set of programs which work together but should
run at different priorities.  What I'd like to do is monitor
how these different programs interact as far as who runs when
and for how long.  What I'd particularly like to know is

  * how does one get the currently running task to relinquish
    the CPU for the rest of the current time slice?  I want to
    have a task which will increment a counter each time it is
    activated, then sleep until its turn to run comes up
    again.

All the other monitoring functions I want to build into this
system can be built on top of this and the other exec functions,
but this one has me stumped.  Any ideas?
_____        
  |      Todd M. Lewis            Disclaimer: If you want my employer's
  ||\/|  utoddl@ecsvax.uncecs.edu             ideas, you'll have to
  ||  || utoddl@ecsvax.bitnet, @unc.bitnet    _buy_ them. 
   |  || utoddl@next1.mscre.unc.edu 
       |___   ("Prgrms wtht cmmnts r lk sntncs wtht vwls." --TML)

valentin@cbmvax.commodore.com (Valentin Pepelea) (09/22/90)

In article <1990Sep21.180100.28580@uncecs.edu> utoddl@uncecs.edu (Todd M. Lewis) writes:
>
>  * how does one get the currently running task to relinquish
>    the CPU for the rest of the current time slice?  I want to
>    have a task which will increment a counter each time it is
>    activated, then sleep until its turn to run comes up
>    again.

There used to be function call available that allowed a task to relinquish
the CPU to some other ready task, but that function has been made private.
I was not part of the devellopment team back then, so I cannot comment on the
rationale of this move for sure, but I can give you my own rationalisation.

The proper way for tasks to synchronize is through signalling and waiting.
The availability of a Relinquish() call would incite novice programmers to
create their own cooperating multitasking environment rather than taking
advantage of the inherent preemptive characteristics of the operating system.

The net result is that the abusive use of a Relinquish() call would amount to
busy waiting. A task would check for the occurrance of an event, and call
Relinquish() if that event did not happen yet. When awaken again, it would
loop back to check for the occurrance of the event, and call Relinquish() if
the event did not occur yet.

This kind of programming practice must be avoided. The proper thing to do is
to make the Relinquish() call unavailable.

Valentin
-- 
The Goddess of democracy? "The tyrants     Name:    Valentin Pepelea
may distroy a statue,  but they cannot     Phone:   (215) 431-9327
kill a god."                               UseNet:  cbmvax!valentin@uunet.uu.net
             - Ancient Chinese Proverb     Claimer: I not Commodore spokesman be

utoddl@uncecs.edu (Todd M. Lewis) (09/27/90)

In article <14593@cbmvax.commodore.com> valentin@cbmvax.commodore.com (Valentin Pepelea) writes:
>In article <1990Sep21.180100.28580@uncecs.edu> utoddl@uncecs.edu (Todd M. Lewis) writes:
>>
>>  * how does one get the currently running task to relinquish
>>    the CPU for the rest of the current time slice?  I want to
>
>There used to be function call available that allowed a task to relinquish
>the CPU to some other ready task, but that function has been made private.
 [ . . . ]
>The proper way for tasks to synchronize is through signalling and waiting.
>The availability of a Relinquish() call would incite novice programmers to
   [... do evil things . . . ]
>This kind of programming practice must be avoided. The proper thing to do is
>to make the Relinquish() call unavailable.

Ok, I can buy that.  I'll try this instead--I'll make two tasks which
signal each other and wait on each other's signals.  One of these will do
the monitoring I want to have, while the other will be there simply
to wake up the first task each time the robin goes 'round.  It isn't
quite as efficient as I had planned, but what I want to test is 
task-relative anyway, not real-time sensitive.  Any problems with this?
_____        
  |      Todd M. Lewis            Disclaimer: If you want my employer's
  ||\/|  utoddl@ecsvax.uncecs.edu             ideas, you'll have to
  ||  || utoddl@ecsvax.bitnet, @unc.bitnet    _buy_ them. 
   |  || utoddl@next1.mscre.unc.edu 
       |___   ("Prgrms wtht cmmnts r lk sntncs wtht vwls." --TML)

valentin@cbmvax.commodore.com (Valentin Pepelea) (10/02/90)

In article <1990Sep27.141415.29158@uncecs.edu> utoddl@uncecs.edu (Todd M.
Lewis) writes:
>
> Ok, I can buy that.  I'll try this instead--I'll make two tasks which
> signal each other and wait on each other's signals.  One of these will do
> the monitoring I want to have, while the other will be there simply
> to wake up the first task each time the robin goes 'round.  It isn't
> quite as efficient as I had planned, but what I want to test is 
> task-relative anyway, not real-time sensitive.  Any problems with this?

Yes. You're missing the point. Signal/Wait are for synchronizing on event
occurances. The round-robin loop around is not an appropriate event to
synchronize on.

Tell me what you want to do, and I'll show you the proper method/algorithm
to do it.

Valentin
-- 
The Goddess of democracy? "The tyrants    Name:    Valentin Pepelea
may destroy a statue,  but they cannot    Phone:   (215) 431-9327
kill a god."                              UseNet:  cbmvax!valentin@uunet.uu.net
             - Ancient Chinese Proverb    Claimer: I not Commodore spokesman be

utoddl@uncecs.edu (Todd M. Lewis) (10/05/90)

In article <14782@cbmvax.commodore.com> valentin@cbmvax.commodore.com (Valentin Pepelea) writes:
>
>Yes. You're missing the point. Signal/Wait are for synchronizing on event
>occurances. The round-robin loop around is not an appropriate event to
>synchronize on.

I sorta had a bad feeling about doing that anyway.  It would
probably serve my purposes in this case, but it isn't a general
solution to the problem.

>Tell me what you want to do, and I'll show you the proper method/algorithm
>to do it.

Now, if I could hold you to that for about 20 years, my job would
be a lot easier! :-)  I assume you mean for this particular problem.
Ok, here's the overview:  I've got a group of related tasks (in multiples
of 3--don't ask why) that are going to be waiting on about a half dozen
signals each, and each signaling each other.  Every now and then, each of
these tasks has to go off and think a while--several seconds worth of
floating point calcs--and then signal a few tasks and Wait().  What tasks
get signaled depends on which tasks have been given the mosts time slices.
This explanation is never going to make sense without giving about 20 pages
of background, and there are probably other ways to get the same effect, but
what I want is a public struct that all these tasks can read, with one 
ULONG per task which is a count of the number of time slices each task
has been given.  The process I'm modeling is perfectly simulated by the
round-robin run-if-ready scheduling that exec is already doing.  I'd hate
to have to rebuild all that just to get a count of time-slices used
by each task.  Note that the number of times a task has been awakened
from a Wait() is not enough--I also need the number of slices consumed
in the calc part (which can be quite a few).

I would prefer to use the "proper" way to get this info, if there is one.

>Valentin
_____        
  |      Todd M. Lewis            Disclaimer: If you want my employer's
  ||\/|  utoddl@ecsvax.uncecs.edu             ideas, you'll have to
  ||  || utoddl@ecsvax.bitnet, @unc.bitnet    _buy_ them. 
   |  || utoddl@next1.mscre.unc.edu 
       |___   ("Prgrms wtht cmmnts r lk sntncs wtht vwls." --TML)

valentin@cbmvax.commodore.com (Valentin Pepelea) (10/05/90)

In article <1990Oct4.195348.8975@uncecs.edu> utoddl@uncecs.edu (Todd M. Lewis)
writes:
>
> Ok, here's the overview:  I've got a group of related tasks (in multiples
> of 3--don't ask why) that are going to be waiting on about a half dozen
> signals each, and each signaling each other.  Every now and then, each of
> these tasks has to go off and think a while--several seconds worth of
> floating point calcs--and then signal a few tasks and Wait().  What tasks
> get signaled depends on which tasks have been given the mosts time slices.
> This explanation is never going to make sense without giving about 20 pages
> of background, and there are probably other ways to get the same effect, but
> what I want is a public struct that all these tasks can read, with one 
> ULONG per task which is a count of the number of time slices each task
> has been given.

I'm sorry to disappoint you, but you are asking for something that you simply
cannot get. Although the Quantum and Elapsed variables in the ExecBase
structure which should give you an idea of how many quantums have been elapsed,
(the quantum is supposedly incremented every vertical blank) these are awfully
unreliable. Strike one.

A second option would be to use the tc_Switch and tc_Launch vectors of the Task
structure.  If the TB_SWITCH flag of the tc_Flags entry is set, then the
routine pointed to by tc_Switch is called every time your task is switched out
and another one is started. If the TB_LAUNCH flag of the tc_Flags entry is set,
then the routine pointed to by tc_Launch is called every time your task is
about to be restarted again.

Unfortunately, the tc_Switch and tc_Launch functions will be running in
supervisor mode, therefore you will not be able to call the timer.device
functions to find out what the current time is. Strike two.

Therefore you have to take over the 16-bit counters of the CIA B timer
yourself, and count the time yourself, while making sure you're keep track of
when the counters wrap-around.

But even if you manage that, interrupts still occur and are processed under
your task's context. (kind of - tc_Switch and tc_Launch will not be called
during interrupt occurances) Therefore the time count that you keep will be
rather useless since you do not know how much time was spent inside your task,
and how much was spent processing interrupts, particularly when talking about
a granularity of a fraction of a second. Strike three, you're out of luck.

> The process I'm modeling is perfectly simulated by the round-robin
> run-if-ready scheduling that exec is already doing.

Kind of. The exec's scheduling algorithm can be viewed as an idealistic
round-robin algorithm, but some implementation details remind us that we
do not live in Utopia.

Valentin
-- 
The Goddess of democracy? "The tyrants    Name:    Valentin Pepelea
may destroy a statue,  but they cannot    Phone:   (215) 431-9327
kill a god."                              UseNet:  cbmvax!valentin@uunet.uu.net
             - Ancient Chinese Proverb    Claimer: I not Commodore spokesman be

rsbx@cbmvax.commodore.com (Raymond S. Brand) (10/05/90)

In article <14900@cbmvax.commodore.com>, valentin@cbmvax.commodore.com (Valentin Pepelea) writes:
> In article <1990Oct4.195348.8975@uncecs.edu> utoddl@uncecs.edu (Todd M. Lewis)
> writes:
> >
> > Ok, here's the overview:  I've got a group of related tasks (in multiples
> > of 3--don't ask why) that are going to be waiting on about a half dozen
> > signals each, and each signaling each other.  Every now and then, each of
> > these tasks has to go off and think a while--several seconds worth of
> > floating point calcs--and then signal a few tasks and Wait().  What tasks
> > get signaled depends on which tasks have been given the mosts time slices.
> > This explanation is never going to make sense without giving about 20 pages
> > of background, and there are probably other ways to get the same effect, but
> > what I want is a public struct that all these tasks can read, with one 
> > ULONG per task which is a count of the number of time slices each task
> > has been given.

[...]

> A second option would be to use the tc_Switch and tc_Launch vectors of the Task
> structure.  If the TB_SWITCH flag of the tc_Flags entry is set, then the
> routine pointed to by tc_Switch is called every time your task is switched out
> and another one is started. If the TB_LAUNCH flag of the tc_Flags entry is set,
> then the routine pointed to by tc_Launch is called every time your task is
> about to be restarted again.
> 
> Unfortunately, the tc_Switch and tc_Launch functions will be running in
> supervisor mode, therefore you will not be able to call the timer.device
> functions to find out what the current time is. Strike two.

Actually, all timer.device functions can be used at any time, including inside
interrupt and exception handling code. However, to get the accuracy you need,
you'll need to use the 2.0 timer.device GetEClock library entry-point.

> Therefore you have to take over the 16-bit counters of the CIA B timer
> yourself, and count the time yourself, while making sure you're keep track of
> when the counters wrap-around.

This is actually easy when you have both timers and only mildly anoying when
you only have one time.

> But even if you manage that, interrupts still occur and are processed under
> your task's context. (kind of - tc_Switch and tc_Launch will not be called
> during interrupt occurances) Therefore the time count that you keep will be
> rather useless since you do not know how much time was spent inside your task,
> and how much was spent processing interrupts, particularly when talking about
> a granularity of a fraction of a second. Strike three, you're out of luck.

This probably isn't a problem since the amount of time spent handling
interrupts is small compared to the quantum. However, there is another problem,
exec reschedules the tasks after interrupts. So in practice, the time spent
handling interrupts and your task timing functions could be of comparable size
to the time spent actually executing the task code.

> > The process I'm modeling is perfectly simulated by the round-robin
> > run-if-ready scheduling that exec is already doing.
> 
> Kind of. The exec's scheduling algorithm can be viewed as an idealistic
> round-robin algorithm, but some implementation details remind us that we
> do not live in Utopia.
> 
> Valentin

						rsbx

------------------------------------------------------------------------
  Raymond S. Brand			rsbx@cbmvax.commodore.com
  Commodore-Amiga Engineering		...!uunet!cbmvax!rsbx
  1200 Wilson Drive			(215)-431-9100
  West Chester PA 19380			"Looking"
------------------------------------------------------------------------

rsbx@cbmvax.commodore.com (Raymond S. Brand) (10/08/90)

In article <14906@cbmvax.commodore.com>, rsbx@cbmvax.commodore.com (Raymond S. Brand) writes:
> 
> This probably isn't a problem since the amount of time spent handling
> interrupts is small compared to the quantum. However, there is another problem,
> exec reschedules the tasks after interrupts. So in practice, the time spent
> handling interrupts and your task timing functions could be of comparable size
> to the time spent actually executing the task code.

The scheduler in the 2.0 exec has been fixed so that it nolonger reschedules
tasks when it doesn't need to. 1.3, however, still has the problem. :-(

What was happening is that anytime the internal AddTask, Permit, or Signal
functions were called (mostly by the external functions of the same name),
a reschedule happened. Signal is used by a number of interrupt handlers and
servers, either directly or indirectly through passing messages.

> 						rsbx

						rsbx

------------------------------------------------------------------------
  Raymond S. Brand			rsbx@cbmvax.commodore.com
  Commodore-Amiga Engineering		...!uunet!cbmvax!rsbx
  1200 Wilson Drive			(215)-431-9100
  West Chester PA 19380			"Looking"
------------------------------------------------------------------------

utoddl@uncecs.edu (Todd M. Lewis) (10/08/90)

In article <14900@cbmvax.commodore.com> valentin@cbmvax.commodore.com (Valentin Pepelea) writes:
>A second option would be to use the tc_Switch and tc_Launch vectors of the Task
>structure.  If the TB_SWITCH flag of the tc_Flags entry is set, then the
>routine pointed to by tc_Switch is called every time your task is switched out
>and another one is started. If the TB_LAUNCH flag of the tc_Flags entry is set,
>then the routine pointed to by tc_Launch is called every time your task is
>about to be restarted again.

That should do nicely.  I can use this to keep a count of each task's activations.
My model depends only on how many times tasks have switched, not how much
time each task has used.
>
>Unfortunately, the tc_Switch and tc_Launch functions will be running in
>supervisor mode, therefore you will not be able to call the timer.device
>functions to find out what the current time is. Strike two.

Ground rule double.  I don't need the time.  And I can fake the Relinquish()
call with something like {ULONG i; i=tc_Count; while(i==tc_Count);}
since tc_Count would be updated by tc_Switch().  So I waste a few cycles.

Thanks.  (Why didn't I think of this?)
>
>Valentin
_____        
  |      Todd M. Lewis            Disclaimer: If you want my employer's
  ||\/|  utoddl@ecsvax.uncecs.edu             ideas, you'll have to
  ||  || utoddl@ecsvax.bitnet, @unc.bitnet    _buy_ them. 
   |  || utoddl@next1.mscre.unc.edu 
       |___   ("Prgrms wtht cmmnts r lk sntncs wtht vwls." --TML)

ken@cbmvax.commodore.com (Ken Farinsky - CATS) (10/09/90)

In article <1990Oct8.152923.1447@uncecs.edu> utoddl@uncecs.edu writes:
>
>Ground rule double.  I don't need the time.  And I can fake the Relinquish()
>call with something like {ULONG i; i=tc_Count; while(i==tc_Count);}
>since tc_Count would be updated by tc_Switch().  So I waste a few cycles.

AACK!!!  I hope that this is strictly educational and not a commercial
product!  I thought that one of the first rules of software on the
Amiga was:   Thou shalt not busy wait.
-- 
--
Ken Farinsky - CATS - (215) 431-9421 - Commodore Business Machines
uucp: ken@cbmvax.commodore.com   or  ...{uunet,rutgers}!cbmvax!ken
bix:  kfarinsky

utoddl@uncecs.edu (Todd M. Lewis) (10/13/90)

In article <14971@cbmvax.commodore.com> ken@cbmvax.commodore.com (Ken Farinsky - CATS) writes:
>In article <1990Oct8.152923.1447@uncecs.edu> utoddl@uncecs.edu writes:
>>
>>Ground rule double.  I don't need the time.  And I can fake the Relinquish()
>>call with something like {ULONG i; i=tc_Count; while(i==tc_Count);}
>>since tc_Count would be updated by tc_Switch().  So I waste a few cycles.
>
>AACK!!!  I hope that this is strictly educational and not a commercial
>product!  I thought that one of the first rules of software on the
>Amiga was:   Thou shalt not busy wait.

NAK!  It is (was) an educational experiment.  The code never made it
out of my office.  It was a monitor for another program (I needed
more information about how some tasks were interacting, and this 
didn't interfere--well it slowed everything down but didn't otherwise
alter the sequences) and has already been deleted.

It worked quite well, actually.  Thanks again for your help.

Hmmmm.  Would this work?  What if I had my task Wait() for some
signal, and have the tc_Switch() routine simply signal my task?
This should give me the same effect as Relinquish().
Can a tc_xxxxx() routine signal safely?
Darn it all--I've got to try this (now that I don't need it 
anymore, of course).  I just love rewriting code I just threw away.
_____        
  |      Todd M. Lewis            Disclaimer: If you want my employer's
  ||\/|  utoddl@ecsvax.uncecs.edu             ideas, you'll have to
  ||  || utoddl@ecsvax.bitnet, @unc.bitnet    _buy_ them. 
   |  || utoddl@next1.mscre.unc.edu 
       |___   ("Prgrms wtht cmmnts r lk sntncs wtht vwls." --TML)

valentin@cbmvax.commodore.com (Valentin Pepelea) (10/13/90)

In article <1990Oct12.203910.18240@uncecs.edu> utoddl@uncecs.edu (Todd M.
Lewis) writes:
>
> Hmmmm.  Would this work?  What if I had my task Wait() for some
> signal, and have the tc_Switch() routine simply signal my task?

Uh, yes, that should work. But remember to clear the signal you would be
waiting for, since it will get set every time you get switched out. In fact,
you should do that within a Disable():

	Disable();
	   clear signal;
	   Wait(signal<<1);
	Enable();

> This should give me the same effect as Relinquish().
> Can a tc_xxxxx() routine signal safely?

Signal() can be called safely from supervisor mode, from interrupts, from
everything.

> Darn it all--I've got to try this (now that I don't need it 
> anymore, of course).  I just love rewriting code I just threw away.

If you were using the old file system on that disk, then perhaps you can use
DiskSalv to recover your files.

Valentin
-- 
The Goddess of democracy? "The tyrants    Name:    Valentin Pepelea
may destroy a statue,  but they cannot    Phone:   (215) 431-9327
kill a god."                              UseNet:  cbmvax!valentin@uunet.uu.net
             - Ancient Chinese Proverb    Claimer: I not Commodore spokesman be