[comp.sys.amiga.programmer] General questions regarding Tasks

schwager@m.cs.uiuc.edu (Michael Schwager) (05/24/91)

Hi,
I'm looking for guidelines about using tasks within C programs.  C code
examples would help.  I've got Aztec C 5.0d, and their little beep.c
example only goes so far.  For example, I wonder: Can I call other
functions from within my task?  What do I need to be careful of (besides
changing system variables)?  Also, can I pass variables to a Task, like
this:

     CreateTask ("Name", 0, func3 (var0, var1, var2), BIGSTACK);

I'm getting random (I think) address error Guru's when I try to get too
fancy with Tasks.  Too fancy means: I want to call another function from
within it.

Here's a concrete example (something I'm trying now).  Given a program,
source code in at least two files.  The first file contains a coupla
functions:

************ file 0 ***********
func0 ()  /* main () or whatever */
{
blah;
}

func1 (number)
int number;
{
struct Task *myTCB;
     blah;
     myTCB = func2 ();
     loop:
     {
     blah;
     if (some_condition) DeleteTask (myTCB);
     }
}
************ file 0 ***********
The second, function(s) and a Task:
************ file 1 ***********
struct Task *
func2 ()
{
struct Task *someTCB;
     blah;
     someTCB = CreateTask ("Name", 0, func3, BIGSTACK);
     blah;
     return (someTCB);
}

func3 ()  /* It's a task! */
{
     blah;
     func1 (number);   /* Oooo... can I do this? Say func1() only has automagic
     	       	    variables.  */
     blah;
}
************ file 1 ***********

Thanks for the help.
-Mike Schwager                             | Machine: Amiga 500, 3 MB RAM/30 HD
INTERNET:schwager@cs.uiuc.edu              | Bike: '83 Kawasaki KZ750 LTD
UUCP:{uunet|convex|pur-ee}!uiucdcs!schwager| Band: Poi Dog Pondering      //
BITNET:schwager@mike.cs.uiuc.edu           | Hero: Robert Bly         \\ //
University of Illinois, Dept. of Comp. Sci.| DoD: #0301                \X/Amiga

markv@kuhub.cc.ukans.edu (05/25/91)

In article <1991May24.160440.374@m.cs.uiuc.edu>, schwager@m.cs.uiuc.edu (Michael Schwager) writes:
> 
> Hi,
> I'm looking for guidelines about using tasks within C programs.  C code
> examples would help.  I've got Aztec C 5.0d, and their little beep.c
> example only goes so far.  For example, I wonder: Can I call other
> functions from within my task?

Yes, you can call other functions and access global variables.  If
using base-relative addressing, do a geta4(); at the beginning of your
task code.  Also, stack checking should be off in any module that has
code that will get run in the context of a sub-task.

>  What do I need to be careful of (besides changing system variables)?

Don't call DOS!!!  This also means don't call things like
OpenLibrary() unless you know the library is in memory.  Many DOS
functions that used to let you get away with being a task in 1.3 will
barf in 2.0.  So don't call DOS or only OpenXXX call that could
require something to be loaded off the disk.  If you do need access to
such devices or libs, have the parent open it first to load it, and
hold it open, then when the child opens it, it will be in memory.

>  Also, can I pass variables to a Task, like this:
>      CreateTask ("Name", 0, func3 (var0, var1, var2), BIGSTACK);

Not normally.  Create just just creates the task struct, allocates the
stack, and jumps to the beginning PC.  Parameters are before
offset to the stack base, so they contain garbage.  If you manually
create a task and add it with AddTask(), you could fudge the stack and
put a dummy parameter list at the base of the stack.  Since the task
code allocates the auto variables, all local variables are okay (of
course static variables are still that).
  
> I'm getting random (I think) address error Guru's when I try to get too
> fancy with Tasks.  Too fancy means: I want to call another function from
> within it.

Hmmm, this should work.  It works fine for me under SAS with both PC
rel and 32 bit abs function jumps.
 
> func3 ()  /* It's a task! */
> {
>      blah;
>      func1 (number);   /* Oooo.can I do this? Say func1() only has automagic
>      	       	    variables.  */

No, this is okays long as you have enough stack.  The code in func3()
will allocate and clean up the parameters on the stack.  Code in
func1() at begin and end will allocate and cleanup auto variables
(usually via LINK and UNLINK).

>      blah;

Zapp.  If I read this right your task is running right off the end.
If it does this it hits a return.  Return to where?  There isn't a
valid address, so it RETs into space.

A task function should be an endless loop with some kind of check for
an exit condition.  Since RemTask(called by DeleteTask) can be picky
about conditions, it should call Wait(0L) (wait forever) to put itself
in a safe state to be deleted by the parent.  Alternately a task can
RemTask(0L) itself, but this doesn't clean up the allocations for the
stack and TCB.

-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mark Gooderum			Only...		\    Good Cheer !!!
Academic Computing Services	       ///	  \___________________________
University of Kansas		     ///  /|         __    _
Bix:	  mgooderum	      \\\  ///  /__| |\/| | | _   /_\  makes it
Bitnet:   MARKV@UKANVAX		\/\/  /    | |  | | |__| /   \ possible...
Internet: markv@kuhub.cc.ukans.edu
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

schwager@m.cs.uiuc.edu (Michael Schwager) (05/25/91)

schwager@m.cs.uiuc.edu (Michael Schwager) writes:

Oops I made a mistake in my pseudo-code.  My task really looks like this:

func3 ()  /* It's a task! */
{
     endless_loop:  /* depends on the parent task to annihilate this task
     	       	       when the user hits a gadget */
     {
     blah;
     func1 (number);   /* Oooo... can I do this? Say func1() only has automagic
     	       	    variables.  */
     Forbid ();
     DrawImage (in_some_Window_declared_globally);
     blah;
     Permit ();
     }
}
************ file 1 ***********

Also, I RTFRKM's and know about the problems with calling DOS functions; I
shan't do that!

Thanks for all the replies so far; I'm beginning to understand these
blasted things....
-Mike

bj@cbmvax.commodore.com (Brian Jackson) (05/25/91)

In article <1991May24.120133.31035@kuhub.cc.ukans.edu> markv@kuhub.cc.ukans.edu writes:
>In article <1991May24.160440.374@m.cs.uiuc.edu>, schwager@m.cs.uiuc.edu (Michael Schwager) writes:
>> 
>> Hi,
>> I'm looking for guidelines about using tasks within C programs.
>  ...

>>  What do I need to be careful of (besides changing system variables)?
>
>Don't call DOS!!!  This also means don't call things like
>OpenLibrary() unless you know the library is in memory.  Many DOS
>functions that used to let you get away with being a task in 1.3 will
>barf in 2.0.  So don't call DOS or only OpenXXX call that could
>require something to be loaded off the disk.  If you do need access to
>such devices or libs, have the parent open it first to load it, and
>hold it open, then when the child opens it, it will be in memory.

Yes, and don't call anything that might, in turn, call DOS.

Be aware that there are some libraries that want 'task-specific'
information and the above trick will be less than optimal in those
cases. :)   It's really better in this case to just create a new
Process instead. Under 1.3 this is a tad arcane (but not really that
hard) and under 2.0 it's cake.

>>  Also, can I pass variables to a Task, like this:
>>      CreateTask ("Name", 0, func3 (var0, var1, var2), BIGSTACK);

Ths easiest way to do this (for me, anyway :)) is to have the child
task create a Message Port for such things. That way, you can make up
your own data structure in the parent, create the task, fill in the
structure with the data that the child needs and then PutMsg() the
thing to the child. You pretty much have to do this anyway since there
needs to be some mechanism to tell the child "we're going away now."

>Mark Gooderum

Brian

 -----------------------------------------------------------------------
/ Brian Jackson  Software Engineer, Commodore-Amiga Inc.    GEnie: B.J. \
\ bj@cbmvax.cbm.commodore.com      or     ...{uunet|rutgers}!cbmvax!bj  /
/ "Anyone considering the purchase of a high-end Macintosh or IBM-      \
\ compatible computer should try an Amiga 3000 for a few hours before   /
/ making the final decision." - Consumer Reports ('91 Buying Guide)     \
-------------------------------------------------------------------------

ronald@ecl014.UUCP (Ronald van Eijck) (05/26/91)

In article <1991May24.192517.9935@m.cs.uiuc.edu> schwager@m.cs.uiuc.edu (Michael Schwager) writes:
>schwager@m.cs.uiuc.edu (Michael Schwager) writes:
>
>Oops I made a mistake in my pseudo-code.  My task really looks like this:
>
>func3 ()  /* It's a task! */
>{
>     endless_loop:  /* depends on the parent task to annihilate this task
>                          when the user hits a gadget */
>     {
>     blah;
>     func1 (number);   /* Oooo... can I do this? Say func1() only has automagic
>                       variables.  */
>     Forbid ();
>     DrawImage (in_some_Window_declared_globally);
>     blah;
>     Permit ();
>     }
>}
>************ file 1 ***********
>
>Also, I RTFRKM's and know about the problems with calling DOS functions; I
>shan't do that!
>
>Thanks for all the replies so far; I'm beginning to understand these
>blasted things....
>-Mike

If you use globals you should start your task by calling geta4() this fixes
your a4 register so it points to the middle of your global data area.
another solution is to use the large code/data memory model.

Hope this helps,

--
  +-------------------------------------------------------------------------+
  |  Ronald van Eijck                  {eunet!}hp4nl!cbmnlux!ecl014!ronald  |
  |                                                                         |
  |  We do the impossible at once for a miracle we need a little more time  |
  +-------------------------------------------------------------------------+

fjrei@kbsaar.UUCP (Franz-Josef Reichert) (05/27/91)

In article <1991May24.160440.374@m.cs.uiuc.edu> schwager@m.cs.uiuc.edu (Michael Schwager) writes:
>
>Hi,
>I'm looking for guidelines about using tasks within C programs.  C code
>examples would help.  I've got Aztec C 5.0d, and their little beep.c
>example only goes so far.  For example, I wonder: Can I call other
>functions from within my task?  What do I need to be careful of (besides
>changing system variables)?  Also, can I pass variables to a Task, like
>this:
>
>     CreateTask ("Name", 0, func3 (var0, var1, var2), BIGSTACK);

No, you can't do that. But you may pass arguments on the task's stack
before calling exec's AddTask(). So you can't use plain CreateTask() as
given by the library. Do the following modification to drop your arguments 
(assuming they are ULONG's):

  *(--((ULONG*)newTask->tc_SPReg)) = var2;
  *(--((ULONG*)newTask->tc_SPReg)) = var1;
  *(--((ULONG*)newTask->tc_SPReg)) = var0;

>I'm getting random (I think) address error Guru's when I try to get too
>fancy with Tasks.  Too fancy means: I want to call another function from
>within it.

No problem at all. Provide a synchronisation between parent and child because
parent task has to wait for the child task to complete. Otherwise, it will
unload the child's code and data space on parent's exit, which will probably
make your program fail. Any global data must be accessed properly. Use absolute 
adressing or 'geta4()' on function entry. 

>-Mike Schwager                             | Machine: Amiga 500, 3 MB RAM/30 HD

--
Best regards,
Franz-Josef Reichert      VOICE: +49 6805 7417
Kuchlingerstrasse 13      UUCP:  ...uunet!cbmvax!cbmehq!cbmger!kbsaar!fjrei
D-6601 Kleinblittersdorf  GERMANY

trantow@csd4.csd.uwm.edu (Jerry J Trantow) (05/28/91)

In article <1991May24.160440.374@m.cs.uiuc.edu> schwager@m.cs.uiuc.edu (Michael Schwager) writes:
>
>Hi,
>I'm looking for guidelines about using tasks within C programs.  C code
>examples would help.  I've got Aztec C 5.0d, and their little beep.c
>example only goes so far.  For example, I wonder: Can I call other

>-Mike Schwager                             | Machine: Amiga 500, 3 MB RAM/30 HD

I would be very wary of the beep.c example if it is the original by Sam Dicker.
I looked at that about 1 1/2 years ago and found several problems with it.  I
don't recall all of the problems, but here is my modified version of it.

I used this as a starting point for a program using a task to animate a 
pointer and I have what I feel is a much better task example, but I'm 
still hoping it will show up in AC.  (Submitted 1 year ago in July, accepted
in December, and I'M STILL WAITING!)

In the meantime, here is my modified beep.c.

/*****************************************************************************
 * VT100-like Beep Sound Example		22 February 1986
 * Copyright (c) 1986 Sam Dicker, Commodore/Amiga Inc.

25 Jan 90 Heavily hacked by JJT since Sam Dicker programs for shit
*
 * Calling VTBeep() starts the sound which stops when it times out.
 * Calling it again, while it is playing, only restarts the timer.
 * Call KillVTBeep() to kill any beeps in progress before exiting your
 * main program.

 *****************************************************************************/

#include "functions.h"

#include "exec/types.h"
#include "exec/errors.h"
#include "exec/memory.he "devices/timer.h"
#include "devices/audio.h"
#include "libraries/dos.h"

#define BEEPNAME	"JJTBeep"
#define BEEPSIZE	10L
#define BEEPFREQ	1000L
#define COLORCLOCK	3579545L
#define SIGB_BEEP	31L
#define SIGF_BEEP	(ULONG)(1L << 31L)
#define SIGF_PORT	(ULONG)(1L << replyPort->mp_SigBit)

#define NOT_DONE (LONG)(FALSE)    /* useful for CheckIO() */
#defONG CreatePort(); */

/* channel allocation map (try left channel; if unavailable try right) */
UBYTE allocationMap[] = { 1, 8, 2, 4 };

/* make beep sound */

LONG VTBeep()
{
    struct Task *beepTCB;
    VOID beepTask();

    Forbid();

    if ((beepTCB = (struct Task *)FindTask(BEEPNAME))== NULL)
    {
	beepTCB = (struct Task *)CreateTask(BEEPNAME, 0L,(LONG)beepTask,500L);
/* printf("Spawned\n"); */
    }
    else
    {
	Signal((struct Task *)beepTCB,SIGF_BEEP);
/* printf("Signalled\n"); */
    }
    Permit();
    return((LONG)(beepTCB != NULL));
}
/* kill any beep sounds in progress.  This is necessary before exiting the
 * main program; otherwise, if a beep is playing, when the beep times out
 * and the t may be gone */
LONG KillVTBeep()
{
    struct Task *beepTCB;
    do {
	Forbid();

	if ((beepTCB = (struct Task *)FindTask((char *)BEEPNAME)) != NULL) 
        {
	    Signal((struct Task *)beepTCB,(LONG)SIGBREAKF_CTRL_C);
/* printf("Kill the task\n"); */
	    Delay(10L);
	}
	Permit(); 
    } while (beepTCB != NULL); /* if it existed, look for it again (Another copy perhaps)*/
}

/* beep sound child task */

VOID beepTask()
{
  struct MsgPort *replyPort;
  struct timerequest *timerIOB;
  struct IOAudio *audioIOB;
  UBYTE *beepWave;
  struct Message *myIO;
  ULONG signals;

  geta4();
  if ((LONG)AllocSignal(SIGB_BEEP) == SIGB_BEEP)
  {
    replyPort = (struct MsgPort *)CreatePort((char *)NULL,0L);
    if (replyPort != NULL) 
    {
      timerIOB = (structt *)CreateExtIO((APTR)replyPort,(LONG)sizeof(struct timerequest));
      if (timerIOB != NULL)
      {
        if ((LONG)OpenDevice((char *)TIMERNAME,(LONG)UNIT_VBLANK,(APTR)timerIOB,0L) == 0L) 
        {
          timerIOB->tr_node.io_Command = TR_ADDREQUEST;
          beepWave = (UBYTE *)AllocMem((LONG)BEEPSIZE,(LONG)MEMF_CHIP|MEMF_CLEAR);
          if (beepWave != NULL) 
          {
            beepWave[0] = 127;
            beepWave[1] =-128;
            audioIOB=(struct IOAudio *)CreateExtIO((struct MsgPort *)replyPort,(LONG)sizeof(struct IOAudio));
            if (audioIOB != NULL) 
            {
              audioIOB->ioa_Request.io_Message.mn_Node.ln_Pri =85;
              audioIOB->ioa_Data = allocationMap;
              audioIOB->ioa_Length = sizeof(allocationMap); /* allocate */
              if ((LONG)OpenDevice(AUDIONAME, 0L, audioIOB, 0L) == 0L)
              {
                audioIOB->ioa_Request.io_Command = CMD_WRITE;
                audioIOB->ioa_Request.io_Flags = ADIOF_PERVOL;
                audioIOB->ioa_Data = beepWave;
                audioIOB->ioa_Length = BEEPSIZE;
                audioIOB->ioa_Period =COLORCLOCK / (BEEPSIZE * BEEPFREQ);
                audioIOB->ioa_Volume = 64;
                audioIOB->ioa_Cycles=0; 
     timerIOB->tr_time.tv_secs = 0;
     timerIOB->tr_time.tv_micro = 1L;

     SendIO(timerIOB);            /* initializes timer IO */

  do
  {
    Forbid(); 

    signals=(LONG)Wait((LONG)(SIGBREAKF_CTRL_C|SIGF_BE        GetMsg(replyPort);   /* doesn't matter what order they come off */
        AbortIO(audioIOB); 
        GetMsg(replyPort); 
      }
    }
    else
    {
      if ((signals&SIGF_BEEP)!=0L) /* start or restart a beep */
      {
        if (CheckIO(tim* these values get destroyed */
        timerIOB->tr_time.tv_micro = 250000L;
        SendIO(timerIOB);            /* start a new timer */
      }
      else   /* Signal came from the ReplyPort */
      {      /* timerIOB returned */
        myIO=(struct Message *)CheckIO(timerIOB);
        if (myIO != NULL)
        {
          Remove(replyPort->mp_MsgList,myIO);
          AbortIO(audioIOB);
        }
        else   /* audioIOB returned */
        {
          AbortIO(timerIOB); 
          GetMsg(replyPort); 
        }
      }
    } 
    Permit(); 
  }
  while ((signals & SIGBREAKF_CTRL_C) == 0L);
/* clean up */
				/* closing the audio device kills the sound
				 * and frees the channels */
				CloseDevice(audioIOB);
			    }
			    DeleteExtIO(audioIOB);
			}
			FreeMem((UBYTE *)beepWave, BEEPSIZE);
		    }
		    CloseDevice(timerIOB);
		}
		DeleteExtIO(timerIOB);
	    }
	    DeletePort(replyPort);
	}
    }
}

main()
{
	VTBeep();
	VTBeep();
	KillVTBeep();
}


Jerry J. Trantow          | I swear by my life and my love of it, 
8967 N. Pelham Parkway    | that I will never live for the sake of another man,
Milwaukee, Wi 53217-1954  | nor ask another man to live for mine. 
(414) 228-4689            |                          Ayn Rand
_____________________________________________________________________________

phorgan@cup.portal.com (Patrick John Horgan) (05/30/91)

Mark Gooderum said:
>A task function should be an endless loop with some kind of check for
>an exit condition.  Since RemTask(called by DeleteTask) can be picky
>about conditions, it should call Wait(0L) (wait forever) to put itself
>in a safe state to be deleted by the parent.  Alternately a task can
>RemTask(0L) itself, but this doesn't clean up the allocations for the
>stack and TCB.
CreateTask and DeleteTask are not system calls, but rather library routines
provided by your friendly C compiler folks.  I rewrote mine to allocate the
memory for the stack, the TCB and the name of the task as part of the 
tc_MemEntry list so that when a RemTask is done the memory is freed by the
operating system.  I originally started mucking around with it because
Manx's routine didn't allocate memory for the name pointer passed to it,
it just stored the string address passed to it into the Nodes name.  This
non-reentrency broke my code which spawned many tasks (alright it only made
all the tasks have the same name, but debugging was weird!).  I sent the
new code to Manx, but haven't checked since 5.0 came out to see if they
included it.  I also sent them Create/Delete Port code to replace theirs
with the same problem.  I haven't checked to see if they used it either.

It's easy (just a few minutes after using the CreateTask in the RKM's as
a starting point), to create the CreateTask that hangs all the neccessary
memory allocations off of the Task structure, and it's so elegant.  Then
the Task can just RemTask itself:)

Patrick Horgan                      pjh70@zeus.ras.amdahl.com


-- 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Mark Gooderum			Only...		\    Good Cheer !!!
Academic Computing Services	       ///	  \___________________________
University of Kansas		     ///  /|         __    _
Bix:	  mgooderum	      \\\  ///  /__| |\/| | | _   /_\  makes it
Bitnet:   MARKV@UKANVAX		\/\/  /    | |  | | |__| /   \ possible...
Internet: markv@kuhub.cc.ukans.edu
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

peter@sugar.hackercorp.com (Peter da Silva) (05/31/91)

In article <42787@cup.portal.com> phorgan@cup.portal.com (Patrick John Horgan) writes:
> It's easy (just a few minutes after using the CreateTask in the RKM's as
> a starting point), to create the CreateTask that hangs all the neccessary
> memory allocations off of the Task structure, and it's so elegant.  Then
> the Task can just RemTask itself:)

So how about posting the code?

A lot of times people talk about this neat code they have, but never post it
for the world to benefit. Since you intended the world should have this code
(at least you gave it to Manx), how about it?
-- 
Peter da Silva.   `-_-'
<peter@sugar.hackercorp.com>.