/* 2Tasks.C	James M Synge,	May 18, 1987		  */

/* Include files */
#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/tasks.h"
#include "exec/libraries.h"
#include "exec/ports.h"

/* Other declarations	*/

struct Library  *TaskBase, /* Library Base Pointer. */
		*OpenLibrary();

struct MsgPort	*CreatePort(), *FindPort();
struct Message	*WaitPort(), *GetMsg();

#define TASK_LIBRARY	"task.library"
#define	TASK_VERSION	1L

#define CHILD_PORT	"2Tasks.Child.Port"
#define CHILD_TASK	"2Tasks.Child.Name"

main()
{
	struct Message message, *msg;
	struct MsgPort *ParentPort, *ChildPort;
	struct Task *ChildTask, *CreateTask();
	void ChildMain();
	int seconds;	

	/* First things first: Open the task library: */

	printf("Openning %s\n", TASK_LIBRARY);
	TaskBase = OpenLibrary(TASK_LIBRARY, TASK_VERSION);
	if (TaskBase == 0L) {
		printf("Unable to open %s\n", TASK_LIBRARY);
		exit(10);
	}

	/* Create a nameless MsgPort where we can receive
	 * the reply to a message.
	 */
	if ((ParentPort = CreatePort( 0L, 0L )) == 0L) {
		printf("Unable to create a MsgPort!\n");
		CloseLibrary( TaskBase );
		exit(10);
	}

	/* Now create the child task. */

	printf("Creating the child task.\n");
	ChildTask = CreateTask(
		CHILD_TASK,	/* Name of the task.	*/
		1L,		/* Higher priority.	*/
		ChildMain,	/* Its main routine.	*/
		4096L);		/* Stack Size.		*/

	if (ChildTask == 0L) {
		printf("Unable to create child task!\n");
		DeletePort( ParentPort );
		CloseLibrary( TaskBase );
		exit(10);
	}

	/* Find the child's message port. */

	for(seconds = 0; seconds < 60; seconds++) {
		printf("Find the child's MsgPort\n");
		if (ChildPort = FindPort( CHILD_PORT ))
			break;
		Delay(50);	/* Wait a second! */
	}

	if (ChildPort == 0L) {
		printf("Unable to find child MsgPort!\n");
		DeleteTask( ChildTask );
		DeletePort( ParentPort );
		CloseLibrary( TaskBase );
		exit(10);
	}

	/* Send the child a message. */

	message.mn_Node.ln_Type	= NT_MESSAGE;
	message.mn_ReplyPort	= ParentPort;
	message.mn_Length	= 0;

	printf("Sending the message.\n");
	PutMsg( ChildPort, &message );

	/* Wait for the child to respond. */

	WaitPort( ParentPort );

	/* Get the message. */

	msg = GetMsg( ParentPort );
	printf("Got the reply.  All done.\n");
	
	/* And now delete the MsgPort we used. */

	DeletePort( ParentPort );
	
	/* Finally, close the library. */
	
	CloseLibrary( TaskBase );

	exit(0);
}

/* Notice that the child doesn't open any libraries,
 * including exec.library and Task.Library whose
 * routines it uses. It does this because it will
 * operate solely during the life of the parent, when we
 * know the libraries will be open.  This is not kosher in
 * general, but Commodore has produced examples doing this,
 * and I know there aren't any problems in this case.
 *
 * It's also particularly difficult to close a library
 * after using DeleteTask() on yourself!
 */
void ChildMain()
{
	struct Message *msg;
	struct MsgPort *ChildPort;

	/* To allow this to be a small code/small data model
	 * task, we must make sure register A4 contains the
	 * correct value.  We do so by calling the Aztec C
	 * routine geta4() which computes the value.
	 */
	geta4();

	/* Create a MsgPort where we can receive a message
	 * from the parent task.
	 */
	if ((ChildPort = CreatePort( CHILD_PORT, 0L )) == 0)
		DeleteTask( FindTask( 0L ));
		
	/* Now wait for the message. */
 
	WaitPort( ChildPort );

	/* Fetch it from the port... */

	msg = GetMsg( ChildPort );

	/* ... and reply to it.  Do so inside a Forbid() /
	 * Permit() pair so there'll be time to delete the
	 * message port.  Note that the Permit() call is
	 * not included because it will never be called;
	 * instead, DeleteTask() is called so that we
	 * delete the current task: ourselves!
	 */
	Forbid();

	ReplyMsg( msg );

	DeletePort( ChildPort );

	/* Now take the leap of death. */

	DeleteTask( FindTask( 0L ));

	/* That's all she wrote! */
}
