/* Non pre-empting synchronization kernel, machine-independent portion
 * Copyright 1991 Phil Karn, KA9Q
 */

#undef	PROCTRACE		/* kernel debugging */

#include "global.h"
#include "commands.h"
#ifdef SETPSINFO
#include "timer.h"
#endif
#include "mbuf.h"
#include "proc.h"
#include "socket.h"
#include "daemon.h"
#include "hardware.h"
#ifdef SOUNDS
#include <sys/wait.h>
#endif

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: kernel.c,v 1.24 1997/09/14 14:37:46 root Exp $";
#endif

struct proc *Curproc;		/* Currently running process */
struct proc *Rdytab;		/* Processes ready to run (not including curproc) */
struct proc *Waittab[PHASH];	/* Waiting process list */
struct proc *Susptab;		/* Suspended processes */
static struct proc *Rdytail;
static struct proc *Waittail[PHASH];
static struct proc *Susptail;
static struct mbuf *Killq;

#ifdef SETPSINFO
extern char shortversion[];
#endif


extern void stopalltimers (struct proc *pp);
extern void tnosfcloseall (struct proc *p);
int valid_proc (struct proc *pp);
static void addproc (struct proc * entry);
static void delproc (struct proc * entry);
static int procsigs (void);
static void ksig (volatile void *event, int n);
struct ksig Ksig;
void volatile *lastkernelevent = (void *)0;

/* Create a process descriptor for the main function. Must be actually
 * called from the main function!
 * Note that standard I/O is NOT set up here.
 */
struct proc *
mainproc (const char *name)
{
register struct proc *pp;

	/* Create process descriptor */
	pp = (struct proc *) callocw (1, sizeof (struct proc));

	/* Create name */
	pp->name = strdup (name);

	/* Initialize the magic numbers */
	pp->magic1 = PROC_MAGIC1;
	pp->magic2 = PROC_MAGIC2;

#ifdef UNIX
	pp->stksize = STACKBASE;
	pp->stack = (void *) STACKBASE;
#else	/* UNIX */

#ifdef TNOS_68K
	pp->stksize = _stklen / sizeof (int16);
#endif
#ifdef MSDOS
	pp->stksize = 0;
#endif
	pp->stack = 0;

#endif	/* UNIX */
	/* Make current */
	pp->state = READY;
	Curproc = pp;
	return pp;
}



/* Create a new, ready process and return pointer to descriptor.
 * The general registers are not initialized, but optional args are pushed
 * on the stack so they can be seen by a C function.
 */
struct proc *
newproc (
const char *name,			/* Arbitrary user-assigned name string */
unsigned int stksize,			/* Stack size in words to allocate */
void (*pc) (int, void *, void *),	/* Initial execution address */
int iarg,				/* Integer argument (argc) */
void *parg1,				/* Generic pointer argument #1 (argv) */
void *parg2,				/* Generic pointer argument #2 (session ptr) */
int freeargs				/* If set, free arg list on parg1 at termination */
) {
register struct proc *pp;
int i;

	/* Create process descriptor */
	pp = (struct proc *) callocw (1, sizeof (struct proc));

	/* Create name */
	pp->name = strdup (name);

	/* Initialize the magic numbers */
	pp->magic1 = PROC_MAGIC1;
	pp->magic2 = PROC_MAGIC2;

	/* Allocate stack */
	if (stksize) {
		stksize *= 2;
		stksize = (stksize < 1024) ? 1024 : stksize;
	}
	stksize += 3072;

	stksize = (stksize + 3) & ~3;
	pp->stksize = stksize;
	if ((pp->stack = (int16 *) mallocw (sizeof (int16) * pp->stksize)) == NULL) {
		free (pp->name);
		free ((char *) pp);
		return NULLPROC;
	}
	/* Initialize stack for high-water check */
	for (i = 0; (unsigned) i < stksize; i++)
		pp->stack[i] = STACKPAT;

	pp->func = pc;
	pp->freeargs = freeargs;
	pp->iarg = iarg;
	pp->parg1 = parg1;
	pp->parg2 = parg2;

	/* Task initially runs with interrupts on */
	pp->i_state = 1;

	/* Do machine-dependent initialization of stack */
	psetup (pp);


	/* Inherit creator's input and output sockets */
	(void) usesock (Curproc->input);
	pp->input = Curproc->input;
	(void) usesock (Curproc->output);
	pp->output = Curproc->output;

#ifdef UNIX
	/*
	 * The old "curses" tty driver faked this, occasionally getting it
	 * not quire right (IMHO, but the DOS version did the same implicitly).
	 * The new one uses this pointer to get it "right".
	 *
	 * The session manager uses this because multiple sessions (potentially
	 * all of them!) can be simultaneously "current" (e.g. "xterm" session
	 * manager).  This is even more important with external sessions, which
	 * are *always* "current".
	 */
	pp->session = Curproc->session;
#endif

	/* Add to ready process table */
	pp->state = READY;
	addproc (pp);
	return pp;
}



/* Free resources allocated to specified process. If a process wants to kill
 * itself, the reaper is called to do the dirty work. This avoids some
 * messy situations that would otherwise occur, like freeing your own stack.
 */
void
killproc (register struct proc *pp)
{
char **argv;

	if (!valid_proc (pp))
		return;

	/* Don't check the stack here! Will cause infinite recursion if
	 * called from a stack error
	 */

	if (pp == Curproc)
		killself ();	/* Doesn't return */

	/* Stop alarm clock in case it's running (also done in killself() */
	stop_timer (&pp->alarm);
	stopalltimers (pp);

	/* Close any open sockets and files */
	freesock (pp);
	tnosfcloseall (pp);

	close_s (pp->input);
	close_s (pp->output);

	/* Alert everyone waiting for this proc to die */
	ksignal (pp, 0);

	/* Remove from appropriate table */
	delproc (pp);

	/* Free allocated memory resources */
	if (pp->freeargs == 1) {
		argv = pp->parg1;
		while (pp->iarg-- != 0)
			free (*argv++);
		free (pp->parg1);
	}

	if (pp->freeargs == 2)
		free (pp->parg2);
	free (pp->name);
	free (pp->stack);
	free (pp->outbuf);
	free ((char *) pp);
}



/* Terminate current process by sending a request to the killer process.
 * Automatically called when a process function returns. Does not return.
 */
void
killself ()
{
register struct mbuf *bp;
char *cp;

	if (Curproc != NULLPROC) {
#if 0
		/* Stop alarm clock in case it's running (also done in killproc() */
		stop_timer (&Curproc->alarm);
		stopalltimers (Curproc);
#endif
		bp = pushdown (NULLBUF, sizeof (Curproc));
		memcpy (bp->data, (char *) &Curproc, sizeof (Curproc));
		enqueue (&Killq, bp);
	}

	cp = (char *) mallocw (strlen (Curproc->name) + 1 + strlen (" (zombie)"));
	if (cp)	{
		sprintf (cp, "%s (zombie)", Curproc->name);
		chname (Curproc, cp);
		free (cp);
	}

	/* "Wait for me; I will be merciful and quick." */
	for (;;)
		kwait (NULL);
}



/* Process used by processes that want to kill themselves */
void
killer (int i OPTIONAL, void *v1 OPTIONAL, void *v2 OPTIONAL)
{
struct proc *pp;
struct mbuf *bp;

	server_disconnect_io ();
	for ( ; ; ) {
		while ((volatile struct mbuf *) Killq == NULLBUF)
			kwait (&Killq);
		bp = dequeue (&Killq);
		(void) pullup (&bp, (unsigned char *) &pp, sizeof (pp));
		free_p (bp);
		if (pp != Curproc)	/* We're immortal */
			killproc (pp);
	}
}



/* Inhibit a process from running */
void
suspend (struct proc *pp)
{
	if (!valid_proc (pp))
		return;

	if (pp != Curproc)
		delproc (pp);	/* Running process isn't on any list */
	pp->state |= SUSPEND;
	if (pp != Curproc)
		addproc (pp);	/* kwait will do it for us */
	else
		kwait (NULL);
}



/* Restart suspended process */
void
resume (struct proc *pp)
{
	if (!valid_proc (pp))
		return;

	if (pp != Curproc)
		delproc (pp);		/* Can't be Curproc! */
	pp->state &= ~SUSPEND;
	if (pp != Curproc)
		addproc (pp);	/* kwait will do it for us */
	else
		kwait (NULL);
}



/* Wakeup waiting process, regardless of event it's waiting for. The process
 * will see a return value of "val" from its kwait() call.
 */
void
alert (struct proc *pp, int val)
{
	if (!valid_proc (pp))
		return;

#ifdef	PROCTRACE
	tcmdprintf ("alert(%lx,%u) [%s]\n", ptol (pp), val, pp->name);
#endif
	if (pp != Curproc)
		delproc (pp);
	pp->state &= ~WAITING;
	pp->retval = val;
	pp->event = 0;
	if (pp != Curproc)
		addproc (pp);
	else
		kwait (NULL);
}



/* Post a wait on a specified event and give up the CPU until it happens. The
 * null event is special: it means "I don't want to block on an event, but let
 * somebody else run for a while". It can also mean that the present process
 * is terminating; in this case the wait never returns.
 *
 * Kwait() returns 0 if the event was signaled; otherwise it returns the
 * arg in an alert() call. Kwait must not be called from interrupt level.
 *
 * Before waiting and after giving up the CPU, kwait() processes the signal
 * queue containing events signaled when interrupts were off. This means
 * the process queues are no longer modified by interrupt handlers,
 * so it is no longer necessary to run with interrupts disabled here. This
 * greatly improves interrupt latencies.
 */
int
kwait (volatile void *event)
{
struct proc *oldprocptr;
int tmp;
int i_state;

	Ksig.kwaits++;

	/* Enable interrupts, after saving the current state.
	 * This minimizes interrupt latency since we may have a lot
	 * of work to do. This seems safe, since care has been taken
	 * here to ensure that signals from interrupt level are not lost, e.g.,
	 * if we're waiting on an event, we post it before we scan the
	 * signal queue.
	 */
	i_state = istate();
	if (!i_state)
		Ksig.kwaitints++;
	(void) enable ();
	
	if(event != NULL){
		/* Post a wait for the specified event */
		Curproc->event = event;
		Curproc->state = WAITING;
		addproc(Curproc);	/* Put us on the wait list */
	}
	/* If the signal queue contains a signal for the event that we're
	 * waiting for, this will wake us back up
	 */
	(void) procsigs();
	if (event == NULL){
		/* We remain runnable */
		if (Rdytab == NULL) {
			/* Nothing else is ready, so just return */
			Ksig.kwaitnops++;
			restore(i_state);
			return 0;
		}
		addproc(Curproc); /* Put us on the end of the ready list */
	}
	/* Look for a ready process and run it. If there are none,
	 * loop or halt until an interrupt makes something ready.
	 */
	while(Rdytab == NULL){
		/* Give system back to upper-level multitasker, if any.
		 * Note that this function enables interrupts internally
		 * to prevent deadlock, but it restores our state
		 * before returning.
		 */
		giveup();
		/* Process signals that occurred during the giveup() */
		(void) procsigs();
	}
	/* Remove first entry from ready list */
	oldprocptr = Curproc;
	Curproc = Rdytab;
	delproc(Curproc);

	/* Now do the context switch.
	 * This technique was inspired by Rob, PE1CHL, and is a bit tricky.
	 *
	 * First save the current process's state. Then if
	 * this is still the old process, load the new environment. Since the
	 * new task will "think" it's returning from the setjmp() with a return
	 * value of 1, the comparison with 0 will bypass the longjmp(), which
	 * would otherwise cause an infinite loop.
	 */
#ifdef	PROCTRACE
	if (strcmp (oldprocptr->name, Curproc->name) != 0)
		tcmdprintf ("kwait -> %s(%d)\n", Curproc->name, !!Curproc->i_state);
#endif
	/* Save old state */
	if (oldprocptr)	{
		oldprocptr->i_state = 0;
		if(i_state)
			oldprocptr->i_state = 1;
	}
	if(!oldprocptr || setjmp(oldprocptr->env) == 0){
		/* We're still running in the old task; load new task context.
		 * The interrupt state is restored here in case longjmp
		 * doesn't do it (e.g., systems other than Turbo-C).
		 */
		restore(Curproc->i_state);
		longjmp(Curproc->env,1);
	}
	/* At this point, we're running in the newly dispatched task */
	tmp = Curproc->retval;
	Curproc->retval = 0;

	/* Also restore the true interrupt state here, in case the longjmp
	 * DOES restore the interrupt state saved at the time of the setjmp().
	 * This is the case with Turbo-C's setjmp/longjmp.
	 */
	restore(Curproc->i_state);

#if 0
	/* If an exception signal was sent and we're prepared, take it */
	if((Curproc->flags.sset) && tmp == Curproc->signo)
		longjmp(Curproc->sig,1);
#endif

	/* Otherwise return normally to the new task */
	return tmp;
}



int
ksignal (volatile void *event, int n)
{
int cnt;

	if(istate())	{
		/* Interrupts are on, just call ksig directly after
		 * processing the previously queued signals
		 */
		cnt = procsigs ();
		ksig (event, n);
		return cnt;
	}

	/* Interrupts are off, so quickly queue event */
	Ksig.ksigsqueued++;

 	/* Ignore duplicate signals to protect against a mad device driver
	 * overflowing the signal queue
	 */
	if(event == lastkernelevent && Ksig.nentries != 0){
		Ksig.duksigs++;
		return 0;
	}
	
	if(Ksig.nentries == SIGQSIZE){
		/* It's hard to handle this gracefully */
		Ksig.lostsigs++;
		return 0;
	}
	
	lastkernelevent = Ksig.wp->event = event;
	Ksig.wp->n = n;
	if (++Ksig.wp > &Ksig.entry[SIGQSIZE - 1])
		Ksig.wp = Ksig.entry;
	Ksig.nentries++;
	return 0;
}



static int
procsigs(void)
{
int cnt = 0;
int tmp;
int i_state;
#ifndef MSDOS
static time_t nextsecond = 0;
time_t thissecond;
#endif

	for ( ; ; )	{
		/* Atomic read and decrement of entry count */
		i_state = disable ();
		tmp = Ksig.nentries;
		if (tmp != 0)
			Ksig.nentries--;
		restore(i_state);
		
		if (tmp == 0)
			break;
		ksig (Ksig.rp->event, Ksig.rp->n);
		if (++Ksig.rp > &Ksig.entry[SIGQSIZE - 1])
			Ksig.rp = Ksig.entry;
		cnt++;
	}
	
	if (cnt > Ksig.maxentries)
		Ksig.maxentries = cnt;	/* Record high water mark */

#ifndef MSDOS
	/* While this may NOT be the best place for these, this routine
	   will NOT accidentally get killed or be omitted to be compiled
	   in, so it makes the best place (for the time being). */
	thissecond = time ((time_t *)0);
	if (nextsecond < thissecond)	{
#ifdef SOUNDS
		/* this is to clean up zombie processes from playing sounds */
		(void) waitpid (0, NULL, WNOHANG);
#endif
#ifdef SETPSINFO
		{
			char buf[64];

			sprintf (buf, "%s (%sRegistered) - UP: %s", shortversion, (is_registered ())? "" : "Not ", tformat (secclock ()));
			setprocname (buf);
		}
#endif
		nextsecond = thissecond + 5;
	}
#endif
	return cnt;
}



/* Make ready the first 'n' processes waiting for a given event. The ready
 * processes will see a return value of 0 from kwait().  Note that they don't
 * actually get control until we explicitly give up the CPU ourselves through
 * a kwait(). ksig is now called from pwait, which is never called at
 * interrupt time, so it is no longer necessary to protect the proc queues
 * against interrupts. This also helps interrupt latencies considerably.
 */
static void
ksig(
volatile void *event,	/* Event to signal */
int n		/* Max number of processes to wake up */
){
struct proc *pp;
struct proc *pnext;
unsigned int hashval;
int cnt = 0;

	Ksig.ksigs++;

	if(event == NULL){
		Ksig.ksignops++;
		return;		/* Null events are invalid */
	}

	/* n == 0 means "signal everybody waiting for this event" */
	if(n == 0)
		n = 65535;

	hashval = phash(event);
	for (pp = Waittab[hashval]; n != 0 && pp != NULLPROC; pp = pnext)	{
		pnext = pp->next;
		if(pp->event == event){
#ifdef	PROCTRACE
			tcmdprintf ("ksignal(%lx,%u) wake waiting %lx [%s]\n", ptol (event), n,
				 ptol (pp), pp->name);
#endif
			delproc (pp);
			pp->state &= ~WAITING;
			pp->retval = 0;
			pp->event = NULL;
			addproc (pp);
			n--;
			cnt++;
		}
	}
	for (pp = Susptab; n != 0 && pp != NULLPROC; pp = pnext)	{
		pnext = pp->next;
		if(pp->event == event){
#ifdef	PROCTRACE
			tcmdprintf ("ksignal(%lx,%u) wake suspended %lx [%s]\n", ptol (event), n,
				 ptol (pp), pp->name);
#endif /* PROCTRACE */
			delproc (pp);
			pp->state &= ~WAITING;
			pp->event = 0;
			pp->retval = 0;
			addproc (pp);
			n--;
			cnt++;
		}
	}
	if(cnt == 0)
		Ksig.ksignops++;
	else
		Ksig.ksigwakes += (unsigned long) cnt;
}



/* Rename a process */
void
chname (struct proc *pp, const char *newname)
{
	free (pp->name);
	pp->name = strdup (newname);
}



int
valid_proc (struct proc *pp)
{
	if (pp == NULLPROC)
		return 0;
	if (pp->magic1 != PROC_MAGIC1 || pp->magic2 != PROC_MAGIC2)
		return 0;
	if (pp->state > (SUSPEND + 1))
		return 0;
	return 1;
}



/* Remove a process entry from the appropriate table */
static void
delproc (entry)
register struct proc *entry;	/* Pointer to entry */
{
int i_state;
struct proc *next = NULLPROC;
struct proc *prev = NULLPROC;

	if (!valid_proc (entry))
		return;

	i_state = disable ();

	if (valid_proc (entry->prev))
		prev = entry->prev;

	if (valid_proc (entry->next))	{
		entry->next->prev = prev;
		next = entry->next;
	}

	if (prev != NULLPROC)
		prev->next = next;
	else {
		switch (entry->state) {
			case READY:
				Rdytab = next;
				break;
			case WAITING:
				Waittab[phash (entry->event)] = next;
				break;
			case SUSPEND:
			case SUSPEND | WAITING:
				Susptab = next;
				break;
			default:
				break;
		}
	}
	entry->next = entry->prev = NULLPROC;

	switch (entry->state) {
		case READY:
			if (entry == Rdytail)
				Rdytail = prev;
			break;
		case WAITING:
			if (entry == Waittail[phash (entry->event)])
				Waittail[phash (entry->event)] = prev;
			break;
		case SUSPEND:
		case SUSPEND | WAITING:
			if (entry == Susptail)
				Susptail = prev;
			break;
		default:
			break;
	}

	restore (i_state);
}



/* Append proc entry to end of appropriate list */
static void
addproc (entry)
register struct proc *entry;	/* Pointer to entry */
{
struct proc **head, **tail;
int i_state;

	if (!valid_proc (entry))
		return;

	switch (entry->state) {
		case READY:
			head = &Rdytab;
			tail = &Rdytail;
			break;
		case WAITING:
			head = &Waittab[phash (entry->event)];
			tail = &Waittail[phash (entry->event)];
			break;
		case SUSPEND:
		case SUSPEND | WAITING:
			head = &Susptab;
			tail = &Susptail;
			break;
		default:
			head = 0;	/* silence warning */
			tail = 0;
			break;
	}
	if (head) {
		entry->next = NULLPROC;
		i_state = disable ();
		if (!valid_proc (*head)) {
			if (*head && shall_we_crash())
				crash_it_already ("corrupted process table");
			/* Empty list, stick at beginning */
			entry->prev = NULLPROC;
			*head = entry;
			if (tail)		/* should always be true */
				*tail = entry;
		} else {
			if (tail)	{	/* should always be true */
				if (!valid_proc(*tail))
					crash_it_already ("corrupted process table");
				(*tail)->next = entry;
				entry->prev = *tail;
				*tail = entry;
			}
		}
		restore (i_state);
	}
}


#ifdef MSDOS
END_OF_FUNCTION(ksignal)
#endif
