/*
 *	bsyscalls.c
 *	Blocking system calls
 *	Copyright (C) 2000-2005 Fred Barnes <frmb@kent.ac.uk>
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 2 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(OOS_BUILD)
/*{{{  RMoX/OOS stuff*/


#include <oos_funcs.h>

/*{{{  void _killcall (int *w)*/
/*
 *	dummy for OOS
 */
void _killcall (int *w)
{
	oos_printk ("PANIC:: _killcall()\n");
	return;
}
/*}}}*/

/*}}}*/
#else	/* !OOS_BUILD */

#ifdef BLOCKING_SYSCALLS
/*{{{  includes*/
#define __BSYSCALLS_C

#include <stdio.h>
#include <string.h>
/* #include <stdlib.h> */
#include <stdarg.h>
#include <setjmp.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef DMEM_USE_MMAP
	#include <sys/mman.h>
#endif
#include <sys/types.h>

#ifdef USE_PTHREADS
	#include <pthread.h>
	#include <semaphore.h>
#else
	#include <sched.h>
	#include <sys/ipc.h>
	#include <sys/sem.h>
#endif
#include <signal.h>
#include <errno.h>

#include <sched_consts.h>
#include <arch/sched_types.h>
#include <arch/sched.h>
#include <arch/sched_asm_inserts.h>
#include <arch/occam_io.h>
#include <queue_defs.h>

#include <arch/atomics.h>

#include <arch/bsyscalls.h>
#include <rts.h>
#include <dmem_if.h>

#ifdef LINUX_CSPDRIVER
	#include <arch/cspdriver.h>
#endif


/*}}}*/
/*{{{  prototypes and structures*/

#ifndef USE_PTHREADS
/* will probably need to declare __clone for ourselves */
extern int __clone (int (*fn)(void *arg), void *child_stack, int flags, void *arg);
#endif

#ifndef USE_PTHREADS
/* Make sure we have a nice definition of semaphore control.. */
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
	/* union semun is defined in sys/sem.h */
#else
	union semun {
		int val;
		struct semid_ds *buf;
		unsigned short int *array;
		struct seminfo *__buf;
	};
#endif
#endif	/* !USE_PTHREADS */
/*}}}*/

/*{{{  global vars*/

/* job free-list */
volatile bsc_jobinfo *fl_qhead;
spl_t fl_lock;


/* job collect queue */
volatile bsc_jobinfo *collect_qhead, *collect_qtail;
spl_t collect_lock;

#ifdef USE_PTHREADS
int bsyscalls_dead_flag = 0;
#endif

#ifdef LINUX_CSPDRIVER
volatile bsc_thread *volatile q_mfptr, *volatile q_mbptr;
#endif

#ifdef BLOCKING_PROFILE
	volatile int bp_dispatch_trigger[NR_BUCKETS];
	volatile int bp_trigger_finish[NR_BUCKETS];
	volatile int bp_kill_finish[NR_BUCKETS];
	volatile int bp_finish_collect[NR_BUCKETS];
	extern volatile long long signal_time;	/* in sched.c */

#endif	/* BLOCKING_PROFILE */

/*}}}*/
/*{{{  private data*/

/* job dispatch queue */
static volatile bsc_jobinfo *dispatch_qhead, *dispatch_qtail;
static spl_t dispatch_lock;

#if 0
static volatile bsc_thread dispatching;
static volatile bsc_thread *v_dispatching;
static spl_t dispatch_lock;
#endif


#ifdef USE_PTHREADS
static bsc_thread *all_threads;

static volatile bsc_thread *dead_threads;
static spl_t dead_lock;

static volatile int kill_all_pthreads = 0;
#else	/* !USE_PTHREADS */
static volatile int clone_pids[MAX_NUM_CLONES];
static volatile bsc_thread *volatile clone_arry[MAX_NUM_CLONES];

#endif	/* !USE_PTHREADS */

static int occam_proc_pid;
#ifdef USE_PTHREADS
static sem_t *clone_sem;		/* array of pthread sem_t's */
#else
static int clone_sem;
#endif
static int bsc_uses_cspdriver = 0;

/* one semaphore in the set 'clone_sem' */
#define NOTIFY_SEM 0		/* to notify a clone */

static spl_t idle_lock;		/* for protecting various vars */
static int num_clones = 0;
static volatile int num_idle = 0;


/*}}}*/

/*{{{  prototypes and defines*/
/* Prototypes */

static int create_clone (void);
static int clone_entry (void *arg);
#ifdef USE_PTHREADS
static int set_semaphores (sem_t *semid, int n_sems, ...);
static int claim_semaphore (sem_t *semid, int semnum);
static int notify_semaphore (sem_t *semid, int semnum);
#else
static int set_semaphores (int semid, int n_sems, ...);
static int claim_semaphore (int semid, int semnum);
static int notify_semaphore (int semid, int semnum);
#endif

#ifndef USE_PTHREADS
static void clone_bad_signal (int sig);
static void clone_vbad_signal (int sig);
static void clone_abort (int sig);
#endif

#ifdef USE_PTHREADS
#define CLONE_FSTR	"THR-%d,WS-%p,R-%p,A-%p,F-%p"
#define CLONE_PARAMS(X)	(int)(X->me), (X->job ? X->job->ws_ptr : NULL), (X->job ? X->job->raddr : NULL), (X->job ? X->job->ws_arg : NULL), (X->job ? X->job->func : NULL)
#else
#define CLONE_FSTR	"P-%d,T-%d,S-%d,WS-%p,R-%p,A-%p,F-%p"
#define CLONE_PARAMS(X)	X->pid, X->thr_num, X->par_sync, (X->job ? X->job->ws_ptr : NULL), (X->job ? X->job->raddr : NULL), (X->job ? X->job->ws_arg : NULL), (X->job ? X->job->func : NULL)
#endif
/*}}}*/

/*{{{  int bsyscalls_create_clones (void)*/
/*
 *	int bsyscalls_create_clones (void)
 *	Creates the clones (and sets them off)
 *	Returns 0 on success, -1 on failure
 */
int bsyscalls_create_clones (void)
{
	int x;

	spl_init (&fl_lock);
	spl_init (&dispatch_lock);
	spl_init (&collect_lock);
	spl_init (&idle_lock);
#ifdef USE_PTHREADS
	spl_init (&dead_lock);

	spl_lock (&dead_lock);
	bsyscalls_dead_flag = 0;
	dead_threads = NULL;
	kill_all_pthreads = 0;
	spl_unlock (&dead_lock);
#endif

	spl_lock (&fl_lock);
	fl_qhead = NULL;
	spl_unlock (&fl_lock);

	spl_lock (&dispatch_lock);
	dispatch_qhead = NULL;
	dispatch_qtail = NULL;
	spl_unlock (&dispatch_lock);

	num_idle = 0;

	#if 0 && defined(LINUX_CSPDRIVER)
	if (cspdriver_initialise_bsc_support ()) {
		v_dispatching = &dispatching;
		q_mfptr = NULL;
		q_mbptr = NULL;
		bsc_uses_cspdriver = 0;
	} else {
		bsc_uses_cspdriver = 1;
		/* oki, get count and allocate buffers */
		x = cspdriver_bsc_num_buffers ();
		if (x <= 0) {
			v_dispatching = &dispatching;
			bsc_uses_cspdriver = 0;
		} else {
			int asize = bsc_commsize (dispatching);
			bsc_thread *tmp, *last;

			x *= 2;		/* need twice as many at least */
			/* allocate extra one first */
			v_dispatching = (bsc_thread *)dmem_alloc (asize);
			last = (bsc_thread *)v_dispatching;
			for (x++; x > 0; x--) {

				tmp = (bsc_thread *)dmem_alloc (asize);
				*((bsc_thread **)last) = tmp;
				last = tmp;
			}
			/* NULL end link */
			*((bsc_thread **)last) = NULL;
		}
	}
	#else
	bsc_uses_cspdriver = 0;
	#endif

	#ifdef BLOCKING_PROFILE
		/* zero buckets */
		for (x=0; x<NR_BUCKETS; x++) {
			bp_dispatch_trigger[x] = 0;
			bp_trigger_finish[x] = 0;
			bp_kill_finish[x] = 0;
			bp_finish_collect[x] = 0;
		}
	#endif

	occam_proc_pid = getpid ();

	if (!bsc_uses_cspdriver) {
		#ifdef USE_PTHREADS
			clone_sem = (sem_t *)dmem_alloc (2 * sizeof (sem_t));
		#else
			clone_sem = semget (IPC_PRIVATE, 2, 0600);
			if (clone_sem < 0) {
				fprintf (stderr, "KRoC: bsyscalls: unable to create semaphores: %s\n", strerror(errno));
				return -1;
			}
		#endif
		if (set_semaphores (clone_sem, 1, 0, 0)) {
			fprintf (stderr, "KRoC: bsyscalls: unable to set semaphore values.\n");
			return -1;
		}
	}

	for (x=0; x<NUM_CLONES; x++) {
		if (create_clone ()) {
			return -1;
		}
	}
	return 0;
}
/*}}}*/
/*{{{  void bsyscalls_destroy_clones (void)*/
/*
 *	void bsyscalls_destroy_clones (void)
 *	Destroyes the clones (sends a SIGKILL)
 */
void bsyscalls_destroy_clones (void)
{
	int x;
#if !defined(USE_PTHREADS)
	union semun dummy;
#endif
	int countdown;

#if 0
fprintf (stderr, "KRoC: bsyscalls[pthreads]: cleaning up, PID = %d, occam_proc_pid = %d\n", getpid(), occam_proc_pid);
#endif
	/* Sanity ? -- we could get in here by a signal which causes entry to somewhere in the RTS */
	if (getpid() != occam_proc_pid) {
#if defined(USE_PTHREADS)
		pthread_exit ((void *)0);
#else
		_exit (0);
#endif
	}

	/* Go and kill the others */
#if defined(USE_PTHREADS)
	spl_lock (&collect_lock);
	collect_qhead = NULL;
	collect_qtail = NULL;
	spl_unlock (&collect_lock);

	for (x=0; x<num_clones + 1; x++) {
		/* wake up any sleeping processes */
		if (sem_post (&(clone_sem[NOTIFY_SEM])) != 0) {
			#ifdef BLOCKING_DEBUG
				fprintf (stderr, "KRoC: bsyscalls[pthreads] (%d): unable to post semaphore\n", (int)pthread_self ());
			#endif
		}
	}
	sem_destroy (&(clone_sem[NOTIFY_SEM]));

	/* set a flag that other threads can see */
	kill_all_pthreads = 1;
	barrier ();

	{
		bsc_thread *tmp = all_threads;

		while (tmp) {
			void *thrret;

#if 0
fprintf (stderr, "KRoC: bsyscalls_destroy_clones(): cancelling thread %d (i am thread %d [%d])\n", (int)(tmp->me), (int)pthread_self(), getpid());
#endif
			if (pthread_cancel (tmp->me) != 0) {
				#ifdef BLOCKING_DEBUG
					fprintf (stderr, "KRoC: bsyscalls[pthreads] (%d): unable to cancel thread\n", (int)pthread_self ());
				#endif
			}
			pthread_join (tmp->me, &thrret);
			tmp = tmp->qnext;
		}
		all_threads = NULL;
	}
#else	/* !defined(USE_PTHREADS) */
	/* This should release processes blocking on the semaphore */
	if (semctl (clone_sem, 0, IPC_RMID, dummy) < 0) {
		#ifdef BLOCKING_DEBUG
			fprintf (stderr, "KRoC: bsyscalls (%d): unable to destroy semaphore set: %s\n", getpid(), strerror(errno));
		#endif
	}
	for (x=0; x<num_clones; x++) {
		#ifdef BLOCKING_DEBUG
			fprintf (stderr, "KRoC: bsyscalls (%d): destroying clone %d (pid=%d)\n", getpid(), x, clone_pids[x]);
		#endif
		if (clone_pids[x] > 0) {
			kill (clone_pids[x], SIGKILL);
		}
	}
#endif	/* !defined(USE_PTHREADS) */

	/* free jobs on any of the queues */
	for (countdown = 5; countdown && spl_lock_or_fail (&fl_lock); countdown--);
	while (fl_qhead) {
		bsc_jobinfo *next = fl_qhead->next;

		dmem_release ((void *)fl_qhead);
		fl_qhead = next;
	}
	if (countdown) {
		spl_unlock (&fl_lock);
	}
	for (countdown = 5; countdown && spl_lock_or_fail (&dispatch_lock); countdown--);
	while (dispatch_qhead) {
		bsc_jobinfo *next = dispatch_qhead->next;

		dmem_release ((void *)dispatch_qhead);
		dispatch_qhead = next;
	}
	if (countdown) {
		spl_unlock (&dispatch_lock);
	}
	for (countdown = 5; countdown && spl_lock_or_fail (&collect_lock); countdown--);
	while (collect_qhead) {
		bsc_jobinfo *next = collect_qhead->next;

		dmem_release ((void *)collect_qhead);
		collect_qhead = next;
	}
	if (countdown) {
		spl_unlock (&collect_lock);
	}

	#ifdef BLOCKING_PROFILE
		/* dump histogram buckets */
		for (x=0; x<LAST_BUCKET; x++) {
			fprintf (stderr, "%d ", bp_dispatch_trigger[x]);
		}
		fprintf (stderr, "%d\n", bp_dispatch_trigger[LAST_BUCKET]);
		for (x=0; x<LAST_BUCKET; x++) {
			fprintf (stderr, "%d ", bp_trigger_finish[x]);
		}
		fprintf (stderr, "%d\n", bp_trigger_finish[LAST_BUCKET]);
		for (x=0; x<LAST_BUCKET; x++) {
			fprintf (stderr, "%d ", bp_kill_finish[x]);
		}
		fprintf (stderr, "%d\n", bp_kill_finish[LAST_BUCKET]);
		for (x=0; x<LAST_BUCKET; x++) {
			fprintf (stderr, "%d ", bp_finish_collect[x]);
		}
		fprintf (stderr, "%d\n", bp_finish_collect[LAST_BUCKET]);
	#endif
	return;
}
/*}}}*/
/*{{{  static int create_clone (void)*/
/*
 *	int create_clone (void)
 *	Creates a clone process, sets it up and runs into the occam kernel
 */
static int create_clone (void)
{
	void *c_stack;
	volatile bsc_thread *new;

	if (num_clones == MAX_NUM_CLONES) {
		return -1;
	}
#ifdef USE_PTHREADS
	if (bsyscalls_dead_flag) {
		bsyscalls_recover_dead_threads ();
	}
	/*
	 * when using POSIX threads, the clone structure and other data is allocated from
	 * the free-lists.  pthreads takes care of the stack for us.  quite generous to
	 * allow room for userptr (most apps only store a small amount here)
	 */
	c_stack = (void *)dmem_alloc (sizeof (bsc_thread) + sizeof(sigjmp_buf) + 2048);
	new = (bsc_thread *)c_stack;
	new->terminated = 0;
	new->cancel = 0;
	new->user_ptr = (void *)(c_stack + sizeof(bsc_thread) + 16);
	if ((int)(new->user_ptr) % sizeof(void *)) {
		new->user_ptr = (void *)((int)(new->user_ptr) - ((int)(new->user_ptr) % sizeof(void *)));
	}
	new->clone_cleanup = NULL;
	new->next = NULL;
	new->qprev = NULL;
	new->qnext = all_threads;
	new->me = (pthread_t)0;
	if (all_threads) {
		all_threads->qprev = (bsc_thread *)new;
	}
	all_threads = (bsc_thread *)new;
	barrier ();

#if 0
fprintf (stderr, "KRoC: creating new thread..\n");
#endif
	if (pthread_create ((pthread_t *)(&new->me), NULL, (void *(*)(void *))clone_entry, (void *)new)) {
		fprintf (stderr, "KRoC: bsyscalls: pthread_create() failed\n");
		return -1;
	}
#if 0
fprintf (stderr, "KRoC: create_clone(): new pthread %d, bsc_thread %p\n", (int)new->me, new);
#endif

#else	/* !USE_PTHREADS */

	/* changed: always use locked_malloc() for clone stacks */
	c_stack = (void *)locked_malloc (CLONE_STACK_SIZE);
	if (!c_stack) {
		fprintf (stderr, "KRoC: bsyscalls: unable to allocate %d bytes for clone stack: %s\n", CLONE_STACK_SIZE, strerror(errno));
		return -1;
	}


	new = (bsc_thread *)c_stack;
	new->pid = 0;
	new->thr_num = num_clones;
	new->terminated = 0;
	new->cancel = 0;
	new->jbuf = (sigjmp_buf *)(c_stack + sizeof(bsc_thread) + 16);
	if ((int)(new->jbuf) % sizeof(void *)) {
		new->jbuf = (sigjmp_buf *)((int)(new->jbuf) - ((int)(new->jbuf) % sizeof(void *)));
	}
	new->user_ptr = (void *)((char *)(new->jbuf) + sizeof(sigjmp_buf) + 16);
	if ((int)(new->user_ptr) % sizeof(void *)) {
		new->user_ptr = (void *)((int)(new->user_ptr) - ((int)(new->user_ptr) % sizeof(void *)));
	}
	new->clone_cleanup = NULL;
	clone_arry[num_clones] = new;
	clone_pids[num_clones] = -1;
	barrier ();
	/* give it a bit of room above the stack.. */
	if (__clone (clone_entry, (c_stack + ((CLONE_STACK_SIZE) - 16)), CLONE_VM|CLONE_FS|CLONE_FILES, (void *)new) == -1) {
		fprintf (stderr, "KRoC: bsyscalls: calling __clone() failed because: %s\n", strerror(errno));
		return -1;
	}
#endif	/* !USE_PTHREADS */
	barrier ();
	spl_lock (&idle_lock);
	barrier ();
	num_clones++;
	num_idle++;
	barrier ();
	spl_unlock (&idle_lock);
	barrier ();
	return 0;
}
/*}}}*/
#ifdef USE_PTHREADS
/*{{{  static void clone_cleanup (void *arg)*/
/*
 *	called when a clone is cancelled to clean-up, or when we finish executing the user-function
 */
static void clone_cleanup (void *arg)
{
	bsc_thread *me = (bsc_thread *)arg;
	int *ptr;
	int need_sig;

#if 0
fprintf (stderr, "KRoC: clone_cleanup (arg = 0x%8.8x), kill_all_pthreads = %d\n", (unsigned int)arg, kill_all_pthreads);
#endif
	if (kill_all_pthreads) {
		/* get out! */
		return;
	}
#if 0
fprintf (stderr, "KRoC: clone_cleanup(): me->job = %p\n", me->job);
#endif

	barrier ();
	spl_lock (&dead_lock);
	barrier ();
	me->next = (bsc_thread *)dead_threads;
	dead_threads = me;
	bsyscalls_dead_flag = 1;
	barrier ();
	spl_unlock (&dead_lock);
	barrier ();
	spl_lock (&idle_lock);
	barrier ();
	num_clones--;
	barrier ();
	spl_unlock (&idle_lock);
	barrier ();

	if (!me->job) {
		return;
	}
	if (me->job->adjustment) {
		/* Clear channel parameter (in ptr) */
		ptr = (int *)(me->job->ws_arg[-1]);
		*ptr = 0;
	}
	barrier ();
	/* run cleanup function if necessary */
	if (me->clone_cleanup) {
		me->clone_cleanup (me->user_ptr);
	}
	/* add to queue */
	#ifdef BLOCKING_DEBUG
		fprintf (stderr, "Clone [" CLONE_FSTR "] adding workspace to queue.\n", CLONE_PARAMS(me));
	#endif
	need_sig = 0;

	/* new code: we just put the job on the collect queue -- other code recovers the occam process */

	barrier ();
	spl_lock (&collect_lock);
	barrier ();

	if (!collect_qhead) {
		collect_qhead = me->job;
		need_sig = 1;
	} else {
		collect_qtail->next = me->job;
	}
	collect_qtail = me->job;

	barrier ();
	spl_unlock (&collect_lock);
	barrier ();

#if 0
	spl_lock (&idle_lock);
	barrier ();

	num_idle++;

	barrier ();
	spl_unlock (&idle_lock);
	barrier ();
#endif

	me->job = NULL;

	/* signal occam process */
	#ifdef BLOCKING_DEBUG
		fprintf (stderr, "Clone [" CLONE_FSTR "] signalling occam process.\n", CLONE_PARAMS(me));
	#endif
	if (need_sig) {
		#ifdef BLOCKING_PROFILE
			signal_time = x_time;
		#endif
		if (bsc_uses_cspdriver) {
#if 0
			resync_kernel = 1;
#endif
		} else {
			kill (occam_proc_pid, SIGBLOCKING);
		}
	} else if (bsc_uses_cspdriver) {
#if 0
		resync_kernel = 0;
#endif
	}
	barrier ();

	return;
}
/*}}}*/
/*{{{  void bsyscalls_recover_dead_threads (void)*/
/*
 *	called to recover any recently dead threads
 */
void bsyscalls_recover_dead_threads (void)
{
	bsc_thread *tmp;

	barrier ();
	spl_lock (&dead_lock);
	barrier ();
	tmp = (bsc_thread *)dead_threads;
	dead_threads = NULL;
	bsyscalls_dead_flag = 0;
	barrier ();
	spl_unlock (&dead_lock);
	barrier ();

	while (tmp) {
		bsc_thread *next = tmp->next;

		if (tmp->qprev) {
			tmp->qprev->qnext = tmp->qnext;
		} else {
			all_threads = tmp->qnext;
			all_threads->qprev = NULL;
		}
		if (tmp->qnext) {
			tmp->qnext->qprev = tmp->qprev;
		}

		dmem_release ((void *)tmp);
		tmp = next;
	}
	return;
}
/*}}}*/
#else
/*{{{  static void clone_abort (int sig)*/
/*
 *	void clone_abort (int sig)
 *	SIGUSR1 handler for clone
 */
static void clone_abort (int sig)
{
	int n_cln, x;
	volatile bsc_thread *volatile tmp;
	int me = getpid ();

	n_cln = num_clones;
	for (x=0; x<n_cln; x++) {
		if (clone_pids[x] == me) {
			break;
		}
	}
	if (x == n_cln) {
		fprintf (stderr, "KRoC: FATAL: clone not found in clone_abort ().  count = %d, pid = %d\n", num_clones, getpid ());
		kill (occam_proc_pid, SIGSEGV);
		pause ();
		#ifdef USE_PTHREADS
			pthread_exit (NULL);
		#else
			_exit (1);
		#endif
	}
	tmp = clone_arry[x];
	siglongjmp (*(tmp->jbuf), 1);
}
/*}}}*/
/*{{{  static void clone_vbad_signal (int sig)*/
/*
 *	void clone_vbad_signal (int sig)
 *	very bad signal..
 */
static void clone_vbad_signal (int sig)
{
	fprintf (stderr, "KRoC: clone (%d) got bad signal %d\n", getpid (), sig);
	#ifdef USE_PTHREADS
		pthread_exit (NULL);
	#else
		_exit (sig);
	#endif
}
/*}}}*/
/*{{{  static void clone_segfault_signal (int sig, siginfo_t *siginf, void *ptr)*/
/*
 *	called when a segfault occurs, gives fairly verbose diagnostics.
 */
static void clone_segfault_signal (int sig, siginfo_t *siginf, void *ptr)
{
	fprintf (stderr, "KRoC: clone (%d) got segfault signal (%d).  pointer at %p\n", getpid (), sig, ptr);
	if (siginf) {
		fprintf (stderr, "KRoC: signal %d at address %p\n", siginf->si_signo, siginf->si_addr);
	}
	#ifdef BLOCKING_STOP
		fprintf (stderr, "KRoC: clone (%d) stopping (but not terminating)\n", getpid());
		pause ();
	#endif
	#ifdef USE_PTHREADS
		pthread_exit (NULL);
	#else
		_exit (sig);
	#endif
}
/*}}}*/
#endif
/*{{{  static int clone_entry (void *arg)*/
/*
 *	int clone_entry (void *arg)
 *	Clone enters here -- in fact, it never leaves...
 */
static int clone_entry (void *arg)
{
	volatile bsc_thread *volatile me;
	int need_sig, *ptr, e_withsig;
	int xflag, do_cleanup;
	volatile int *volatile xptr;
	#ifdef BLOCKING_PROFILE
		long long t_time, x_time;
	#endif
#ifndef USE_PTHREADS
	struct sigaction act, oact;
	struct sigaction segact;		/* special one for segfault */
#endif
	sigset_t just_usr1, old_mask;
	volatile int resync_kernel = 0;

	ptr = NULL;
	me = (bsc_thread *)arg;
	me->job = NULL;
#ifdef USE_PTHREADS
	me->me = pthread_self ();
#if 0
	fprintf (stderr, "KRoC: clone_entry(%p): hello from thread %d\n", arg, me->me);
#endif
#else
	clone_pids[me->thr_num] = getpid();
	me->pid = getpid ();
#endif

#ifndef USE_PTHREADS
	/* Boring signals first */
	signal (SIGTERM, clone_vbad_signal);
	signal (SIGHUP, clone_vbad_signal);
	signal (SIGCHLD, clone_vbad_signal);
	/* signal (SIGSEGV, clone_vbad_signal); */
	signal (SIGBUS, clone_vbad_signal);
	signal (SIGPIPE, SIG_IGN);	/* can happen easily on sockets, etc. just ignore it, things return with EPIPE */
	signal (SIGUSR2, clone_vbad_signal);
	/* Setup SIGUSR1 (terminate signal) */
	act.sa_handler = clone_abort;
	sigemptyset (&act.sa_mask);
	act.sa_flags = 0;
	if (sigaction (SIGUSR1, &act, &oact) < 0) {
		fprintf (stderr, "KRoC: bsyscalls: clone %d unable to set handler for SIGUSR1: %s\n", me->thr_num, strerror(errno));
		_exit (1);
	}
	segact.sa_handler = NULL;
	segact.sa_sigaction = clone_segfault_signal;
	sigemptyset (&segact.sa_mask);
	segact.sa_flags = SA_SIGINFO;
	if (sigaction (SIGSEGV, &segact, NULL) < 0) {
		fprintf (stderr, "KRoC: bsyscalls: clone %d unable to set handler for SEGFAULT: %s\n", me->thr_num, strerror(errno));
		_exit (1);
	}

	/*
	 *	we don't use signals for the pthreads version
	 */
#endif

#ifdef USE_PTHREADS
	/* block all signals */
	sigemptyset (&just_usr1);
	sigaddset (&just_usr1, SIGTERM);
	sigaddset (&just_usr1, SIGHUP);
	sigaddset (&just_usr1, SIGCHLD);
	sigaddset (&just_usr1, SIGBUS);
	sigaddset (&just_usr1, SIGPIPE);
	sigaddset (&just_usr1, SIGUSR1);
	sigaddset (&just_usr1, SIGUSR2);
	sigaddset (&just_usr1, SIGINT);
	sigaddset (&just_usr1, SIGSTOP);
	sigaddset (&just_usr1, SIGCONT);

	pthread_sigmask (SIG_BLOCK, &just_usr1, &old_mask);
	pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

#else	/* !USE_PTHREADS */
	/* block the signal SIGUSR1 */
	sigemptyset (&just_usr1);
	sigaddset (&just_usr1, SIGUSR1);
	sigprocmask (SIG_BLOCK, &just_usr1, &old_mask);


#endif	/* !USE_PTHREADS */
	#ifdef BLOCKING_DEBUG
		fprintf (stderr, "Clone [" CLONE_FSTR "] started.\n", CLONE_PARAMS(me));
	#endif
	/* Go! */
	for (;;) {
		/* Block on semaphore - access dispatching */
		#ifdef BLOCKING_DEBUG
			fprintf (stderr, "Clone [" CLONE_FSTR "] about to wait on semaphore.\n", CLONE_PARAMS(me));
		#endif
		barrier ();
		if (bsc_uses_cspdriver) {
			/* go via the kernel to collect */
			#if 0 && defined(LINUX_CSPDRIVER)
				if (cspdriver_bsc_callin ((void **)(&lv_dispatching), bsc_commsize(*lv_dispatching), resync_kernel) < 0) {
					fprintf (stderr, "KRoC: bsyscalls: clone %d unable to callin: %s\n", me->thr_num, strerror (errno));
					_exit (1);
				}
				memcpy ((char *)me, (char *)lv_dispatching, bsc_commsize (*me));
				/* we've actually got lv_dispatching for the duration of the call.. */
			#endif
		} else {
			bsc_jobinfo *tmpjob;

			if (claim_semaphore (clone_sem, NOTIFY_SEM) < 0) {
				#ifdef USE_PTHREADS
					pthread_exit (NULL);
				#else
					_exit (0);
				#endif
			}
			#ifdef BLOCKING_DEBUG
				fprintf (stderr, "Clone [" CLONE_FSTR "] woke up (1).\n", CLONE_PARAMS(me));
			#endif
			#ifdef BLOCKING_PROFILE
				x_time = v_dispatching->time_dispatch;
			#endif

			barrier ();
			spl_lock (&dispatch_lock);
			barrier ();
			tmpjob = (bsc_jobinfo *)dispatch_qhead;
			if (!tmpjob) {
				#ifndef USE_PTHREADS
					fprintf (stderr, "Clone [%d] had no job!\n", getpid());
					_exit (0);
				#else
					pthread_exit (NULL);
				#endif
			}
			if (dispatch_qhead == dispatch_qtail) {
				dispatch_qhead = NULL;
			} else {
				dispatch_qhead = tmpjob->next;
			}
			barrier ();
			spl_unlock (&dispatch_lock);
			barrier ();

			me->job = tmpjob;
			me->clone_cleanup = NULL;
			/* go! */
		}
		barrier ();
		me->cancel = 0;
		me->terminated = 0;
		#ifdef BLOCKING_DEBUG
			fprintf (stderr, "Clone [" CLONE_FSTR "] woke up (2).\n", CLONE_PARAMS(me));
		#endif
		if (me->job->adjustment) {
			/* Place a pointer to ourselves in the workspace (this is the magic occam channel) */
			ptr = (int *)(me->job->ws_arg[-1]);
			*ptr = (int)me;
		}
		#ifdef BLOCKING_DEBUG
			fprintf (stderr, "Clone [" CLONE_FSTR "] before barrier()\n", CLONE_PARAMS(me));
		#endif
		barrier ();
#if 0
fprintf (stderr, "Clone woke up: me->job->func=0x%8.8x, me->job->ws_arg=0x%8.8x\n", (unsigned int)me->job->func, (unsigned int)me->job->ws_arg);
#endif
#ifndef USE_PTHREADS
		/* Deal with the signal jump stuff... */
		if (!sigsetjmp (*(me->jbuf), 1)) {
#else
		
		pthread_cleanup_push (clone_cleanup, (void *)me);
		
		if (1) {
#endif
			/* We are the first time */
#ifndef USE_PTHREADS
			sigprocmask (SIG_UNBLOCK, &just_usr1, NULL);
#endif	/* !USE_PTHREADS */
			barrier ();
			#ifdef BLOCKING_DEBUG
				fprintf (stderr, "Clone [" CLONE_FSTR "] about to call 0x%x (0x%x)\n", CLONE_PARAMS(me), (unsigned int)me->job->func, (unsigned int)me->job->ws_arg);
			#endif
			#ifdef BLOCKING_PROFILE
				LOADTIME (t_time);
				ADDTOBUCKET (bp_dispatch_trigger, x_time, t_time);
			#endif
			if (me->job->arg3) {
				((void (*)(word *, void *, void *))me->job->func)((word *)me->job->ws_arg, me->job->ws_arg2, me->job->ws_arg3);
			} else {
				me->job->func ((word *)me->job->ws_arg);
			}
			#ifdef BLOCKING_PROFILE
				LOADTIME (x_time);
				ADDTOBUCKET (bp_trigger_finish, t_time, x_time);
			#endif
			#ifdef BLOCKING_DEBUG
				fprintf (stderr, "Clone [" CLONE_FSTR "] done calling 0x%x (0x%x)\n", CLONE_PARAMS(me), (unsigned int)me->job->func, (unsigned int)me->job->ws_arg);
			#endif
			me->terminated = 1;
#ifndef USE_PTHREADS
			sigprocmask (SIG_BLOCK, &just_usr1, NULL);
#endif	/* !USE_PTHREADS */
			e_withsig = 0;
		} else {
			/* We are the interrupted instance -- SIGUSR1 block from siglongjmp :-) */
			#ifdef BLOCKING_PROFILE
				LOADTIME (x_time);
				ADDTOBUCKET (bp_trigger_finish, t_time, x_time);
				ADDTOBUCKET (bp_kill_finish, me->time_kill, x_time);
			#endif
			me->terminated = 1;
			e_withsig = 1;
		}
		barrier ();
#ifdef USE_PTHREADS
		pthread_cleanup_pop (0);
#endif
		do_cleanup = 0;
		/* me->cancel check */
		xflag = 1;
		xptr = &(me->cancel);
		XCHG (xflag, xptr);
		if (xflag) {
			/* cancellation was started */
			if (!e_withsig) {
				#ifdef BLOCKING_DEBUG
					fprintf (stderr, "Clone [" CLONE_FSTR "] hasn\'t seen signal yet -- going into sigsuspend ()\n", CLONE_PARAMS(me));
				#endif
				sigsuspend (&old_mask);
			}
			/* We got it */
			do_cleanup = 1;
		}
		if (me->job->adjustment) {
			/* Clear channel parameter (in ptr) */
			ptr = (int *)(me->job->ws_arg[-1]);
			*ptr = 0;
		}
		barrier ();
		/* run cleanup function if necessary */
		if (me->clone_cleanup && do_cleanup) {
			me->clone_cleanup (me->user_ptr);
		}
		/* add to queue */
		#ifdef BLOCKING_DEBUG
			fprintf (stderr, "Clone [" CLONE_FSTR "] adding workspace to queue.\n", CLONE_PARAMS(me));
		#endif
		need_sig = 0;
#if 0
		barrier ();
		spl_lock (&queue_lock);
		barrier ();
		/* put memory (lv_dispatching) back on the memory queue too.. */
		if (!q_fptr) {
			q_fptr = q_bptr = me->ws_ptr;
			#ifdef LINUX_CSPDRIVER
			if (bsc_uses_cspdriver) {
				q_mfptr = q_mbptr = lv_dispatching;
				*(bsc_thread **)q_mbptr = NULL;
			}
			#endif
			need_sig = 1;
		} else {
			q_bptr[Link] = (word)me->ws_ptr;
			q_bptr = me->ws_ptr;
			#ifdef LINUX_CSPDRIVER
			if (bsc_uses_cspdriver)  {
				*(bsc_thread **)q_mbptr = (bsc_thread *)lv_dispatching;
				q_mbptr = lv_dispatching;
			}
			#endif
		}
		num_queued++;
		((int *)(me->ws_ptr))[Iptr] = (int)me->raddr;
		((int *)(me->ws_ptr))[Link] = NotProcess_p;
		#ifdef PROCESS_PRIORITY
			((int *)(me->ws_ptr))[Priority] = (int)me->pri;
		#endif
		barrier ();
		spl_unlock (&queue_lock);
		barrier ();
#endif
		/* new code: we just put the job on the collect queue -- other code recovers the occam process */

		barrier ();
		spl_lock (&collect_lock);
		barrier ();

		if (!collect_qhead) {
			collect_qhead = me->job;
			need_sig = 1;
		} else {
			collect_qtail->next = me->job;
		}
		collect_qtail = me->job;

		barrier ();
		spl_unlock (&collect_lock);
		barrier ();
		spl_lock (&idle_lock);
		barrier ();

		num_idle++;

		barrier ();
		spl_unlock (&idle_lock);
		barrier ();

		me->job = NULL;

		/* signal occam process */
		#ifdef BLOCKING_DEBUG
			fprintf (stderr, "Clone [" CLONE_FSTR "] signalling occam process.\n", CLONE_PARAMS(me));
		#endif
		if (need_sig) {
			#ifdef BLOCKING_PROFILE
				signal_time = x_time;
			#endif
			if (bsc_uses_cspdriver) {
				resync_kernel = 1;
			} else {
				kill (occam_proc_pid, SIGBLOCKING);
			}
		} else if (bsc_uses_cspdriver) {
			resync_kernel = 0;
		}
		barrier ();
	}
	#ifdef USE_PTHREADS
		pthread_exit (NULL);
	#else
		_exit (0);
	#endif
}
/*}}}*/
/*{{{  void _killcall (int *w)*/
/*
 *	void _killcall (int *w)
 *	PROC C.killcall (CHAN OF INT c, INT status)
 *	terminates a blocking system call (gulp)
 */
void _killcall (int *w)
{
	int *ptr, *result;
	volatile bsc_thread *volatile tmp;
	int xflag;
	volatile int *volatile xptr;
	#ifdef BLOCKING_PROFILE
		long long t_time;
	#endif

	#ifdef BLOCKING_PROFILE
		LOADTIME (t_time);
		tmp->time_kill = t_time;
	#endif
	ptr = (int *)(w[0]);
	result = (int *)(w[1]);
#if 0
fprintf (stderr, "_killcall(): ptr=0x%8.8x, result=0x%8.8x\n", (unsigned int)ptr, (unsigned int)result);
#endif
	tmp = (bsc_thread *)(*ptr);
	if (!tmp) {
		/* Huh -- blocking syscall hasn't got here yet... */
		sched_yield ();
		tmp = (bsc_thread *)(*ptr);
		if (!tmp) {
			/* Oh, ok, it already finished. */
			*result = -1;
			return;
		}
	}
	if (tmp->terminated) {
		/* Thread has already terminated */
		*result = 1;
		*ptr = 0;
		return;
	}
	xflag = 1;
	xptr = &(tmp->cancel);
	XCHG (xflag, xptr);
	if (xflag) {
		/* thread termination already started */
		*result = 2;
		*ptr = 0;
		return;
	}
	/* Signal it.. */
#ifdef USE_PTHREADS
#if 0
fprintf (stderr, "KRoC: _killcall(): cancelling thread %d (i am thread %d [%d])\n", (int)(tmp->me), (int)pthread_self(), getpid());
#endif
	pthread_cancel (tmp->me);
#else	/* !USE_PTHREADS */
	kill (tmp->pid, SIGUSR1);
#endif	/* !USE_PTHREADS */
	*result = 0;
	*ptr = 0;
	return;
}
/*}}}*/
/*{{{  void _getclonecount (int *w)*/
/*
 *	used to retrieve the number of blocking clones in existence.  mostly good for checking after benchmarks.  occam interface is simply:
 *	#PRAGMA EXTERNAL "PROC C.getclonecount (RESULT INT n) = 0"
 */
void _getclonecount (int *w)
{
	int *n = (int *)(w[0]);

	#ifdef USE_PTHREADS
	barrier ();
	spl_lock (&idle_lock);
	barrier ();
	*n = num_clones;
	barrier ();
	spl_unlock (&idle_lock);
	barrier ();
	#else
	*n = num_clones;
	#endif
	return;
}
/*}}}*/
/*{{{  void *bsyscalls_set_cleanup (void (*cleanup_fcn)(void *))*/
/*
 *	void *bsyscalls_set_cleanup (void (*cleanup_fcn)(void *))
 *	sets the cleanup function for a clone
 */
void *bsyscalls_set_cleanup (void (*cleanup_fcn)(void *))
{
#ifdef USE_PTHREADS
	bsc_thread *tmp;
	pthread_t me = pthread_self ();

	for (tmp = all_threads; tmp; tmp = tmp->qnext) {
		if (tmp->me == me) {
			break;
		}
	}
	if (!tmp) {
		fprintf (stderr, "KRoC: FATAL: clone not found in bsyscalls_set_cleanup()\n");
		kill (occam_proc_pid, SIGSEGV);
		pause ();
		pthread_exit (NULL);
	}

#else
	int n_cln, x, me;
	volatile bsc_thread *volatile tmp;

	n_cln = num_clones;
	me = getpid ();
	for (x=0; x<n_cln; x++) {
		if (clone_pids[x] == me) {
			break;
		}
	}
	if (x == n_cln) {
		fprintf (stderr, "KRoC: FATAL: clone not found in bsyscalls_set_cleanup()\n");
		kill (occam_proc_pid, SIGSEGV);
		pause ();
		_exit (1);
	}
	tmp = clone_arry[x];
#endif
	/* tmp is something valid */
	tmp->clone_cleanup = cleanup_fcn;
	return tmp->user_ptr;
}
/*}}}*/
/*{{{  static void clone_bad_signal (int sig)*/
/*
 *	void clone_bad_signal (int sig)
 *	Called when clone gets an unexpected signal
 */
static void clone_bad_signal (int sig)
{
	fprintf (stderr, "KRoC: bsyscalls: clone got unexpected/unhandled signal %d.\n", sig);
	if (sig == SIGSEGV) {
		kill (occam_proc_pid, SIGSEGV);
		#ifdef USE_PTHREADS
			pthread_exit (NULL);
		#else
			_exit (0);
		#endif
	}
	signal (sig, clone_bad_signal);
	return;
}
/*}}}*/
/*{{{  static int set_semaphores ({int,sem_t*} semid, int n_sems, ...)*/
/*
 *	arguments are pairs of [sem_num, val]
 *	Sets semaphores to specified values
 *	Returns 0 on success, -1 on failure.
 *
 * 	pthreads version is a bit hackish, but should work ok
 */
#ifdef USE_PTHREADS
static int set_semaphores (sem_t *semid, int n_sems, ...)
#else
static int set_semaphores (int semid, int n_sems, ...)
#endif
{
	int sem_num, sem_val;
	va_list ap;
	int res;
#ifndef USE_PTHREADS
	union semun sem_parm;
#endif
	
	va_start (ap, n_sems);
	res = 0;
	while (n_sems) {
		sem_num = va_arg (ap, int);
		sem_val = va_arg (ap, int);
#ifdef USE_PTHREADS
		sem_init (&(semid[sem_num]), 0, sem_val);
#else
		sem_parm.val = sem_val;
		if (semctl (semid, sem_num, SETVAL, sem_parm) < 0) {
			n_sems = 0;
			res = -1;
		}
#endif
		n_sems--;
	}
	va_end (ap);
	return res;
}
/*}}}*/
/*{{{  static int claim_semaphore ({int,sem_t*} semid, int semnum)*/
/*
 *	int claim_semaphore (int semid, int semnum)
 *	Claims a semaphore.  Returns 0 on success (ie, claimed), -1 if an error occured
 */
#ifdef USE_PTHREADS
static int claim_semaphore (sem_t *semid, int semnum)
{
	int i;
	
	i = sem_wait (&(semid[semnum]));
	return i;
}
#else
static int claim_semaphore (int semid, int semnum)
{
	struct sembuf sem_parm;

	sem_parm.sem_num = semnum;
	sem_parm.sem_flg = 0L;
	sem_parm.sem_op = -1;
	#ifdef BLOCKING_DEBUG
		fprintf (stderr, "bsyscalls: [%d] claim_semaphore (%d, %d)\n", getpid(), semid, semnum);
	#endif
	while (semop (semid, &sem_parm, 1) == -1) {
		#ifdef BLOCKING_DEBUG
			fprintf (stderr, "bsyscalls: [%d] claim_semaphore (%d, %d) failed: %s\n", getpid(), semid, semnum, strerror(errno));
		#endif
		if (errno == EINTR) {
			continue;
		}
		return -1;
	}
	#ifdef BLOCKING_DEBUG
		fprintf (stderr, "bsyscalls: [%d] claim_semaphore (%d, %d) succeeded\n", getpid(), semid, semnum);
	#endif
	return 0;
}
#endif
/*}}}*/
/*{{{  static int notify_semaphore ({int,sem_t*} semid, int semnum)*/
/*
 *	int notify_semaphore (int semid, int semnum)
 *	Notifies a semaphore.  Returns 0 on success (ie, notified), -1 if an error occured
 */
#ifdef USE_PTHREADS
static int notify_semaphore (sem_t *semid, int semnum)
{
	int i;

	i = sem_post (&(semid[semnum]));
	return i;
}
#else
static int notify_semaphore (int semid, int semnum)
{
	struct sembuf sem_parm;
	int try_again = 4;

	sem_parm.sem_num = semnum;
	/* sem_parm.sem_flg = SEM_UNDO; */
	sem_parm.sem_flg = 0L;
	sem_parm.sem_op = 1;
	#ifdef BLOCKING_DEBUG
		fprintf (stderr, "bsyscalls: [%d] notify_semaphore (%d, %d)\n", getpid(), semid, semnum);
	#endif
	while ((semop (semid, &sem_parm, 1) == -1) && (try_again > 0)) {
		if (errno != EINTR) {
			fprintf (stderr, "bsyscalls: [..] notify_semaphore [%d] (%d, %d): failed with: %s\n", try_again, semid, semnum, strerror (errno));
		}
		try_again--;
	}
	return 0;
}
#endif
/*}}}*/
/*{{{  static __inline__ void bsyscalls_dispatch_internal (word *ws, char *raddr, word pri, word *ws_arg, void (*func)(word *), int adjustment, int arg3, void *ws_arg2, void *ws_arg3)*/
/*
 *	void bsyscalls_dispatch_internal (word *ws, char *raddr, word *ws_arg, void (*func)(word *), int adjustment)
 *	Dispatches a thread to handle a blocking system call
 */
#ifdef PROCESS_PRIORITY
static __inline__ void bsyscalls_dispatch_internal (word *ws, char *raddr, word pri, word *ws_arg, void (*func)(word *), int adjustment, int arg3, void *ws_arg2, void *ws_arg3)
#else
static __inline__ void bsyscalls_dispatch_internal (word *ws, char *raddr, word *ws_arg, void (*func)(word *), int adjustment, int arg3, void *ws_arg2, void *ws_arg3)
#endif
{
	int no_clone, deferred_new;
	#ifdef BLOCKING_PROFILE
		long long t_time, x_time;
	#endif

	#ifdef BLOCKING_DEBUG
		fprintf (stderr, "bsyscalls_dispatch () : %d clones\n", num_clones);
	#endif
	#ifdef BLOCKING_PROFILE
		LOADTIME (t_time);
	#endif
	no_clone = 0;
	if (num_idle == 1) {
		deferred_new = 1;
	} else if (!num_idle) {
		/* fprintf (stderr, "bsyscalls_dispatch () : creating new clone\n"); */
		if (create_clone () < 0) {
			deferred_new = 0;
			no_clone = 1;
		} else {
			deferred_new = 1;
		}
	} else {
		deferred_new = 0;
	}
	/* if !no_clone, clone is waiting */
	if (no_clone) {
		fprintf (stderr, "KRoC: bsyscalls: out of clones.\n");
		occam_exit (1, (int)raddr);
	}
	barrier ();

	if (bsc_uses_cspdriver) {
#if 0
		bsc_thread *next_dispatch = *(bsc_thread **)v_dispatching;

		/* dispatch through driver */
		#ifdef BLOCKING_PROFILE
			v_dispatching->time_dispatch = t_time;
		#endif
		v_dispatching->ws_ptr = (int *)ws;
		/* Adjust for the magic channel parameter (if applicable) */
		v_dispatching->ws_arg = (int *)((char *)ws_arg + adjustment);
		v_dispatching->arg3 = arg3;
		if (arg3) {
			v_dispatching->ws_arg2 = ws_arg2;
			v_dispatching->ws_arg3 = ws_arg3;
		}
		v_dispatching->func = func;
		v_dispatching->raddr = raddr;	/* to store in ws_ptr[Iptr] later */
		#ifdef PROCESS_PRIORITY
			v_dispatching->pri = pri;	/* to store in ws_ptr[Priority] later */
		#endif
		v_dispatching->adjustment = adjustment;
		if (cspdriver_bsc_dispatch ((void *)v_dispatching, bsc_commsize(*v_dispatching)) < 0) {
			fprintf (stderr, "KRoC: error!  failed to dispatch through cspdriver: %s\n", strerror (errno));
			occam_exit (1, (int)raddr);
		}

		/* advance v_dispatching one slot */
		v_dispatching = next_dispatch;
		num_spare--;
#endif
	} else {
		bsc_jobinfo *newjob = NULL;

		barrier ();
		spl_lock (&fl_lock);
		barrier ();
		if (fl_qhead) {
			newjob = (bsc_jobinfo *)fl_qhead;
			fl_qhead = newjob->next;
		}
		barrier ();
		spl_unlock (&fl_lock);
		barrier ();

		if (!newjob) {
			newjob = (bsc_jobinfo *)dmem_alloc (sizeof (bsc_jobinfo));
		}

		#ifdef BLOCKING_PROFILE
			newjob->time_dispatch = t_time;
		#endif
		newjob->ws_ptr = (int *)ws;
		/* Adjust for the magic channel parameter (if applicable) */
		newjob->ws_arg = (int *)((char *)ws_arg + adjustment);
		newjob->arg3 = arg3;
		if (arg3) {
			newjob->ws_arg2 = ws_arg2;
			newjob->ws_arg3 = ws_arg3;
		}
		newjob->func = func;
		newjob->raddr = raddr;	/* to store in ws_ptr[Iptr] later */
		#ifdef PROCESS_PRIORITY
			newjob->pri = pri;	/* to store in ws_ptr[Priority] later */
		#endif
		newjob->adjustment = adjustment;
		newjob->next = NULL;

		barrier ();
		while (spl_lock_or_fail (&dispatch_lock)) {
			/* avoid extended spinning by rescheduling */
			sched_yield ();
		}
		barrier ();
		if (!dispatch_qhead) {
			dispatch_qhead = newjob;
		} else {
			dispatch_qtail->next = newjob;
		}
		dispatch_qtail = newjob;
		barrier ();
		spl_unlock (&dispatch_lock);
		barrier ();


		barrier ();
		spl_lock (&idle_lock);
		barrier ();
		num_idle--;
		barrier ();
		spl_unlock (&idle_lock);
		barrier ();

	/* fprintf (stderr, "dispatch: notify ()\n"); */
		if (notify_semaphore (clone_sem, NOTIFY_SEM) < 0) {
			fprintf (stderr, "KRoC: error! -- notify_semaphore () failed with %s\n", strerror(errno));
		}
	/* fprintf (stderr, "dispatch: done notify ()\n"); */
	}
	barrier ();
	#ifdef BLOCKING_DEBUG
		fprintf (stderr, "bsyscalls_dispatch (0x%x, 0x%x, 0x%x) : notified clone\n", (unsigned int)ws, (unsigned int)ws_arg, (unsigned int)func);
	#endif
/* fprintf (stderr, "bsyscalls_dispatch (0x%lx, 0x%lx, 0x%lx) : notified clone\n", ws, ws_arg, func); */
	if (deferred_new) {
		create_clone ();
	}
	return;
}
/*}}}*/
/*{{{  void bsyscalls_dispatch (word *ws, char *raddr, word pri, word *ws_arg, void (*func)(word *), int adjustment)*/
/*
 *	void bsyscalls_dispatch (word *ws, char *raddr, word *ws_arg, void (*func)(word *), int adjustment)
 *	dispatches a clone, from occam bsyscall dispatch
 */
#ifdef PROCESS_PRIORITY
void bsyscalls_dispatch (word *ws, char *raddr, word pri, word *ws_arg, void (*func)(word *), int adjustment)
{
	bsyscalls_dispatch_internal (ws, raddr, pri, ws_arg, func, adjustment, 0, NULL, NULL);
	return;
}
#else
void bsyscalls_dispatch (word *ws, char *raddr, word *ws_arg, void (*func)(word *), int adjustment)
{
	bsyscalls_dispatch_internal (ws, raddr, ws_arg, func, adjustment, 0, NULL, NULL);
	return;
}
#endif
/*}}}*/
/*{{{  void bsyscalls_dispatch3 (word *ws, char *raddr, word pri, word *ws_arg, void *ws_arg2, void *ws_arg3, void (*func)(word *, void *, void *))*/
/*
 *	void bsyscalls_dispatch3 (word *ws, char *raddr, word *ws_arg, void *ws_arg2, void *ws_arg3, void (*func)(word *, void *, void *))
 *	dispatches a clone, which takes 3 arguments
 */
#ifdef PROCESS_PRIORITY
void bsyscalls_dispatch3 (word *ws, char *raddr, word pri, word *ws_arg, void *ws_arg2, void *ws_arg3, void (*func)(word *, void *, void *))
{
	bsyscalls_dispatch_internal (ws, raddr, pri, ws_arg, (void (*)(word *))func, 0, 1, ws_arg2, ws_arg3);
	return;
}
#else
void bsyscalls_dispatch3 (word *ws, char *raddr, word *ws_arg, void *ws_arg2, void *ws_arg3, void (*func)(word *, void *, void *))
{
	bsyscalls_dispatch_internal (ws, raddr, ws_arg, (void (*)(word *))func, 0, 1, ws_arg2, ws_arg3);
	return;
}
#endif
/*}}}*/
/*{{{  int bsyscalls_pending (void)*/
/*
 *	int bsyscalls_pending (void)
 *	Returns number of clones currently blocked
 */
int bsyscalls_pending (void)
{
	int pending = 0;

	barrier ();
	spl_lock (&idle_lock);
	barrier ();
	pending = num_clones - num_idle;
	barrier ();
	spl_unlock (&idle_lock);
	barrier ();

	return pending;
}
/*}}}*/
/*{{{  int bsyscalls_pending_call (word func)*/
/*
 *	int bsyscalls_pending_call (word func)
 *	returns true if `func' is being blocked on
 */
int bsyscalls_pending_call (word func)
{
#ifdef USE_PTHREADS
	bsc_thread *tmp;

	for (tmp = all_threads; tmp; tmp = tmp->qnext) {
		if (tmp->job && ((word)(tmp->job->func) == func) && !(tmp->terminated)) {
			return 1;
		}
	}
#else
	int i;

	for (i=0; i<num_clones; i++) {
		if (clone_arry[i] && clone_arry[i]->job && ((word)(clone_arry[i]->job->func) == func) && !(clone_arry[i]->terminated)) {
			return 1;
		}
	}
#endif
	return 0;
}
/*}}}*/
#if 0 && defined(LINUX_CSPDRIVER)
/*{{{  void bsyscalls_collect_qmem (void)*/
/*
 *	called to collect (dispatching) memory from finished calls
 *	we have the global queue lock when coming in here..
 */
void bsyscalls_collect_qmem (void)
{
	if (!q_mfptr || !bsc_uses_cspdriver) {
		return;
	}
	*(bsc_thread **)q_mbptr = (bsc_thread *)v_dispatching;
	v_dispatching = q_mfptr;
	q_mfptr = NULL;
	q_mbptr = NULL;
	return;
}
/*}}}*/
#endif

#else	/* !BLOCKING_SYSCALLS */
	#if defined(BLOCKING_SYSCALLS_UNSUPPORTED)
		void _blocking_syscalls_unsupported (void)
	#else
		void _blocking_syscalls_disabled (void)
	#endif
	{
		return;
	}
#endif	/* !BLOCKING_SYSCALLS */

#endif	/* !OOS_BUILD */

