/* Task_Routines.c	Copyright 1987 by James M Synge   */

/* This file contains CreateTask() and DeleteTask(),
 * versions of the routines of the same names in the ROM
 * Kernel Manual.
 */

#include "exec/types.h"
#include "exec/ports.h"
#include "exec/tasks.h"
#include "exec/memory.h"

struct Task *
CreateTask(Task_Name, Priority, Startup_Routine, Stack_Size)

char *Task_Name;
int Priority;
void (* Startup_Routine) ();
int Stack_Size;
{
    /* A pointer to the child task's task structure. */
    register struct Task *Child;
    register APTR Child_Stack;   /* A pointer to it's stack */
    register APTR AllocMem();

    /* First allocate a stack for the task. */

    Child_Stack = AllocMem(Stack_Size, MEMF_CLEAR);

    if (Child_Stack == 0) {

/*	Couldn't allocate a stack! And we cann't print an
 *	error message because this routine could be called
 *	by a task or a process which doesn't have a window.
 */
	return(NULL);
    };

    /* Now allocate a Task structure. */

    Child = (struct Task *)
    	AllocMem(sizeof(struct Task),
		 MEMF_CLEAR | MEMF_PUBLIC);

    if (Child == NULL) {
	FreeMem(Child_Stack, Stack_Size);
	return(NULL);
    };

    /* Now initialize the task structure as per the RKM. */

    Child->tc_SPLower = Child_Stack;

    Child->tc_SPReg   =
    Child->tc_SPUpper = (APTR)( (ULONG)Child_Stack +
    				(ULONG)Stack_Size);

    Child->tc_Node.ln_Type = NT_TASK;
    Child->tc_Node.ln_Pri = Priority;
    Child->tc_Node.ln_Name = Task_Name;

    AddTask(Child, Startup_Routine, 0L);

    return(Child);
}

/* DeleteTask is written in a style which will allow it to
 * be called by any task, including the task being deleted.
 * Note that it can not delete an AmigaDOS Process.
 */

void DeleteTask(Child)
    register struct Task *Child;
{
    register ULONG Stack_Size;

    if (Child == NULL) return;

    /* Free up the stack and the Task structure.  This is
     * done inside a Forbid() / Permit() so that no other
     * task can do the same.
     */

    Forbid();

    if (Child->tc_Node.ln_Type != NT_TASK) {
	/* OOPS! It ain't a Task! */
	Permit();
    	return;
    }

    Stack_Size = (ULONG)(Child->tc_SPUpper) -
    		 (ULONG)(Child->tc_SPLower);

    FreeMem(Child->tc_SPLower, Stack_Size);
    FreeMem(Child, sizeof(struct Task));

    /* Now remove the task.  This will not return if it is
     * removing the current task.
     */

    RemTask( Child );

    /* If we removed another task, then we continue on:
     * so call Permit() and return.
     */

    Permit();

    return;
}
