/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident "$Header: parallel.h 1.3 90/03/07 $"

/*
 * parallel.h
 *	Definitions for use in parallel, shared-memory programs.
 */

/*
 * A "lock" is a byte of memory, initialized to zero (unlocked).
 */

typedef unsigned char	slock_t;		/* 's' for "spin"-lock */

#define	L_UNLOCKED	0
#define	L_LOCKED	1

/*
 * Was a conditional lock request granted (L_SUCCESS) or denied (L_FAILED)
 */
#define L_FAILED	0
#define L_SUCCESS	1

/*
 * A "barrier" allows multiple processes to synchronize by having
 * all of them exit the barrier construct simultaneously.
 *
 * This version assumes <= 255 processes, fits in one 4-byte integer,
 * and is based on spin-locks.
 */

typedef struct	{
	slock_t		b_mutex;	/* mutual exclusion */
	unsigned char	b_limit;	/* number participants */
	unsigned char	b_count;	/* state counter */
	unsigned char	b_useno;	/* current use # (state flag) */
} sbarrier_t;				/* 's' for "spin"-barrier */

/*
 * Other useful declarations.
 */

extern	char	*sbrk(), *shsbrk();
extern	char	*shmalloc();

/*
 * S_LOCK() S_UNLOCK(), S_CLOCK(), S_INIT_LOCK, and S_WAIT_BARRIER functions.
 * These are implemented as "asm functions", and will produce very good code.
 *
 * Need to use the -i flag with the optimizer when these inline functions are
 * used. (cc -O -i).   This is to prevent the optimizer from removing
 * "redundant" moves (which aren't really redundant in this case)
 * in the spin loops.
 */

#ifndef	lint
asm void S_LOCK(laddr)
{
%reg laddr; lab loop, spin, done;
/PEEPOFF
loop:	movb	$L_LOCKED, %al
	xchgb	%al, (laddr)		/* an atomic "test and set" */
	cmpb	$L_UNLOCKED, %al
	je	done			/* if equal, we got the lock */
spin:	cmpb	$L_UNLOCKED, (laddr)	/* spin in cache until unlocked */
	je	loop			/* then, try get lock again */
	jmp	spin
done:
/PEEPON
%mem laddr; lab loop, spin, done;
/PEEPOFF
loop:	movb	$L_LOCKED, %al
	movl	laddr, %ecx
	xchgb	%al, (%ecx)		/* an atomic "test and set" */
	cmpb	$L_UNLOCKED, %al
	je	done			/* if equal, we got the lock */
spin:	cmpb	$L_UNLOCKED, (%ecx)	/* spin in cache until unlocked */
	je	loop			/* then, try get lock again */
	jmp	spin
done:
/PEEPON
}

asm void S_UNLOCK(laddr)
{
%reg laddr;
	movb	$L_UNLOCKED, %al
	xchgb	%al, (laddr)		/* clear lock, "atomically" */
%mem laddr;
	movb	$L_UNLOCKED, %al
	movl	laddr, %ecx
	xchgb	%al, (%ecx)		/* clear lock, "atomically" */
}

asm S_CLOCK(laddr)
{
%reg laddr; lab done;
	movb	$L_LOCKED, %dl
	movl	$L_SUCCESS, %eax
	xchgb	%dl, (laddr)		/* test and set lock */
	cmpb	$L_UNLOCKED, %dl	/* if we got it, return success */
	je	done
	movl	$L_FAILED, %eax		/* else return failure */
done:
%mem laddr; lab done;
	movb	$L_LOCKED, %dl
	movl	laddr, %ecx
	movl	$L_SUCCESS, %eax
	xchgb	%dl, (%ecx)		/* test and set lock */
	cmpb	$L_UNLOCKED, %dl	/* if we got it, return success */
	je	done
	movl	$L_FAILED, %eax		/* else return failure */
done:
}
#endif	/* !lint */

#define S_INIT_LOCK(lp)	S_UNLOCK(lp)

/*
 *	Heavy contention spin lock for a finite small number of processes.
 *	This version has the processors that miss form a line to get the lock.
 *	This cuts down on bus traffic when the lock is freed. When there is no
 *	contention, 2 extra writes are done by the locking process.
 *	In this version, the lock is actually the local cell. The 0 cell merely
 *	points to the real lock cell.
 */

#define Q_LOCK_SIZE 31
typedef struct qlock_t{ unsigned char	x[Q_LOCK_SIZE][16];};

#ifndef	lint
asm void Q_LOCK(laddr)
{
%reg laddr; lab spin, done;
/PEEPOFF
	movl	m_myid,%edx
	addl	%edx,%edx
	movl	%edx,%eax
	movb	16(laddr,%edx,8),%ah
	xchgl	%eax,0(laddr)	/* an atomic "test and set" */
	movb	%al,%dl
spin:	cmpb	%ah,16(laddr,%edx,8)	/* lock count =?= unlock count + 1 */
	je	spin
done:
/PEEPON
%mem laddr; lab spin, done;
/PEEPOFF
	movl	laddr,%ecx
	movl	m_myid,%edx
	addl	%edx,%edx
	movl	%edx,%eax
	movb	16(%ecx,%edx,8),%ah
	xchgl	%eax,0(%ecx)	/* an atomic "test and set" */
	movb	%al,%dl
spin:	cmpb	%ah,16(%ecx,%edx,8)	/* lock count =?= unlock count + 1 */
	je	spin
done:
/PEEPON
}

asm void Q_UNLOCK(laddr)
{
%reg laddr;
	movl	m_myid,%edx
	shll	$4,%edx
	incb	16(laddr,%edx)
%mem laddr;
	movl	laddr, %ecx
	movl	m_myid,%edx
	shll	$4,%edx
	incb	16(%ecx,%edx)
}

asm Q_INIT_LOCK(laddr)
{
%reg laddr;
	movl	$0,(laddr)
	movb	$1,16(laddr)
%mem laddr;
	movl	laddr, %ecx
	movl	$0,(%ecx)
	movb	$1,16(%ecx)
}

asm int Q_IS_LOCKED(laddr)
{
%reg laddr;
/PEEPOFF
	subl	%edx,%edx
	movl	0(laddr),%eax
	movb	%al,%dl
	cmpb	%ah,16(laddr,%edx,8)	/* lock count =?= unlock count + 1 */
	sete	%al
	andl	$0xff,%eax
/PEEPON
%mem laddr;
/PEEPOFF
	movl	laddr,%ecx
	subl	%edx,%edx
	movl	0(%ecx),%eax
	movb	%al,%dl
	cmpb	%ah,16(%ecx,%edx,8)	/* lock count =?= unlock count + 1 */
	sete	%al
	andl	$0xff,%eax
/PEEPON
}
#endif	/* !lint */

/*
 * In line barrier initialization macro
 *
 * Note, there's no need to initialize either b_useno (due to its manner
 * of useage, or b_mutex (due to its non-usage).
 */

#define S_INIT_BARRIER(b, l)	((b)->b_limit = (b)->b_count = (l))

/*
 * In line barrier wait procedure.
 *
 * Define offsets of fields in barrier, for use in assembly language.
 */

#define MUTEX	0
#define COUNT	1
#define LIMIT	2
#define USENO	3

#ifndef	lint
asm void S_WAIT_BARRIER(baddr)
{
%reg baddr; lab spin;
/PEEPOFF
	movb	USENO(baddr), %al	/* save old barrier useno value */
	lock				/* assert lock on next instruction */
	decb	COUNT(baddr)		/* atomic decrement of barrier count */
	jne	spin			/* if not all checked in, go spin */
	movb	LIMIT(baddr), %dl
	movb	%dl, COUNT(baddr)	/* all checked in! re-init barrier */
	incb	USENO(baddr)		/* and free all the spinners */
spin:	cmpb	USENO(baddr), %al	/* spin here until all are checked in */
	je	spin
/PEEPON
%mem baddr; lab spin;
/PEEPOFF
	movl	baddr, %ecx		/* load barrier pointer into register */
	movb	USENO(%ecx), %al	/* save old barrier useno value */
	lock				/* assert lock on next instruction */
	decb	COUNT(%ecx)		/* atomic decrement of barrier count */
	jne	spin			/* if not all checked in, go spin */
	movb	LIMIT(%ecx), %dl
	movb	%dl, COUNT(%ecx)	/* all checked in! re-init barrier */
	incb	USENO(%ecx)		/* and free all the spinners */
spin:	cmpb	USENO(%ecx), %al	/* spin here until all are checked in */
	je	spin
/PEEPON
}

/*
 * This is for internal use by parallel library functions only
 */
asm void _M_JOIN(baddr, clearl)
{
%mem baddr; mem  clearl; lab spin;
/PEEPOFF
	movl	baddr, %ecx		/* load barrier pointer into register */
	movb	USENO(%ecx), %al	/* save old barrier useno value */
	lock				/* assert lock on next instruction */
	decb	COUNT(%ecx)		/* atomic decrement of barrier count */
	jne	spin			/* if not all checked in, go spin */
	movb	LIMIT(%ecx), %dl
	movb	%dl, COUNT(%ecx)	/* all checked in! re-init barrier */
	movl	clearl, %edx
	movl	$0, (%edx)
	incb	USENO(%ecx)		/* and free all the spinners */
spin:	cmpb	USENO(%ecx), %al	/* spin here until all are checked in */
	je	spin
/PEEPON
}
#endif	/* !lint */

/*
 * Convenience definitions.
 */

#ifndef	NULL
#define	NULL		0
#endif

/*
 * Various globally used data.
 */
#ifndef lint
extern private volatile int errno;
extern private int _shm_fd;	/* fd for shared data mapped file */
extern private int _pgoff;	/* getpagesize() - 1 */
#else
extern int errno;
extern int _shm_fd;	/* fd for shared data mapped file */
extern int _pgoff;	/* getpagesize() - 1 */
#endif

/*
 * PGRND() rounds up a value to next page boundary.
 */

#define	PGRND(x)	(char *) (((int)(x) + _pgoff) & ~_pgoff)


