/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*---------------------------------------------------------------------------
 *		
 * MODULE NAME:	rciAbort
 *
 * DESCRIPTION:	Routines to implement the rciAbort() call.
 *
 * AUTHOR/DATE:	John Lloyd, Fall 1987
 *
 * MODIFICATION HISTORY:
 *
 *--------------------------------------------------------------------------*/

#include <signal.h>

#include <varargs.h>

#include "../h/rciTask.h"
#include "../h/mutex.h"
#include "../h/errorCodes.h"

#include "../rci/abort.h"

extern ABORT_DESC rciAbortDesc;
extern MUTEX rciAbortMutex;
extern int rciAbortFlag;

extern ABORT_DESC rciFatalDesc;
extern MUTEX rciFatalMutex;
extern char rciFatalFlag;

extern int rciCpu;
extern int rciTaskId;
extern int rciNumTasks;
extern RCI_DESC rciTaskTable[MAX_RCI_TASKS];

static ABORT_DESC *lockAbortDesc(fxnName, code)
char *fxnName;
int code;
{
	/* Called by the abort functions to get the descriptor into
	   which they will write their abort error information. It 
	   is possible that this call will fail, which means that another
	   abort call has already occured and we don't need to bother
	   with this one. 

	   Exclusive access to the descriptor is done using a 'test and set'.

	   If the error code is EInternal or EFatal, make sure we
	   use the 'fatal' descriptor, which will guarantee a program
	   exit ... we will not call an application supplied
	   abort handler in such circumstances. */	 

	if (code == EInternal || code == EFatal)
	 { if (!(MUTEX_VALID(&rciFatalMutex)))
	    { rciPrintf ("ERROR in abort: bad fatal mutex\n");
	      return (NULL);
	    }
	   else
	    { if (testAndSet (&rciFatalFlag, &rciFatalMutex) == 0)
	       { return (NULL);
	       }
	    }
	   return (&rciFatalDesc);
	 }
	else
	 { if (!(MUTEX_VALID(&rciAbortMutex)))
	    { mutexAbort (fxnName, &rciAbortMutex);
	      return (NULL);
	    }
	   else
	    { if (testAndSet (&rciAbortFlag, &rciAbortMutex) == 0)
	       { return (NULL);
	       }
	    }
	   return (&rciAbortDesc);
	 }
}

rciAbort (va_alist)
va_dcl
{
	char *fxnName = "rciAbort()";

	int code;
	char *format;
	va_list ap;
	ABORT_DESC *desc;
	int id;

	va_start(ap);
	code = va_arg(ap, int);
	format = va_arg(ap, char*);

	if ((desc = lockAbortDesc(fxnName, code)) == NULL)
	 { va_end (ap);
	   return (0);
	 }

	/* Write the abort information into the descriptor for processing
	   later by the SIGHUP handler. */

	desc->taskId = rciTaskId;
	desc->cpu = rciCpu;
       	desc->code = code;
	vsnprintf (desc->buf, ABORT_BUF_SIZE, format, ap);
	desc->buf[ABORT_BUF_SIZE] = '\0';

	/* Tell all RCI tasks to abort. */
	for (id=1; id<rciNumTasks; id++)
	 { rciTaskTable[id].tcb->abortReq = (rciTaskId + 1);
	 }

	/* Signal the planning level. */
	rciSignal (SIGHUP);

	va_end (ap);
	return (1);
}

rciAbortErrors()
{
	/* Like rciAbort(), except that the error code(s) and message(s)
	   are obtained from the task's error stack.
	 */

	char *fxnName = "rciAbortErrors()";

	int code;
	ABORT_DESC *desc;
	int numErrs;
	int id;

	code = ((numErrs = numErrors()) > 0) ? getErrorCode(0) : 0;

	if ((desc = lockAbortDesc(fxnName, code)) == NULL)
	 { return (0);
	 }

	/* Write the abort information into the descriptor for processing
	   later by the SIGHUP handler. */

	desc->taskId = rciTaskId;
	desc->cpu = rciCpu;
       	desc->code = code;

	if (numErrs > 0)
	 { dumpErrorMsgs (desc->buf, ABORT_BUF_SIZE);
	 }
	else
	 { strcpy (desc->buf, "(rciAbortErrors() -- error stack is empty)\n");
	 }

	/* Tell all RCI tasks to abort. */
	for (id=1; id<rciNumTasks; id++)
	 { rciTaskTable[id].tcb->abortReq = (rciTaskId + 1);
	 }

	/* Signal the planning level. */
	rciSignal (SIGHUP);

	return (1);
}

rciClearAbort ()
{
	/* Called by the planning level to reset the abort flags after an 
	   abort has been received and processed, and we are ready to 'start
	   again'. */

	rciAbortFlag = 0;
	mutexClear (&rciAbortMutex);
	rciFatalFlag = 0;
	mutexClear (&rciFatalMutex);
}

mutexAbort (s, mutex)
char *s;
MUTEX *mutex;
{
	/* Called when a mutual exclusion flag is bad */

	rciAbort (EFatal, "%s -- bad mutex at address 0x%x\n", s, mutex);
}

