home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
199.lha
/
GimmeLib
/
subtask.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-12-27
|
8KB
|
303 lines
/*
* 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 */