home *** CD-ROM | disk | FTP | other *** search
- /*
- * FILE: subtask.c
- * Support routines for spawning subtasks (not processes).
- * This means the subtask shares the same memory space as its parent task.
- *
- * Note: this "library" is re-entrant, so any task can spawn and keep track
- * of its own subtasks independantly of other users of this "library".
- *
- * Public Domain, but keep my name in it as the original author.
- * 31-Aug-88 Jan Sven Trabandt first release version
- * 31-Oct-88 Jan Sven Trabandt (de)initialize routines moved to subtinit.c
- * so that minimal routines need to be
- * linked in when using stdstuff.c
- * renamed getRidOfSubTask to killSubTask
- */
-
-
- #define I_AM_SUBTASK
- #include "gimmelib/gimmefuncs.h"
-
-
- #define MIN_STACK 100L
-
- #define MIN_COUNT 2
- #define MAX_EXTRA 2
-
-
- typedef struct {
- struct Message msg;
- ULONG flags;
- APTR ptr;
- } DONEMSG;
-
- #define DONEFLAG ((1L << 31) + 1857329L)
-
-
- /* internal use only!!
- *
- * NAME: a4get, a4put
- *
- * SYNOPSIS: a4value = a4get();
- * a4put( a4value );
- * LONG a4value;
- *
- * DESCRIPTION: Get or put a value into register A4. MANX specific!!!!
- * These two functions are support routines for task creation.
- * BECAUSE A4 GETS CLEARED IN THE SUBTASK
- * and Manx needs it as a base pointer in small-code model.
- * Manx's geta4() won't work if its code is too far away,
- * so this ensures a4 is set up.
- * I'm not sure if this is only in small code and/or small data.
- *
-
- static LONG a4get();
- static VOID a4put();
-
- #asm
- cseg
- _a4get:
- move.l a4,d0
- rts
-
- _a4put:
- move.l 4(a7),a4
- rts
- #endasm
-
-
- struct Task *gimmeSubTask( countptr, portptr, stack_size, data_size,
- myportptr )
- SHORT *countptr;
- struct MsgPort **portptr;
- LONG stack_size, data_size;
- struct MsgPort **myportptr;
- {
- struct Task *task;
- DONEMSG *msg = NULL;
- struct MemList *mymemlist;
- struct myneeds {
- struct MemList mn_head; /* 1 MemEntry in the head */
- struct MemEntry mn_body[MIN_COUNT+MAX_EXTRA-1];
- } myneeds;
- UWORD count = 0;
-
- myneeds.mn_head.ml_Node.ln_Type = NT_MEMORY;
- myneeds.mn_head.ml_Node.ln_Pri = 0;
- myneeds.mn_head.ml_Node.ln_Name = NULL;
-
- if( data_size > 0L ) {
- myneeds.mn_head.ml_me[count].me_Reqs = MEMF_PUBLIC | MEMF_CLEAR;
- myneeds.mn_head.ml_me[count].me_Length = data_size;
- ++count;
- }
- if( myportptr && portptr ) {
- myneeds.mn_head.ml_me[count].me_Reqs = MEMF_PUBLIC | MEMF_CLEAR;
- myneeds.mn_head.ml_me[count].me_Length = (LONG) sizeof(DONEMSG);
- ++count;
- }
-
- /* leave stack and struct Task as last two memory blocks */
-
- if( stack_size < MIN_STACK ) {
- stack_size = MIN_STACK;
- }
- myneeds.mn_head.ml_me[count].me_Reqs = MEMF_PUBLIC;
- myneeds.mn_head.ml_me[count].me_Length = stack_size;
- ++count;
- myneeds.mn_head.ml_me[count].me_Reqs = MEMF_PUBLIC | MEMF_CLEAR;
- myneeds.mn_head.ml_me[count].me_Length = (LONG) sizeof(struct Task);
- ++count;
-
- myneeds.mn_head.ml_NumEntries = count;
- mymemlist = (struct MemList *) AllocEntry( &myneeds );
- if( (ULONG)(mymemlist) & (1L<<31) ) {
- return( NULL );
- }
-
- --count;
- task = (struct Task *) mymemlist->ml_me[count].me_Addr;
- NewList( &task->tc_MemEntry );
- AddTail( &task->tc_MemEntry, mymemlist );
-
- --count;
- task->tc_SPLower = mymemlist->ml_me[count].me_Addr; /* stack */
- task->tc_SPUpper = (APTR) (stack_size + (ULONG) task->tc_SPLower);
- task->tc_SPReg = task->tc_SPUpper;
- task->tc_Node.ln_Type = NT_TASK;
-
- if( myportptr && portptr ) {
- --count;
- msg = (DONEMSG *) mymemlist->ml_me[count].me_Addr;
- msg->msg.mn_Length = mymemlist->ml_me[count].me_Length;
- msg->msg.mn_Node.ln_Type = NT_MESSAGE;
- msg->ptr = (APTR) *portptr; /* save subtask monitoring port */
- msg->flags = DONEFLAG;
- } /* endif */
- pushTaskStack( task, msg );
-
- if( data_size > 0L ) {
- --count;
- task->tc_UserData = mymemlist->ml_me[count].me_Addr;
- }
- pushTaskStack( task, task->tc_UserData );
- pushTaskStack( task, myportptr );
- pushTaskStack( task, countptr );
-
- return( task );
- } /* gimmeSubTask */
-
-
- /* internal use only!!
- *
- * pushTaskStack : push a long value onto the subtask's stack
- * task struct and stack must already be initialized,
- * task must not have been started yet.
- static pushTaskStack( task, val )
- struct Task *task;
- LONG val;
- {
- --((LONG *)task->tc_SPReg);
- *((LONG *)task->tc_SPReg) = val;
- } /* pushTaskStack */
-
-
- /* internal use only!!
- *
- * popTaskStack : pop a long value off the subtask's stack
- * task struct and stack must already be initialized,
- * task must not have been started yet.
- static LONG popTaskStack( task )
- struct Task *task;
- {
- return( *((LONG *)task->tc_SPReg)++ );
- } /* popTaskStack */
-
-
- VOID undoGimmeSubTask( task )
- struct Task *task;
- {
- if( task ) {
- FreeEntry( task->tc_MemEntry.lh_Head );
- }
- } /* undoGimmeSubTask */
-
-
- VOID killSubTask( countptr, portptr, task )
- SHORT *countptr;
- struct MsgPort **portptr;
- struct Task *task;
- {
- struct MsgPort *myport;
-
- if( portptr && (myport = *portptr) ) {
- *portptr = NULL;
- DeletePort( myport );
- }
- /* It is permissible to do a Forbid() and RemTask(NULL) [kill myself]
- * even though the following Permit() statement would not get executed
- * since the OS will take care of things just fine.
- */
- Forbid();
- if( countptr ) {
- --(*countptr);
- }
- RemTask( task );
- Permit();
- } /* killSubTask */
-
-
- /* internal use only!!
- *
- * getRidOfMyself
- * what's actually on the stack:
- * func a4 countptr myport data msg -> see bridgeSubTask
- * what getRidOfMyself thinks is there:
- * ret-addr dum [countptr] [myport] [data] [msg]
- * note it doesn't actually know about data, msg and port.
- * thus countptr is 1 long past dum, etc.
- static VOID getRidOfMyself( dum )
- LONG dum;
- {
- LONG *parm;
- DONEMSG *msg;
- struct MsgPort *mp, *myport;
-
- parm = &dum;
- msg = (DONEMSG *) *(parm + 4);
- if( msg && (myport = (struct MsgPort *) *(parm + 2)) ) {
- mp = (struct MsgPort *) msg->ptr;
- msg->ptr = (APTR) FindTask( NULL );
- msg->msg.mn_ReplyPort = myport;
- PutMsg( mp, msg );
- for( ;; ) {
- WaitPort( myport );
- while ( msg = (DONEMSG *) GetMsg(myport) ) {
- if( msg->msg.mn_Node.ln_Type != NT_REPLYMSG ) {
- ReplyMsg( msg );
- } else if( msg->flags == DONEFLAG ) {
- goto gromyself_done;
- }
- } /* while */
- } /* for */
- }
- gromyself_done:
- killSubTask( *(parm + 1), parm + 2, NULL );
- } /* getRidOfMyself */
-
-
- /* internal use only!!
- *
- * bridgeSubTask
- * Used as the actual initialPC for the subtask, it gets a message port
- * if necessary and calls the real subtask routine.
- * Also increments the subtask counter!!!!
- * Also changes portptr on the stack to port (ie a pointer to a port, not
- * a pointer to a pointer) so things are kosher for getRidOfMyself.
- * startSubTask does the set-up so that the new subtask starts executing
- * with this routine.
- static VOID bridgeSubTask( func, a4, countptr, myportptr, data, msg )
- long (*func)();
- LONG a4;
- SHORT *countptr;
- struct MsgPort **myportptr;
- APTR data;
- DONEMSG *msg;
- {
- a4put( a4 );
- if( countptr ) {
- Forbid();
- ++(*countptr);
- Permit();
- }
- if( myportptr ) {
- *myportptr = CreatePort( NULL, 0L );
- if( !*myportptr ) {
- return;
- }
- *((struct MsgPort **)&myportptr) = *myportptr;
- }
- func( data );
- } /* bridgeSubTask */
-
-
- short startSubTask( task, name, pri, initialpc, finalpc )
- struct Task *task;
- char *name;
- BYTE pri;
- void (*initialpc)(), (*finalpc)();
- {
- if( !task || !initialpc ) {
- return( -1 );
- }
- task->tc_Node.ln_Name = name;
- task->tc_Node.ln_Pri = pri;
- pushTaskStack( task, a4get() );
- pushTaskStack( task, initialpc );
- if( !finalpc ) {
- (APTR) finalpc = (APTR) getRidOfMyself;
- }
- AddTask( task, bridgeSubTask, finalpc );
- return( 0 );
- } /* startSubTask */
-