[comp.sys.amiga.tech] CreateTask

LK-KARI@FINTUVM.BITNET (Kari Sutela) (04/12/89)

Sorry, this is a bit long.

For most part of the week now, I have been seriously confused about creating
a new task. I have looked on other peoples' source and I can understand them
just fine but they are not quite what I need to do. I have managed to locate
source code for tasks that end before their parent, only.

BUT I need to create a task that *outlives* its parent and I haven't managed
to do this in a reliable manner. What I need to do is start a task which
will initialize a public message port and waits for messages to arrive there.
The task should remove itself when a special 'kill yourself' message appears
in its message port.

The code goes something like this: (Please note, due to EBCDIC-ASCII
translation, square and curly brackets might appear as some other characters,
so the code might look a bit strange)

/* GLOBALS */

struct RxsLib *RexxSysBase;
struct IntuitionBase *IntuitionBase;   /* The task needs these libraries */

char PORTNAME#$ "MyPublicPort";

extern void the_task();

void main()
{
    struct Task *MyTask;

    OpenLibraries();    /* And other initializations */

    MyTask = CreateTask( "MyTask", 0L, the_task, 4000L );
    /* Hope I got the parameters right */
    if( mytask == NULL )
         error( "Couldn't create the task" );
         /* error() will release resources, too */

    return;

}

void the_task()
{
    struct MsgPort *MyPort;
    struct Message *mymsg;
    BOOL quit;

    geta4(); /* For Manx small code memory model */

    MyPort = CreatePort( PORTNAME ); /* Perhaps some other parameters as well*/
    if( MyPort == NULL )
         cleanup();
    /* Perhaps I remember the semantics of CreatePort() incorrectly, did it
     * return the signal bit? Any way, I'm typing this from memory
     */


    /* Make it public */
    AddPort( MyPort );

    /* Now wait */
    for(;;) {
         WaitPort( MyPort );
         mymsg = GetMsg( MyPort );
         if( quit = handle_msg( mymsg ) ) {
              ExhaustPort( MyPort );
              RemPort( MyPort );  /* Not public anymore */
              DeletePort( MyPort );  /* delete it entirely */
              cleanup();
         }
    }

}

BOOL handle_msg( msg )
struct Message *msg;
{
    /* Handles the message - returns true if it was 'kill yourself' */
}

void ExhaustPort( port )
struct MsgPort *port;
{
    /* replies to all pending messages */
}

void cleanup()
{
    CloseLibs();
    RemTask( 0L );
}

/* End of example */

I am using Aztec C, if it makes any difference. I seem to be able to launch
the new task allright, but the message port won't open properly most of the
time. What especially confuses me is that, if I run the program through SDB,
I'll get a proper message port with the name I wanted. And most of the time
my tests indicate that the messages are correctly handled.

But if I invoke the command straight from the command line, a message port
will open but its name is garbage. This is really confusing, why should a
program behave differently if it's being debugged (have I created a monster
program that's aware of monitoring :-)???

BTW, has anyone found a way to use SDB on tasks?

BTW, if I leave out the task creation and just 'run' the program (or use Manx's
detach.o) everything works absolutely fine. But these methods create a process
and this program doesn't actually need it - I'd really like to make it a task.

Any hints on this? Have I completely misunderstood the semantics of CreateTask
and task behaviour in general? Am I mistaken in assuming that a task can use
resources opened by its parent (even after the parent has died) and close the
resources when it exits? The Exec manual isn't too clear on this.

-   Kari Sutela
-   lk-kari@fintuvm.utu.fi (internet) OR lk-kari@fintuvm.bitnet (bitnet)

jesup@cbmvax.UUCP (Randell Jesup) (04/19/89)

In article <740LK-KARI@FINTUVM> LK-KARI@FINTUVM.BITNET (Kari Sutela) writes:
>BUT I need to create a task that *outlives* its parent and I haven't managed
>to do this in a reliable manner.
...
>void main()
>{
...
>    MyTask = CreateTask( "MyTask", 0L, the_task, 4000L );
...
>    return;
>}
>void the_task()
>{
...
>}
>Any hints on this? Have I completely misunderstood the semantics of CreateTask
>and task behaviour in general? Am I mistaken in assuming that a task can use
>resources opened by its parent (even after the parent has died) and close the
>resources when it exits? The Exec manual isn't too clear on this.

	When your main() program exits, the memory it is stored in (the entire
compiled program) is freed back to the system.  The results will be random,
and depends on what is allocating memory and where your program was loaded.
I'm suprised the system didn't crash quickly.

	The system does no automatic resource tracking.  It has no knowlege
that you are still using code that was loaded by the original task (how
could it in this case?)  In general, programs must release resources they
allocated before exiting (often the C compiler exit() routine will free
C library-allocated items - malloc()ed memory, fopen()ed files, etc.)

	To have the new task last after the main one, you must somehow
stop the memory from being freed, or load it into memory seperately, or
if it is position-independant (C usually isn't on the Amiga) you can copy
it to memory you AllocMem()ed.

	I recommend LoadSeg()ing the task code seperately (note LoadSeg returns
a BPTR to the longword BEFORE the entry point).  Make sure the task releases
it's memory when exiting, or that memory will be lost forever.  A way to do
this is:

	void newtask ()
	{
	...
	Forbid();
	UnLoadSeg(seg);	/* note - you must have a way to get the BPTR returned
			   by LoadSeg().  Sending a message with it (and any
			   other parameters) after CreateTask()ing is a good
			   way. */
	}

	This works because you can use memory that has been freed while under
forbid (just don't make any calls that might Wait() for something...!)

-- 
Randell Jesup, Commodore Engineering {uunet|rutgers|allegra}!cbmvax!jesup