/*
**
**	$Id: process.c,v 1.2 1995/10/07 02:52:52 chris Exp $
**	$Revision: 1.2 $
**
**	$Filename: developer/process.c $
**	$Author: chris $
**	$Date: 1995/10/07 02:52:52 $
**	$Portability: AMIGADOS $
**
**	Prozess-Verwaltung
**
**	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF RELOG AG.
**
**	COPYRIGHT (C) 1991-1996 BY RELOG AG, ZUERICH. ALL RIGHTS RESERVED.
**	NO PART OF THIS SOFTWARE MAY BE COPIED, REPRODUCED, OR TRANSMITTED
**	IN ANY FORM OR BY ANY MEANS,  WITHOUT THE PRIOR WRITTEN PERMISSION
**	OF RELOG AG.
**
*/

#include "OS.h"

#define  _PROCESS
#include "process.h"
#include "arexx.h"

#include <proto/exec.h>
#include <exec/memory.h>
#include <proto/dos.h>
#include <dos/dostags.h>

#include <stdio.h>
#include <string.h>

#define STACKSIZE		8192		/* Stapelgrösse des Message-Handler-Prozesses */


struct ProcEnv
{
	struct Message		Message;		/* Wird dem Prozess geschickt */

	PROCFUNC			Func;			/* Handler-Funktion */
	VOID				*Para;			/* Parameter der Handler-Funktion */

	struct Process		*Process;		/* Zeiger auf Prozess oder NULL */

	struct ARexxPort	*RexxPort;		/* Zeiger auf REXX-Port */
	char				PortName[32];	/* REXX-Port-Name */
};


struct MsgPort	*proclistport;			/* Liste aller ProcEnv-Strukturen */


/****** Process_Create ******************************************************
*
*   NAME
*       Process_Create -- Create a process
*
*   SYNOPSIS
*       pid = Process_Create( func, userdata, name, priority );
*
*       U32 Process_Create( BOOL (*)( U32 ), U32, U8 *, S8 );
*
*   FUNCTION
*       This function creates a process which will basically sleep. Whenever
*       Process_Wakeup() with its pid is called, the process will wake up and
*       call `func' with `userdata' as its sole argument. When the function
*       returns, the process will fall asleep anew.
*       If func returns FALSE, the process will self-terminate.
*
*       Additionally, an AREXX message port will be created, and AREXX
*       messages will be handled.
*
*   INPUTS
*       func     - Function to be called, or NULL if this is an AREXX
*                  handler process.
*       userdata - Will be passed to func and all AREXX functions
*       name     - Name of the process
*       priority - Process priority [-128..127]
*
*   RESULT
*       pid - Process ID.
*
*   SEE ALSO
*
****************************************************************************/

static VOID __saveds ProcessEntry( VOID )
{
	struct Process	*me = (struct Process *)FindTask( NULL );
	struct ProcEnv	*env;

	/*
	**	Startup-Message mit Environment abholen
	*/
	WaitPort( &me->pr_MsgPort );
	env = (struct ProcEnv *)GetMsg( &me->pr_MsgPort );


	/*
	**	ARexx-Port erzeugen, falls keine Handler-Funktion angegeben wurde
	*/
	if (env->Func == NULL)
	{
		OS_sprintf( env->PortName, "AM-%08lx", env->Process );
		env->RexxPort = ARexx_CreatePort( env->PortName, env->Para );	/* $$$ Abfrage? */
	}


	/*
	**	Initialisierung ist jetzt beendet, Env wird zurückgeschickt.
	*/
	ReplyMsg( &env->Message );


	/*
	**	Hauptschleife
	*/
	for (;;)
	{
		U32 mask = SIGBREAKF_CTRL_C | SIGF_SINGLE;

		if (env->RexxPort != NULL)
			mask |= 1UL << env->RexxPort->Port->mp_SigBit;

		if (Wait( mask ) & SIGBREAKF_CTRL_C )
				break;

		if (env->Func != NULL)
		{
			if (!(*env->Func)( env->Para ))
				break;
		}
		else if (env->RexxPort != NULL)
		{
			ARexx_HandleMessages( env->RexxPort );
		}
	}

	if (env->RexxPort != NULL)
	{
		ARexx_DeletePort( env->RexxPort );
		env->RexxPort = NULL;
	}

	env->Process = NULL;
}


U32
Process_Create( PROCFUNC func, VOID *para, U8 *name, S8 pri )
{
	struct ProcEnv *env;

	if (proclistport == NULL)
	{
		printf( "Creating the first process, initializing proclistport\n" );
		if (!(proclistport = CreateMsgPort()))
			return NULL;
	}

	if ((env = OS_Malloc( sizeof( *env ) )) != NULL)
	{
		env->Func	= func;
		env->Para	= para;

		/*
		**	Message-Handler-Prozess erzeugen
		*/
		if (env->Process = CreateNewProcTags(
				NP_Entry,		(U32)ProcessEntry,
				NP_Name,		(U32)name,
				NP_Priority,	(S32)pri,
				NP_StackSize,	STACKSIZE,
				NP_Output,		Output(),
				NP_CloseOutput,	FALSE,
				TAG_DONE
		))
		{
			if (env->Message.mn_ReplyPort = CreateMsgPort())
			{
				/*
				**	Startup-Message zum neuen Prozess schicken und warten,
				**	bis sie wieder zurückgekommen ist
				*/
				PutMsg( &env->Process->pr_MsgPort, &env->Message );
				WaitPort( env->Message.mn_ReplyPort );
				DeleteMsgPort( env->Message.mn_ReplyPort );
				env->Message.mn_ReplyPort = NULL;

				/* env in die Prozessliste eintragen */
				PutMsg( proclistport, &env->Message );

				printf( "Created process 0x%08lx\n", env );
				return (U32)env;
			}

			/* $$$ Oehm... da liegt jetzt so ein toter Prozess rum! */
		}

		OS_Free( env );
	}

	return (U32)NULL;
}


/****** Process_Delete ******************************************************
*
*   NAME
*       Process_Delete -- Delete a process
*
*   SYNOPSIS
*       Process_Delete( pid );
*
*       VOID Process_Delete( U32 );
*
*   FUNCTION
*       This function kills a process.
*
*   INPUTS
*
*   RESULT
*
*   SEE ALSO
*
****************************************************************************/

VOID
Process_Delete( U32 pid )
{
	struct ProcEnv *env = (struct ProcEnv *)pid;

	Forbid();
	if (Process_IsValidPID( pid ))
	{
		U16 i;

		Remove( &env->Message.mn_Node );

		for (i=0; i < 20; ++i)	/* 4 Sekunden */
		{
			if (env->Process != NULL)
				Signal( &env->Process->pr_Task, SIGBREAKF_CTRL_C );

			if (env->Process == NULL)
				break;

			OS_Delay( 100 );
		}

		printf( "Deleting process 0x%08lx\n", env );
		OS_Free( env );
/*		env = NULL; */
	}

	if (ISLISTEMPTY( (IC_LIST *)&proclistport->mp_MsgList ))
	{
		printf( "This was the last process, deleting proclistport\n" );
		DeleteMsgPort( proclistport );
		proclistport = NULL;
	}
	Permit();
}


/****** Process_WakeUp ******************************************************
*
*   NAME
*       Process_WakeUp -- Signal a process to wake up
*
*   SYNOPSIS
*       Process_WakeUp( pid );
*
*       VOID Process_WakeUp( U32 );
*
*   FUNCTION
*
*   INPUTS
*
*   RESULT
*
*   SEE ALSO
*
****************************************************************************/

VOID
Process_WakeUp( U32 pid )
{
	struct ProcEnv *env = (struct ProcEnv *)pid;

	Forbid();
	if (Process_IsValidPID( pid ))
	{
		Signal( &env->Process->pr_Task, SIGF_SINGLE );
	}
	Permit();
}


/****** Process_IsValidPID ******************************************************
*
*   NAME
*       Process_IsValidPID -- Test if a pid is valid
*
*   SYNOPSIS
*       Process_IsValidPID( pid );
*
*       VOID Process_IsValidPID( U32 );
*
*   FUNCTION
*       Tests if there's a process running with pid.
*
*   INPUTS
*
*   RESULT
*       TRUE if valid, FALSE if not.
*
*   SEE ALSO
*
****************************************************************************/

BOOL
Process_IsValidPID( U32 pid )
{
	struct ProcEnv *env = (struct ProcEnv *)pid, *e2;

	Forbid();
	if (proclistport != NULL)
	{
		DOLIST( (IC_LIST *)&proclistport->mp_MsgList, e2 )
		{
			if (e2 == env)
			{
				Permit();
				return TRUE;
			}
		}
	}
	Permit();

	printf( "WARNING: IsValidPID( 0x%08lx ) failed!\n", pid );
	return FALSE;
}


struct ARexxPort *
Process_GetRexxPort( U32 pid )
{
	struct ProcEnv *env = (struct ProcEnv *)pid;

	if (Process_IsValidPID( pid ))
	{
		return env->RexxPort;
	}

	return NULL;
}

