// A simple simulation of an M/M/1 queue to determine server utilization.
//
// CS 525, Simulation, Spring 2005

#include <cassert>
#include <queue>
#include <deque>
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include "rng.h"


// Simulation time units.

  typedef double ticks;


// The arrival and service distributions.

   static rng * arrival_rate, * service_time;


// The trigger action type.

   typedef void (* trigger_proc)();


// An event; the scheduling unit.

   struct event {

     // When this event should be triggered.

	ticks trigger_time;

     // What happens when this event gets triggered.

	trigger_proc trigger;

     // Create a new event with the given the absolute trigger time and
     // behavior.

	event(ticks tt, trigger_proc t)
	  : trigger_time(tt), trigger(t) { }

     // Return true iff this event is greater than the given event.

       bool operator > (const event & e) const {
	 return trigger_time > e.trigger_time;
	 }
     };


// The event queue.  Maintained in increasing trigger time; events with
// identical trigger times are pulled off the queue in an arbitrary order.

   static std::priority_queue<event, std::deque<event>, std::greater<event> >
     event_queue;


// The most recent time at which the server went from idle to busy.

   static ticks server_starts;

// The total time at which the server has been busy so far.

   static ticks server_busy_time;


// The current simulation time; it's equal to the trigger time of the most
// recently triggered event.

   static ticks current_time;


// The number of customers waiting for service, including the one being
// serviced (if any).

   static unsigned queue_length;


// Deja vu, c++ style.

   static void server_start();
   static void schedule(const event &);
   static void run();


static void 
client() {

  // A client arrives for service.

  static unsigned clients = 0;

  if (++clients < 1000) {
    if (1 == ++queue_length)
      schedule(event(current_time, server_start));
    schedule(event(current_time + arrival_rate->next(), client));
    }
  }


int 
main(int argc, char * argv[]) {

  if (argc != 3) {
    std::cerr << "Command format is \"" << argv[0] 
	      << " arrival-rate service-time\".\n";
    exit(1);
    }

  srand48(getpid());

  total_service_time = current_time = 0;
  arrival_rate = new exponential(atof(argv[1]));
  service_time = new exponential(atof(argv[2]));

  schedule(event(current_time, client));

  run();

  std::cout << total_service_time/current_time << "\n";

  delete arrival_rate;
  delete service_time;
  }

static void
run() {

  // Pull events off the event queue and trigger them; keep doing it until
  // there ain't no mo'.

  while (not event_queue.empty()) {
    const event e = event_queue.top();
    event_queue.pop();
    current_time = e.trigger_time;
    e.trigger();
    }
  }


static void 
schedule(const event & e) {

  // Schedule the given event.

  assert(e.trigger_time >= current_time);

  event_queue.push(e);
  }


static void
server_continue() {

  // Once the server has finished with one customer, look for another customer
  // or go to the idle state.

  assert(queue_length > 0);
  if (--queue_length > 0)
    schedule(event(current_time + service_time->next(), server_continue));
  else {
    server_busy_time += current_time - server_starts;
    std::cout << server_busy_time/current_time << "\n";
    }
  }


static void
server_start() {

  // A new customer wakes the idle server up.

  assert(queue_length > 0);

  server_starts = current_time;
  schedule(event(current_time + service_time->next(), server_continue));
  }


// $Log: mm1-utilization.cc,v $
// Revision 1.4  2005/04/05 01:57:22  rclayton
// Updated.
//
// Revision 1.3  2005/04/04 03:10:43  rclayton
// Print the server utilization samples.
//
// Revision 1.2  2005/03/27 01:46:41  rclayton
// Define ticks.
//


syntax highlighted by Code2HTML, v. 0.9