
/*
 *           PVM 3.2:  Parallel Virtual Machine System 3.2
 *               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 3.2 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.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 "ddpro.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 myhostpart;				/* 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.1 1993/08/30 23:26:51 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;
{
	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)
		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) {
		struct mesg *mp;

		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_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;

	/* notify anyone who asked */

	if (tp->t_tid) {
		for (wp = waitlist->wa_link; wp != waitlist; wp = wp->wa_link) {
			if (wp->wa_on == tp->t_tid) {
				switch (wp->wa_kind) {

				case WT_TASKX:
					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;

				default:
					break;
				}
				wp2 = wp;
				wp = wp->wa_rlink;
				wait_delete(wp2);
				continue;
			}
			if (wp->wa_tid == tp->t_tid)
				wp->wa_tid = 0;				/* in case tid gets recycled */
		}
	}

	/* 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 %8x %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");
}

