/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  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 1, 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.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JRR                     Initial version.
*       V01.2   JRR     ???		???
*       V01.3   JRR     12-FEb-1992	Fixed declaration of static routines.
*       V01.4   JRR     02-Mar-1992	Added Cosmic V2 changes.
*	V01.5	JRR	17-Jun-1992	Added header.
*	V01.6	JRR	20-Nov-1992	Added C prototypes.
*/
/*++ nqs_tid.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.30/src/RCS/nqs_tid.c,v $
 *
 * DESCRIPTION:
 *
 *	This module contains the two public functions of:
 *
 *		tid_allocate(), and
 *		tid_deallocate()
 *
 *	which respectively allocate and deallocate transaction-ids.
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	March 28, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.5 $ $Date: 1992/12/22 15:41:33 $ $State: Exp $)
 * $Log: nqs_tid.c,v $
 * Revision 1.5  1992/12/22  15:41:33  jrroma
 * Version 3.30
 *
 * Revision 1.4  92/06/18  17:31:23  jrroma
 * Added gnu header
 * 
 * Revision 1.3  92/03/02  16:21:00  jrroma
 * Added Cosmic V2 changes.
 * 
 * Revision 1.2  92/02/12  13:58:44  jrroma
 * Fixed declaration of static routines.
 * 
 * Revision 1.1  92/02/12  13:57:44  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"
#include "nqsxvars.h"			/* External variables and dirs */


#ifndef __CEXTRACT__
#if __STDC__

static void tid_initialize ( void );

#else /* __STDC__ */

static void tid_initialize (/* void */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */


#define	TRANSACTARRAYSIZE ((MAX_TRANSACTS+PRECISION_ULONG-1)/PRECISION_ULONG)
					/* Number of elements in the */
					/* freetransaction descriptor */
					/* bitmap array */

/*
 *	Variables local to this module.
 */
static short initialized = 0;		/* Bitmap initialized flag */
static unsigned long freetransact [TRANSACTARRAYSIZE];
					/* Bitmap of free transaction-ids */
					/* 1=free; 0=allocated */


/*** tid_allocate
 *
 *
 *	int tid_allocate():
 *	Allocate a transaction-id.
 *
 *	Returns:
 *		   0: No free transaction-id exists that
 *		      can be allocated for the request at
 *		      at the present time.
 *		>= 1: Return value denotes the allocated
 *		      transaction descriptor-id.  This
 *		      value will equal 'reserved_tid' if
 *		      'reserved_tid' was non-zero, AND
 *		      the specified tid was not already
 *		      reserved.
 */
int tid_allocate (reserved_tid, request_is_external)
int reserved_tid;			/* If non-zero, then the caller */
					/* wants this tid reserved */
int request_is_external;		/* Boolean true if the reserving */
					/* request was queued externally */
{

	register int i;			/* Index var */
	register int j;			/* Loop var */
	register unsigned long mask;	/* Bit mask */
	register unsigned long bitmap;	/* Bit map word */

	if (!initialized) tid_initialize();
	if (request_is_external) {
		/*
		 *  The associated request was queued from a remote
		 *  machine.  Each NQS machine places a limit on the
		 *  number of externally queued requests, so that
		 *  locally generated requests have a few extra
		 *  transaction-ids available only to them.
		 *
		 *  Without such a reservation, it is possible for
		 *  an NQS system to "drown" when too many requests
		 *  are queued from external sources.
		 */
		if (Extreqcount >= Maxextrequests) {
			/*
			 *  The limit on the number of externally
			 *  queued requests has already been
			 *  reached.
			 */
			return (0);
		}
	}
	if (reserved_tid) {
		/*
		 *  The caller wants this tid reserved.
		 */
		if (reserved_tid < 0 || reserved_tid > MAX_TRANSACTS) {
			/*
			 *  The tid to reserve does not exist.
			 */
			printf ("E$Invalid tid requested in tid_allocate().\n");
			fflush (stdout);
			return (0);
		}
		i = (reserved_tid - 1) / PRECISION_ULONG;
		mask = (1L << ((reserved_tid - 1) % PRECISION_ULONG));
		if (freetransact [i] & mask) {
			/*
			 *  The specified transaction descriptor
			 *  has not been previously reserved.
			 */
			mask = ~mask;
			freetransact[i] &= mask;/* Reserve the requested tid */
			return (reserved_tid);	/* Reservation succeeded */
		}
		/*
		 *  The specified tid is already reserved.
		 */
		return (0);	/* Reservation failed */
	}
	/*
	 *  The caller wants some tid allocation.  Any tid will do.
	 */
	for (i = 0; i < TRANSACTARRAYSIZE; i++) {
		bitmap = freetransact [i];	/* Get bitmap integer */
		if (bitmap) {
			/*
			 *  A free transaction-id has been discovered.
			 */
			mask = 1;
			for (j = 1; j <= PRECISION_ULONG; j++) {
				if (bitmap & mask) {
					/*
					 *  We have located a free
					 *  transaction-id.  Allocate it.
					 */
					mask = ~mask;
					freetransact [i] &= mask;
					return (i * PRECISION_ULONG + j);
				}
				mask <<= 1;
			}
		}
	}
	return (0);			/* No free transaction-ids */
}


/*** tid_deallocate
 *
 *
 *	void tid_deallocate():
 *	Deallocate a transaction-id.
 */
void tid_deallocate (trans_id)
register int trans_id;			/* Transaction-id */
{

	register int i;			/* Index var */
	register unsigned long mask;	/* Mask */

	if (!initialized) tid_initialize();
	if (trans_id > 0 && trans_id <= MAX_TRANSACTS) {
		/*
		 *  The transaction-id is valid.
		 */
		trans_id--;
		i = trans_id / PRECISION_ULONG;
		mask = (1L << (trans_id % PRECISION_ULONG));
		freetransact [i] |= mask;	/* Mark as free */
	}
}


/*** tid_initialize
 *
 *
 *	void tid_initialize():
 *	Initialize the state of this module [nqs_tid.c].
 */
static void tid_initialize ()
{
	register int i;
	register unsigned long freemask;
	register unsigned long buildmask;

	initialized = 1;		/* Set initialized flag */
	/*
	 *  Just in case PRECISION_ULONG has been configured as LESS than
	 *  the number of actual bits present in an unsigned long, we
	 *  carefully build the free mask to only set the bits indicated
	 *  by PRECISION_ULONG, rather than just saying ~0.
	 *
	 *  If we don't do this, and PRECISION_ULONG has indeed been
	 *  designated a value LESS than the real number of bits present
	 *  in an unsigned long, then the tid_allocate() procedure in
	 *  this module will break!  Thus, we're simply being paranoid.
	 */
	buildmask = 1;
	freemask = buildmask;
	for (i = 1; i < PRECISION_ULONG; i++) {
		buildmask <<= 1;
		freemask |= buildmask;
	}
	/*
	 *  Make sure that PRECISION_ULONG is not GREATER than the
	 *  number of bits present in an unsigned long.
	 */
	if (buildmask == 0) {
		printf ("F$PRECISION_ULONG exceeds actual value\n");
		printf ("F$in nqs_tid.c\n");
		nqs_abort();
	}
	/*
	 *  Set the free transaction allocation bitmap.
	 */
	i = 0;
	while (i < TRANSACTARRAYSIZE-1) {
		freetransact [i++] = freemask;
	}
	freemask >>= (TRANSACTARRAYSIZE * PRECISION_ULONG - MAX_TRANSACTS);
	freetransact [i] = freemask;
}
