/* (C) Copyright International Business Machines Corporation 23 January */
/* 1990.  All Rights Reserved. */
/*  */
/* See the file USERAGREEMENT distributed with this software for full */
/* terms and conditions of use. */
#ifndef lint
static char sccsinfo[] = "@(#)o_nomin.c	1.17 2/17/92";
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netdb.h>

#include <string.h>

#include "ops.h"
#include "storage.h"
#include "sysdep.h"

#define Dst (DstObj->value.nominal)
#define Src (SrcObj->value.nominal)
#define Src1 (Src1Obj->value.nominal)
#define Src2 (Src2Obj->value.nominal)

#define init_nominal(objectp, val) (set_init((objectp), dr_nominal), (objectp)->value.nominal = (val))

extern datarep dr_nominal;

#define HOSTBUFSIZE 128

/*
 * nominal values.
 *
 * A nominal is a globally unique value.  We produce these as follows:
 * for a single machine implementation, we use the gettimeofday(.)
 * function, which returns the number of seconds since 1970 in GMT
 * (the "epoch"), and the number of milliseconds since the last second
 * tick.  A nominal is represented by a record containing the epoch
 * (time) and a "unique" number (num), which is incremented each time
 * a nominal is generated.  In order to discriminate between
 * successive or concurrent runs which start in the same second, the
 * unique number is initialized to 1,000,000 times the millisecond
 * count.  When the unique number counter overflows the capacity of a
 * counter, the epoch is reset to the current time, and the unique
 * number starts counting again from 0.  Thus within a single run, no
 * two nominals can have the same value unless over 4 billion
 * (assuming a 32-bit counter) are generated in one second.
 * Between runs, collisions can occur when the runs start in the same
 * second, N milliseconds apart, if the earlier run generates N
 * million nominals.  The same consideration applies, slightly
 * modified to collisions between the nominals of a run which starts
 * in a given second, and another which cycles during the same second.
 * Two runs which cycle during the same second are much more
 * vulnerable to collisions, but the minimum number of generated
 * nominals for each of the runs must exceed 3 billion for this to
 * occur. 
 */

static time_t epoch;
static counter uniquenum = 0;

/* in a distributed environment, a uniqueid has to include the originating
 *   host.
 */

static char hostid[HOSTIDSIZE];
static long int scalenom = 1000000;

void
nominit()
{
    void distributed_init();
    struct timeval now;

    gettimeofday(&now, 0);
    epoch = now.tv_sec;
    uniquenum = scalenom * now.tv_usec;
    scalenom = 0;			/* subsequent cycles are full */
#ifdef undefined
    if (uniquenum >= MAXCOUNTER)
      uniquenum = (MAXCOUNTER / 1000) * now.millitm;
#endif
#ifdef DISTRIBUTED
    distributed_init();
#endif

}


void
distributed_init()
{
    void abort_nili();

    struct hostent *h;
    char hname[HOSTBUFSIZE];
    int i;


    if (gethostname(hname, HOSTBUFSIZE) isnt 0) {
	nilperror("distributed_init", "Gethostname() failed");
	abort_nili("Can't get hostname.");
    }

    h = gethostbyname(hname);

    if (h is nil) {
	nilerror("distributed_init", "Gethostbyname() failed.");
	abort_nili("Can't get host information.");
    }

    for (i=0; i < HOSTIDSIZE; i++) 
      if (i < h->h_length)
	hostid[i] = h->h_addr[i];
      else
	hostid[i] = 0;
}


NILOP(o_unique)
{
  predef_exception che_unique();
  predef_exception retcode;
    
  if ((retcode = che_unique(DstObj, args->sched)) != Normal)
       raise_builtin(retcode);
  return;
}

/* che_unique is called directly by generated C-code */
predef_exception che_unique(obj, sched)
objectp obj;
schedblock* sched;
{
    extern flag cherm_flag;
    dfd_nominal *newnominal;
    static int pid = -1;

    if (pid == -1)
      pid = getpid();

    if (uniquenum == 0)
      nominit();		/* (re)initialize epoch, uniquenum

				/* allocate storage for a new nominal */
    if ((newnominal = new(dfd_nominal)) is nil)
      return(Depletion);
    else {
	newnominal->refcount = 1; /* set reference count to 1 */
	newnominal->time = epoch;
	newnominal->num = uniquenum;

/* following ifdef assumes incrementing wraps all by itself */
#ifdef undefined
	if (uniquenum is MAXCOUNTER)
	  uniquenum = 0;		/* wrap around if at max... */
	else
#endif
	  uniquenum++;		/* otherwise just bump counter. */

#ifdef DISTRIBUTED
	(void) strncpy(newnominal->interp.hostname, hostid, HOSTIDSIZE);
	newnominal->interp.number = pid;
#endif
	if (not cherm_flag)
	  re_finalize(obj, F_DISCARD, sched);
	init_nominal(obj, newnominal);
	return(Normal);
    }
}


/*ARGSUSED*/
void
fin_nominal(value, f_op, sched)
valcell value;
finalize_op f_op;
schedblock *sched;
{
    if (--value.nominal->refcount <= 0)
				/* decrement the reference count.  if 0... */
      { dispose(value.nominal, dfd_nominal); }
				/* ...free the nominal cell. */
}



predef_exception
cp_nominal(dst, src)
valcell *dst, src;
{
    valcell newnominal;

    if (src.nominal->refcount is MAXCOUNTER) {
	/* allocate a complete new nominal if we can't bump ref count */
	if ((newnominal.nominal = new(dfd_nominal)) is nil)
	  return(Depletion);
	else {
	    *newnominal.nominal = *src.nominal;
	    newnominal.nominal->refcount = 1;
	    dst->nominal = newnominal.nominal;
	    return(Normal);
	}
    }
    else {
	/* share storage with source nominal */
	src.nominal->refcount++;
	dst->nominal = src.nominal;
	return(Normal);
    }
}

status 
eq_nominal(val1, val2)
valcell val1, val2;
{
#ifdef DISTRIBUTED
    status compare_interpnames();
#endif

    if (val1.nominal is val2.nominal)
      return(SUCCESS);		/* quick check for sharing */
    if (val1.nominal->time is val2.nominal->time and
	val1.nominal->num  is val2.nominal->num
#ifdef DISTRIBUTED
	and compare_interpnames(& val1.nominal->interp, & val2.nominal->interp)
	is SUCCESS
#endif
	)
      return(SUCCESS);
    else
      return(FAILURE);
}
