
/*
 *         PVM version 3.3:  Parallel Virtual Machine System
 *               University of Tennessee, Knoxville TN.
 *           Oak Ridge National Laboratory, Oak Ridge TN.
 *                   Emory University, Atlanta GA.
 *      Authors:  A. L. Beguelin, J. J. Dongarra, G. A. Geist,
 *    W. C. Jiang, R. J. Manchek, B. K. Moore, and V. S. Sunderam
 *                   (C) 1992 All Rights Reserved
 *
 *                              NOTICE
 *
 * Permission to use, copy, modify, and distribute this software and
 * its documentation for any purpose and without fee is hereby granted
 * provided that the above copyright notice appear in all copies and
 * that both the copyright notice and this permission notice appear in
 * supporting documentation.
 *
 * Neither the Institutions (Emory University, Oak Ridge National
 * Laboratory, and University of Tennessee) nor the Authors make any
 * representations about the suitability of this software for any
 * purpose.  This software is provided ``as is'' without express or
 * implied warranty.
 *
 * PVM version 3 was funded in part by the U.S. Department of Energy,
 * the National Science Foundation and the State of Tennessee.
 */

/*
 *	task.c
 *
 *	Task descriptors.
 *
$Log: task.c,v $
 * Revision 1.5  1994/11/08  15:40:59  manchek
 * shared memory cleanup.
 * check if wa_tid is zero before sending message in task_cleanup
 *
 * Revision 1.4  1994/10/15  19:32:49  manchek
 * added log in task_free()
 *
 * Revision 1.3  1994/06/03  20:38:28  manchek
 * version 3.3.0
 *
 * Revision 1.2  1993/11/30  15:55:25  manchek
 * task_free() deletes auth file if one exists
 *
 * Revision 1.1  1993/08/30  23:26:51  manchek
 * Initial revision
 *
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef	SYSVSTR
#include <string.h>
#else
#include <strings.h>
#endif

#include "global.h"
#include "fromlib.h"
#include "ddpro.h"
#include <pvmsdpro.h>
#include "protoglarp.h"
#include "mesg.h"
#include "pkt.h"
#include "task.h"
#include "waitc.h"
#include "pvmalloc.h"
#include "listmac.h"
#include "bfunc.h"


/***************
 **  Globals  **
 **           **
 ***************/

extern void pvmbailout();

extern int busyadding;				/* from ddpro.c */
extern int debugmask;				/* from pvmd.c */
extern int hostertid;				/* from pvmd.c */
extern int myhostpart;				/* from pvmd.c */
extern int mytid;					/* from pvmd.c */
extern int taskertid;				/* from pvmd.c */
extern int tidlmask;				/* from pvmd.c */
extern struct waitc *waitlist;		/* from waitc.c */

struct task *locltasks = 0;			/* all task structs, sorted by tid */

void task_dump();

/***************
 **  Private  **
 **           **
 ***************/

static char rcsid[] = "$Id: task.c,v 1.5 1994/11/08 15:40:59 manchek Exp $";
static char pvmtxt[512];		/* scratch for error log */


/*****************
 **  Interface  **
 **             **
 *****************/

/*	task_init()
*
*	Call once before any other task stuff.
*/

void
task_init()
{
	if (!locltasks) {
		locltasks = TALLOC(1, struct task, "task");
		BZERO((char*)locltasks, sizeof(struct task));
		locltasks->t_link = locltasks;
		locltasks->t_rlink = locltasks;
		locltasks->t_plink = locltasks;
		locltasks->t_prlink = locltasks;
	}
}


/*	tid_new()
*
*	Generates a task id not already in use.
*
*	XXX Be sure to call task_new() with tid before calling
*	tid_new() again.
*/

int
tid_new()
{
	static int lastind = 0;		/* last local-part assigned */

	int startind;				/* to detect when we're out of tids */
	struct task *tp;
	int tid;

	if (++lastind > tidlmask)
		lastind = 1;
	startind = lastind;
	tp = locltasks;

	while (1) {
		tid = myhostpart + lastind;
		while (tp->t_tid < tid)
			if ((tp = tp->t_link) == locltasks)
				break;

		if (tp->t_tid != tid)
			return tid;

		if (++lastind > tidlmask) {
			lastind = 1;
			tp = locltasks;
		}
		if (lastind == startind)
			return -1;
	}
}


/*	task_new()
*
*	Make a new task descriptor, add to list of local tasks but not
*	to by-pid list.
*/

struct task *
task_new(tid)
	int tid;
{
	struct task *tp, *tp2;

	if (!(tp = TALLOC(1, struct task, "task"))) {
		pvmlogerror("task_new() can't get memory\n");
		pvmbailout(0);
	}
	BZERO((char*)tp, sizeof(struct task));
	tp->t_tid = tid;
	tp->t_txq = pk_new(0);
	tp->t_sock = -1;
	tp->t_out = -1;
	tp->t_authfd = -1;
	for (tp2 = locltasks->t_link; tp2 != locltasks; tp2 = tp2->t_link)
		if (tp2->t_tid > tid)
			break;
	LISTPUTBEFORE(tp2, tp, t_link, t_rlink);
	return tp;
}


/*	task_free()
*
*	Do low-level cleanup needed when a task exits.
*	Remove task descriptor from lists and destroy it.
*	Close any fds, unlink any files, free mbufs.
*/

void
task_free(tp)
	struct task *tp;
{
	struct timeval now;
	struct mesg *mp;

	if (debugmask & PDMTASK) {
		sprintf(pvmtxt, "task_free() t%x\n", tp->t_tid);
		pvmlogerror(pvmtxt);
	}
#ifdef SHMEM
	/* XXX this is inside out - mpp_free should call task_free.
	   XXX but for now task_free is what's called.
	   XXX this will change in the portable processor interface cleanup. */
	mpp_free(tp->t_tid);
#endif
	if (tp->t_plink && tp->t_prlink) {
		LISTDELETE(tp, t_plink, t_prlink);
	}
	if (tp->t_link && tp->t_rlink) {
		LISTDELETE(tp, t_link, t_rlink);
	}
	if (tp->t_rxm)
		mesg_unref(tp->t_rxm);
	if (tp->t_rxp)
		pk_free(tp->t_rxp);
	if (tp->t_txq)
		pk_free(tp->t_txq);
	if (tp->t_wait)
		wait_delete(tp->t_wait);
	if (tp->t_authnam) {
		(void)unlink(tp->t_authnam);
		PVM_FREE(tp->t_authnam);
	}
	if (tp->t_sock != -1) {
		wrk_fds_delete(tp->t_sock, 3);
		(void)close(tp->t_sock);
	}
	if (tp->t_out != -1) {
		wrk_fds_delete(tp->t_out, 1);
		(void)close(tp->t_out);
	}
	if (tp->t_outtid) {
		mp = mesg_new(0);
		mp->m_cod = tp->t_outcod;
		mp->m_dst = tp->t_outtid;
		pkint(mp, tp->t_tid);
		pkint(mp, 0);
		sendmessage(mp);
		tp->t_outtid = 0;
	}
	if (tp->t_trctid) {
		mp = mesg_new(0);
		mp->m_cod = tp->t_trccod;
		mp->m_dst = tp->t_trctid;
		gettimeofday(&now, (struct timezone*)0);
		pkint(mp, (int)now.tv_sec);
		pkint(mp, (int)now.tv_usec);
		pkint(mp, tp->t_tid);
		pkint(mp, TEV_ENDTASK);
		pkint(mp, tp->t_status);
		pkint(mp, (int)tp->t_utime.tv_sec);
		pkint(mp, (int)tp->t_utime.tv_usec);
		pkint(mp, (int)tp->t_stime.tv_sec);
		pkint(mp, (int)tp->t_stime.tv_usec);
		sendmessage(mp);
		tp->t_trctid = 0;
	}
	if (tp->t_authfd != -1)
		(void)close(tp->t_authfd);
	if (tp->t_a_out)
		PVM_FREE(tp->t_a_out);
	if (tp->t_mca)
		mca_free(tp->t_mca);
	PVM_FREE(tp);
}


/*	task_find()
*
*	Find a task in local tasks list by its tid.
*/

struct task *
task_find(tid)
	int tid;
{
	struct task *tp;

	if (tid) {
		tp = locltasks->t_link;
		while (tp != locltasks && tp->t_tid < tid)
			tp = tp->t_link;
		if (tp->t_tid == tid)
			return tp;
	}
	return (struct task*)0;
}


/*	task_findpid()
*
*	Find a task in local tasks list by its pid.
*/

struct task *
task_findpid(pid)
	int pid;
{
	struct task *tp;

	tp = locltasks->t_plink;
	while (tp != locltasks && tp->t_pid < pid)
		tp = tp->t_plink;
	return (tp->t_pid == pid) ? tp : (struct task*)0;
}


/*	task_setpid()
*
*	Set the pid for a task, insert it into by-pid list.  Move it
*	if it's already in the list.
*/

void
task_setpid(tp, pid)
	struct task *tp;
	int pid;
{
	struct task *tp2;

	if (tp->t_plink) {
		LISTDELETE(tp, t_plink, t_prlink);
	}
	tp->t_pid = pid;
	for (tp2 = locltasks->t_plink; tp2 != locltasks; tp2 = tp2->t_plink)
		if (tp2->t_pid > pid)
			break;
	LISTPUTBEFORE(tp2, tp, t_plink, t_prlink);
}


/*	task_cleanup()
*
*	Do high-level cleanup needed when a task exits.
*	Wake up any entities waiting on task, free multicast context.
*	XXX should flush any partial messages, but that would be hard.  hm.
*/

void
task_cleanup(tp)
	struct task *tp;
{
	struct mesg *mp;
	struct waitc *wp, *wp2;
	struct task *tp2;

	/* notify anyone who asked */

	if (tp->t_tid) {
		if (debugmask & PDMTASK) {
			sprintf(pvmtxt, "task_cleanup() t%x\n", tp->t_tid);
			pvmlogerror(pvmtxt);
		}
		for (wp = waitlist->wa_link; wp != waitlist; wp = wp->wa_link) {

	/* waits depending on this task */

			if (wp->wa_on == tp->t_tid) {
				switch (wp->wa_kind) {

				case WT_HOSTSTART:
					busyadding = 0;
					free_waitc_add((struct waitc_add *)wp->wa_spec);
					pkint(wp->wa_mesg, PvmDSysErr);
					sendmessage(wp->wa_mesg);
					wp->wa_mesg = 0;
					break;

				case WT_TASKSTART:
					if (wp->wa_tid) {
						if (debugmask & PDMTASK) {
							sprintf(pvmtxt,
								"task_cleanup() tasker t%x takes t%x with it\n",
								tp->t_tid, wp->wa_tid);
							pvmlogerror(pvmtxt);
						}
						if (tp2 = task_find(wp->wa_tid)) {
							wp->wa_tid = 0;
							task_cleanup(tp2);
							task_free(tp2);
						}
					}
					break;

				case WT_TASKX:
					if (wp->wa_tid) {
						mp = mesg_new(0);
						mp->m_dst = wp->wa_tid;
						if (TIDISTASK(mp->m_dst))
							mp->m_cod = wp->wa_dep;
						else {
							mp->m_cod = DM_NOTIFYACK;
							mp->m_wid = wp->wa_dep;
						}
						pkint(mp, tp->t_tid);
						sendmessage(mp);
					}
					break;

				case WT_HOSTA:
					break;

				default:
					sprintf(pvmtxt,
							"task_cleanup() can't deal with wait kind %d\n",
							wp->wa_kind);
					pvmlogerror(pvmtxt);
					break;
				}
				wp2 = wp;
				wp = wp->wa_rlink;
				wait_delete(wp2);
				continue;
			}

	/* waits this task was waiting on */

			if (wp->wa_tid == tp->t_tid) {
				switch (wp->wa_kind) {

				case WT_TASKSTART:
					wp2 = wp;				/* some kinds we can toss now */
					wp = wp->wa_rlink;
					wait_delete(wp2);
					break;

				default:
					wp->wa_tid = 0;			/* in case tid gets recycled */
					break;
				}
			}
		}

		/* notify the scheduler */

		if (tp->t_sched) {
			mp = mesg_new(0);
			mp->m_dst = tp->t_sched;
			mp->m_cod = SM_TASKX;
			if (debugmask & PDMSCHED) {
				sprintf(pvmtxt, "task_cleanup() taskx to t%x status = 0x%x\n",
						tp->t_sched, tp->t_status);
				pvmlogerror(pvmtxt);
			}
			pkint(mp, tp->t_tid);
			pkint(mp, tp->t_status);
			pkint(mp, (int)tp->t_utime.tv_sec);
			pkint(mp, (int)tp->t_utime.tv_usec);
			pkint(mp, (int)tp->t_stime.tv_sec);
			pkint(mp, (int)tp->t_stime.tv_usec);
			sendmessage(mp);
		}

		/* check if it's the hoster */

		if (tp->t_tid == hostertid) {
			if (debugmask & (PDMTASK|PDMSTARTUP)) {
				sprintf(pvmtxt, "task_cleanup() unreg hoster t%x\n", tp->t_tid);
				pvmlogerror(pvmtxt);
			}
			hostertid = 0;
		}

		/* check if it's the tasker */

		if (tp->t_tid == taskertid) {
			if (debugmask & PDMTASK) {
				sprintf(pvmtxt, "task_cleanup() unreg tasker t%x\n", tp->t_tid);
				pvmlogerror(pvmtxt);
			}
			taskertid = 0;
		}
	}

	/* complete multicast */

	if (tp->t_mca) {
/* XXX should send an EOM frag to all rcpts */
		mca_free(tp->t_mca);
		tp->t_mca = 0;
	}
}


void
task_dump()
{
	struct task *tp;
	struct pkt *pp;

	pvmlogerror("task_dump()\n");
	pvmlogerror("     tid     ptid    flags    pid   outtid   trctid\n");
	for (tp = locltasks->t_link; tp != locltasks; tp = tp->t_link) {
		sprintf(pvmtxt, "%8x %8x %8x %6d %8x %8x\n",
				tp->t_tid, tp->t_ptid,
				tp->t_flag, tp->t_pid,
				tp->t_outtid, tp->t_trctid);
		pvmlogerror(pvmtxt);
		pvmlogerror(" txq:pkt      src      dst   flag      len      ofs\n");
		for (pp = tp->t_txq->pk_link; pp != tp->t_txq; pp = pp->pk_link) {
			sprintf(pvmtxt, "%08x %8x %8x %6x %8d %8d\n",
					pp,
					pp->pk_src,
					pp->pk_dst,
					pp->pk_flag,
					pp->pk_len,
					pp->pk_dat - pp->pk_buf);
			pvmlogerror(pvmtxt);
		}
	}
	pvmlogerror("\n");
}

