[comp.lang.c] discrete-event simulation package in c or c++?

vignaux@vuwisor.nz (Tony Vignaux) (04/28/88)

I need a C or C++ based discrete-event simulation package
and I am considering developng one if necessary.

Before I start has anyone else out there developed such
a package to a useful form?

Perhaps some of you have useful pointers?


-- 
G A (Tony) Vignaux:        Inst of Statistics and Operations Research
Victoria University, P.O Box 600, Wellington, New Zealand.
tel:(4)721-000,FAX:(4)712 070....   Internet:vignaux@vuwisor.vuw.ac.nz
Bitnet:vignaux%vuwisor.vuw.ac.nz@uunet.uu.net

yuval@taux01.UUCP (Gideon Yuval) (05/01/88)

An article "How to Make your own Simulation  System"  (Hans  Lindstrom &  Jan
Skanholm;  Software  Practice  & Experience  11,  629-637 (1981)) describes a
collection of "C" macros & procedures (+ a  few  dozen  assembly-code  lines)
that gives "C" users the full power of Simula-67.

Technical  report  79.07,  "The  CSIM  simulation system" (same authors; from
Chalmers Univ. of  Technology,  S-41296  Gothenburg,  Sweden)  has  the  full
source-code.


-- 
Gideon Yuval, yuval@taux01.nsc.com, +972-2-690992 (home) ,-52-522255(work)
 Paper-mail: National Semiconductor, 6 Maskit St., Herzliyah, Israel

bs@alice.UUCP (05/06/88)

In article <145@vuwisor.nz>, vignaux@vuwisor.UUCP writes:
> I need a C or C++ based discrete-event simulation package
> and I am considering developng one if necessary.

> Before I start has anyone else out there developed such
> a package to a useful form?

The C++ shipping tape from AT&T comes with a library of classes that
supports Simula-style event-driven simulation. It is usually referred
to a ``the task system''. There is a paper about it in the release notes
and two in the Proceedings of the USENIX C++ workshop.

djones@megatest.UUCP (Dave Jones) (05/08/88)

I'll let you look at one I wrote.  Even though it's not very big,
it's a little tricky.  It took me a day and a half to
write it.  It's interesting to compare it to the "task" package that 
comes with the ATT C++.

[ I've never actually read anything about this subject, so I would 
be very happy to get some constructive hints for improvement. ]

DEMONSTRATION PURPOSES ONLY.  This is not a "software release". 

It runs under Sun3 OS.  I can't think of any reason why it would not 
work on any other system that has a standard C library, but then, 
that may only be because I haven't thought of it.  The tricky bit 
is to save and restore process-stacks and registers.  UNIX has 
alloca() and setjmp()/longjmp(). Just what the doctor ordered.

I didn't include the queue package Queue, or the priority-queue
package PQ, becuase they require more packages, etc., etc..,
and besides, this is only a demo, right?


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  sim2_demo.c sim2.h sim2.c
# Wrapped by djones@goofy on Sat May  7 20:38:14 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f sim2_demo.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sim2_demo.c\"
else
echo shar: Extracting \"sim2_demo.c\" \(2844 characters\)
sed "s/^X//" >sim2_demo.c <<'END_OF_sim2_demo.c'
Xstatic char
XRCSHeader[] = "$Header$";
Xstatic char
XCopyright[] = "Copyright (C) 1988 by Megatest Corporation All Rights Reserved";
Xstatic char
XTheAuthor[] = "Dave Jones   (djones at goofy)";
X#include "sim2.h"
X#include <stdio.h>
X
X/* Example user program for descrete event simulation. */
X
X/* Procedures "producer_proc" and "consumer_proc" run as
X** simulated processes. They communicate by means of a counting-
X** semaphores.  Semaphore "widget" signals the presence of 
X** widgets produced by producer.  Semaphore "need_widgets"
X** signals the consumer's readiness to accept widgets --
X** (the number of open slots in a widget-queue).
X**
X*/
X
XSimulation sim;
XSemaphore  got_widgets;
XSemaphore  need_widgets;
XProcess producer;
XProcess consumer;
X
Xmain()
X{
X  static void producer_proc(); 
X  static void consumer_proc(); 
X
X  /* Initialize the simulation. The initialization must be done
X  ** before each run.  As simulation cannot be restarted after it
X  ** returns.
X  */
X  PSim_init(&sim);
X
X  /* Schedule the two processes to run under the simulation.
X  ** (Processes may also be added by simulated process which are
X  ** running under the simulation.)
X  */
X  PSim_proc_sched(&sim, &producer, producer_proc);
X  PSim_proc_sched(&sim, &consumer, consumer_proc);
X
X  /* Initialize the semaphores which they will use to communicate. */
X  /* Producer has produced no widgets yet.  We will stipuate that
X  ** the consumer is ready only to accept one initially.
X  */
X  PSim_sema_init(&sim, &got_widgets, 0);
X  PSim_sema_init(&sim, &need_widgets,1);
X
X  /* Now run the simulation.  When a delay-event occurs which takes
X  ** the simulation beyond 55 ticks, the simulation will cease.
X  */
X  PSim_run(&sim, 55L);
X
X  printf("Producer was busy %d ticks.\n", producer.total_time);
X  printf("Consumer was busy %d ticks.\n", consumer.total_time);
X  printf("simulated time is %d.\n", sim.time);
X
X  exit(0);
X}
X
X
X
X
Xstatic int
Xproducer_proc(sim)
X    Simulation * sim;  /* pointer to simulation under which the
X		       ** process is running.  To get the process-
X		       ** record itself, use sim->active.
X		       */
X{
X  int i = 0;
X  while(1)
X    {
X      /* Simulate being busy 8 ticks building a widget. */
X      i++;
X      fprintf(stderr, "Producing %d at %d\n", i, sim->time);
X      PSim_delay(sim, 8);
X
X      /* widget is now ready. */
X      fprintf(stderr, "%d finished at %d\n", i, sim->time);
X      Psema_wait(&need_widgets);
X      Psema_signal(&got_widgets); 
X    }
X}
X
X
X
X
Xstatic int
Xconsumer_proc(sim)
X     Simulation * sim;
X{
X  int i=0;
X  while(1)
X    {
X      i++;
X      fprintf(stderr, "Consumer waits for %d at %d\n", i, sim->time);
X      Psema_wait(&got_widgets);
X
X      /* Simulate being busy 3 ticks consuming a widget. */
X      fprintf(stderr, "Consuming %d at %d\n", i, sim->time);
X      PSim_delay(sim, 3);
X      Psema_signal(&need_widgets);
X
X    }
X}
END_OF_sim2_demo.c
if test 2844 -ne `wc -c <sim2_demo.c`; then
    echo shar: \"sim2_demo.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sim2.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sim2.h\"
else
echo shar: Extracting \"sim2.h\" \(2472 characters\)
sed "s/^X//" >sim2.h <<'END_OF_sim2.h'
X/*
X** RCSHeader = $Header$
X** Copyright = Copyright (C) 1988 by Megatest Corporation All Rights Reserved
X** TheAuthor = Dave Jones   (djones at goofy)
X*/
X
X#ifndef C_SIM
X#define C_SIM
X
X#include "queue.h"
X#include "PQ.h"
X#include <setjmp.h>
X
Xtypedef struct process Process;
X
X/***************************************************/
X/****************** Simulation *********************/
X
Xtypedef struct
X{
X  /******* read only ********/
X  unsigned long time;      /* number of ticks which have transpired */
X  unsigned long stop_time; /* when to stop the simulation */
X  Process* active;         /* the active process */
X
X  /******  private  *********/ 
X  PQ busy;             /* priority queue of busy processes */
X  Bool quit;           /* true iff we should return from PSim_run to caller. */
X  Queue semaphores;    /* list of all semaphores in this simulation */
X
X  char* stack_bottom;  /* marks the division on the stack between
X		       ** that used by the scheduler, and that used
X		       ** by simulated processes.
X		       */
X
X}Simulation;
X
X
X/***************************************************/
X/******************  Semaphore *********************/
X
Xtypedef struct
X{
X  /***** private *****/
X  Simulation* sim;  /* The simulation for which the semaphore is valid */
X  Queue queue;      /* queue of waiting processes */
X  int signals;
X
X}Semaphore;
X
X
X
X/***************************************************/
X/******************  Process ***********************/
X
Xstruct process
X{
X  /* read-only */
X  unsigned long total_time;  /* read-only */
X  int return_value;          /* read-only after "process" returns */
X                             /* Returned by "start" procedure */
X                             /* If process never returned, will be zero. */
X
X  /* private   */
X  unsigned long busy_until;  /* Simulated time at which "process" will resume*/
X  int (*start)();            /* The procedure of the simulated process */
X  char* stack_save;          /* mallocked memory to save the stack */
X  char* stack_real;          /* pointer to the "real" stack of the process */
X  long  stack_size;          /* size of the above */
X  jmp_buf restart;           /* longjmp to this to restart the process */
X  jmp_buf suspend;           /* longjmp to this to suspend the process */
X
X};
X
Xextern Simulation* PSim_init();
Xextern unsigned long PSim_run();
Xextern void PSim_proc_init();
Xextern void PSim_sema_init();
Xextern void Psema_signal();
Xextern void Psema_wait();
X
X#endif C_SIM
END_OF_sim2.h
if test 2472 -ne `wc -c <sim2.h`; then
    echo shar: \"sim2.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sim2.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sim2.c\"
else
echo shar: Extracting \"sim2.c\" \(7323 characters\)
sed "s/^X//" >sim2.c <<'END_OF_sim2.c'
Xstatic char
XRCSHeader[] = "$Header$";
Xstatic char
XCopyright[] = "Copyright (C) 1988 by Megatest Corporation All Rights Reserved";
Xstatic char
XTheAuthor[] = "Dave Jones   (djones at goofy)";
X
X#include "sim2.h"
X#include <stdio.h>
X#include "heap.h"
X
X/******************************************************************
X**
X** Discrete event simulation package.   See sim2_demo.c.
X**
X*******************************************************************/
X
X
X
X/* This procedure is used by the priority-queue package PQ to 
X** order processes which are "busy" until some simulated time.
X** The first process not to be busy will be scheduled to run.
X*/
Xstatic
XBool process_precedes(proc1,proc2)
X  Process* proc1;
X  Process* proc2;
X{
X  return proc1->busy_until < proc2->busy_until;
X}
X
X
X
X
X
X
X/*****************************************************/
X/*        Initialize a simulation                    */
X/*****************************************************/
X
XSimulation*
XPSim_init(obj)
X  register Simulation* obj;
X{
X  obj->time = 0;   /* elapsed simulated time */
X
X  /* priority queue of busy processes */
X  PQ_init(&obj->busy, process_precedes );
X
X  /* keep up with semaphores, in order to clean up later. */
X  Queue_init(&obj->semaphores);
X
X  return obj;
X}
X
X
X
X
X/************************************************************************/
X/* Schedule a process to run in a simulation. (Call PSim_init() first.) */
X/************************************************************************/
X
Xvoid
XPSim_proc_sched(obj, proc, start )
X     Simulation* obj;
X     register  Process* proc;     
X     int (*start)();
X{
X  proc->total_time = 0L;
X  proc->start = start;
X  proc->stack_save = (char*)0;
X  proc->stack_size = 0;
X  proc->return_value = 0;
X
X  /* Schedule it as ready now. */
X  proc->busy_until = obj->time;
X  PQ_insert(&obj->busy, (Ptr)proc);
X
X}
X
X
X
X
X#define PSim_first(obj) ((Process*)(PQ_first(&((obj)->busy))))
X
X/***********************************************************************/
X/*       Delay the active process by some number of ticks.             */
X/***********************************************************************/
X
XPSim_delay(obj, ticks)
X  register Simulation* obj;
X  int ticks;
X{
X  register Process* proc = obj->active;
X
X  /* Update time until which the process will be busy, and
X  ** the total amount of time it will have been busy at
X  ** that time.
X  */
X  proc->total_time += ticks;
X  proc->busy_until = obj->time + ticks;
X
X  /* If the first queued process is ready before this one,
X  ** we must suspend the active process, and start a
X  ** queued one.  If not, just tick the clock.
X  */
X  if((PSim_first(obj)->busy_until < obj->active->busy_until)
X     || obj->active->busy_until >= obj->stop_time
X    )
X    {
X      /* schedule this process as "busy" */
X      PQ_insert(&(obj->busy), obj->active);
X      suspend_active_proc(obj);
X    }
X  else
X    obj->time = proc->busy_until;
X}
X
X
X
X
X/***********************************************************************/
X/*                   stop the simulation                               */
X/***********************************************************************/
X
XPSim_stop(obj)
X  Simulation* obj;
X{
X  obj->quit = 1;
X  suspend_active_proc(obj);
X}
X
X
Xextern char* alloca();
X
X/***********************************************************************/
X/* Run the simulation, stopping after some number of ticks, unless
X** all processes exit, or some process calls PSim_stop() first.
X*/
X/***********************************************************************/
X
X
Xunsigned long
XPSim_run(obj, ticks)
X  Simulation* obj;
X  unsigned long ticks;
X{
X  obj->stack_bottom = (char*)alloca(1);
X  obj->stop_time += ticks;
X
X  while(!obj->quit)
X    {
X      /* Get a busy process from the busy-queue */
X      obj->active = (Process*) PQ_pop(&obj->busy);
X
X      /* If all processes are finished, or are waiting on 
X      ** a semaphore, we are blocked, and must exit the simulation.
X      */
X      if(obj->active==0) goto end_simulation;
X
X      /* Update the time to the time of the active process */
X      obj->time = obj->active->busy_until;
X
X      if( obj->time >= obj->stop_time) goto end_simulation;
X
X      if(setjmp(obj->active->suspend) == 0)
X	{
X	  if(obj->active->stack_save == 0)
X	    {
X	      /* Process has not yet started. Call its start-procedure. */
X	      obj->active->return_value =
X		(*(obj->active->start))(obj);
X	    }
X	  else
X	    { /* Process has been suspended, and will now be restarted. */
X	      
X	      /* allocate the restarting process's stack. */
X	      alloca( obj->active->stack_size );
X	      
X	      /* restore it */
X	      bcopy(  obj->active->stack_save, obj->active->stack_real,
X		    obj->active->stack_size);
X	      sfree(obj->active->stack_save);
X	      obj->active->stack_save = 0;
X	      
X	      /* restart the process */
X	      longjmp(obj->active->restart, 1);
X	    }
X	}
X    }
X
X end_simulation:
X  cleanup(obj);
X  return obj->time;
X}
X
X
Xstatic
Xcleanup(obj)
X  Simulation* obj;
X{
X  PQ_clean(&(obj->busy));
X
X  /* Iterate through semaphores, cleaning them up. */
X  {
X    Semaphore* sema;
X    while( sema = (Semaphore*)Queue_pop(&obj->semaphores))
X      {
X	Process *proc;
X	while( proc = (Process*)Queue_pop(&sema->queue))
X	  if(proc->stack_save != (char*)0)
X	    sfree(proc->stack_save);
X
X	Queue_clean(&sema->queue);
X      }
X    Queue_clean(&obj->semaphores);
X  }
X}
X
X
X
X
X  
X/***********************************************************************/
X/*                     Initialize a semaphore                          */
X/***********************************************************************/
X
Xvoid
XPSim_sema_init(sim, sema, signals)
X     Simulation* sim;
X     Semaphore* sema;
X{
X  sema->signals = signals;
X  sema->sim = sim;
X  Queue_init(&sema->queue);
X  Queue_append(&sim->semaphores, sema);
X}
X
X
X/***********************************************************************/
X/*                      Wait on a semaphore                            */
X/***********************************************************************/
X
Xvoid
XPsema_wait(obj)
X     register Semaphore* obj;
X{
X  if( obj->signals-- > 0)
X    {/* don't suspend. */
X    }
X  else
X    { Queue_append(&obj->queue, (Ptr)obj->sim->active);
X      suspend_active_proc(obj->sim);
X    }
X}
X
X
X
X/***********************************************************************/
X/*                      signal a semaphore                             */
X/***********************************************************************/
X
Xvoid
XPsema_signal(obj)
X     register Semaphore* obj;
X{
X  if(++obj->signals <= 0 )
X    { Process* ready = (Process*)Queue_pop(&(obj->queue));
X      ready->busy_until = obj->sim->time;
X      PQ_insert(&obj->sim->busy, ready);
X    }
X}
X
X
X
X
X
X#define min(a,b)        ((a)<(b)?(a):(b))
X#define abs(a)          ((a)< 0?-(a):(a))
X
X/** suspend the active process **/
X
Xstatic 
Xsuspend_active_proc(obj)
X  register Simulation* obj;
X{
X  char* stack_top = alloca(1);
X  long size = abs(obj->stack_bottom - stack_top);
X  
X  obj->active->stack_save = (char*)smalloc(size);
X  obj->active->stack_real = min(stack_top, obj->stack_bottom);
X  obj->active->stack_size = size;
X
X  if(setjmp(obj->active->restart) == 0)
X    {       
X      /* copy the stack and return to the simulator. */
X      bcopy( obj->active->stack_real, obj->active->stack_save, size);
X      longjmp(obj->active->suspend, 1);
X    }
X}
END_OF_sim2.c
if test 7323 -ne `wc -c <sim2.c`; then
    echo shar: \"sim2.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0