codeRev/src/hla/model/rti-simulator-impl.cc
author muditraajgupta
Sat, 18 Aug 2012 16:05:34 -0400
changeset 15 528951a6288c
parent 12 a47ffb23b237
permissions -rw-r--r--
Added an external federate for Integration testing

/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/*
 * Copyright (c) 2008 University of Washington
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation;
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "ns3/simulator.h"
#include "ns3/rti-simulator-impl.h"
#include "ns3/rti-clock-synchronizer.h"
#include "ns3/scheduler.h"
#include "ns3/event-impl.h"
#include "ns3/synchronizer.h"

#include "ns3/ptr.h"
#include "ns3/pointer.h"
#include "ns3/assert.h"
#include "ns3/fatal-error.h"
#include "ns3/log.h"
#include "ns3/system-mutex.h"
#include "ns3/boolean.h"
#include "ns3/enum.h"

#include <unistd.h>
#include <math.h>
#include <ns3/link-to-rti.h>

NS_LOG_COMPONENT_DEFINE ("RtiSimulatorImpl");

namespace ns3 {

NS_OBJECT_ENSURE_REGISTERED (RtiSimulatorImpl);

LinkToRti o;

TypeId
RtiSimulatorImpl::GetTypeId (void)
{
  static TypeId tid = TypeId ("ns3::RtiSimulatorImpl")
    .SetParent<SimulatorImpl> ()
    .AddConstructor<RtiSimulatorImpl> ()
    .AddAttribute ("SynchronizationMode", 
                   "What to do if the simulation cannot keep up with real time.",
                   EnumValue (SYNC_BEST_EFFORT),
                   MakeEnumAccessor (&RtiSimulatorImpl::SetSynchronizationMode),
                   MakeEnumChecker (SYNC_BEST_EFFORT, "BestEffort",
                                    SYNC_HARD_LIMIT, "HardLimit"))
    .AddAttribute ("HardLimit", 
                   "Maximum acceptable real-time jitter (used in conjunction with SynchronizationMode=HardLimit)",
                   TimeValue (Seconds (0.1)),
                   MakeTimeAccessor (&RtiSimulatorImpl::m_hardLimit),
                   MakeTimeChecker ())
  ;
  return tid;
}


RtiSimulatorImpl::RtiSimulatorImpl ()
{
  NS_LOG_FUNCTION_NOARGS ();

  m_stop = false;
  m_running = false;
  // uids are allocated from 4.
  // uid 0 is "invalid" events
  // uid 1 is "now" events
  // uid 2 is "destroy" events
  m_uid = 4; 
  // before ::Run is entered, the m_currentUid will be zero
  m_currentUid = 0;
  m_currentTs = 0;
  m_currentContext = 0xffffffff;
  m_unscheduledEvents = 0;

  m_main = SystemThread::Self();

  // Be very careful not to do anything that would cause a change or assignment
  // of the underlying reference counts of m_synchronizer or you will be sorry.
  m_synchronizer = CreateObject<RtiClockSynchronizer> ();
}

RtiSimulatorImpl::~RtiSimulatorImpl ()
{
}

void
RtiSimulatorImpl::DoDispose (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  while (!m_events->IsEmpty ())
    {
      Scheduler::Event next = m_events->RemoveNext ();
      next.impl->Unref ();
      if(m_events->IsEmpty ())
      {
        o.g_NoMoreNs3Events=1;
      }
    }
  
  m_events = 0;
  m_synchronizer = 0;
  SimulatorImpl::DoDispose ();
}

void
RtiSimulatorImpl::Destroy ()
{
  NS_LOG_FUNCTION_NOARGS ();

  //
  // This function is only called with the private version "disconnected" from
  // the main simulator functions.  We rely on the user not calling 
  // Simulator::Destroy while there is a chance that a worker thread could be
  // accessing the current instance of the private object.  In practice this
  // means shutting down the workers and doing a Join() before calling the
  // Simulator::Destroy().
  //
  while (m_destroyEvents.empty () == false) 
    {
      Ptr<EventImpl> ev = m_destroyEvents.front ().PeekEventImpl ();
      m_destroyEvents.pop_front ();
      NS_LOG_LOGIC ("handle destroy " << ev);
      if (ev->IsCancelled () == false)
        {
          ev->Invoke ();
        }
    }
}

void
RtiSimulatorImpl::SetScheduler (ObjectFactory schedulerFactory)
{
  NS_LOG_FUNCTION_NOARGS ();

  Ptr<Scheduler> scheduler = schedulerFactory.Create<Scheduler> ();

  { 
    CriticalSection cs (m_mutex);

    if (m_events != 0)
      {
        while (m_events->IsEmpty () == false)
          {
            Scheduler::Event next = m_events->RemoveNext ();
            scheduler->Insert (next);
          }
      }
    m_events = scheduler;
  }
}

void
RtiSimulatorImpl::ProcessOneEvent (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  //
  // The idea here is to wait until the next event comes due.  In the case of
  // a realtime simulation, we want real time to be consumed between events.
  // It is the realtime synchronizer that causes real time to be consumed by
  // doing some kind of a wait.
  //
  // We need to be able to have external events (such as a packet reception event)
  // cause us to re-evaluate our state.  The way this works is that the synchronizer
  // gets interrupted and returns.  So, there is a possibility that things may change
  // out from under us dynamically.  In this case, we need to re-evaluate how long to 
  // wait in a for-loop until we have waited sucessfully (until a timeout) for the 
  // event at the head of the event list.
  //
  // m_synchronizer->Synchronize will return true if the wait was completed without 
  // interruption, otherwise it will return false indicating that something has changed
  // out from under us.  If we sit in the for-loop trying to synchronize until 
  // Synchronize() returns true, we will have successfully synchronized the execution 
  // time of the next event with the wall clock time of the synchronizer.
  //

  for (;;) 
    {
      uint64_t tsDelay = 0;
      uint64_t tsNext = 0;

      //
      // It is important to understand that m_currentTs is interpreted only as the 
      // timestamp  of the last event we executed.  Current time can a bit of a 
      // slippery concept in realtime mode.  What we have here is a discrete event 
      // simulator, so the last event is, by defintion, executed entirely at a single
      //  discrete time.  This is the definition of m_currentTs.  It really has 
      // nothing to do with the current real time, except that we are trying to arrange
      // that at the instant of the beginning of event execution, the current real time
      // and m_currentTs coincide.
      //
      // We use tsNow as the indication of the current real time.
      //
      uint64_t tsNow;

      { 
        CriticalSection cs (m_mutex);
        //
        // Since we are in realtime mode, the time to delay has got to be the 
        // difference between the current realtime and the timestamp of the next 
        // event.  Since m_currentTs is actually the timestamp of the last event we 
        // executed, it's not particularly meaningful for us here since real time has
        // certainly elapsed since it was last updated.
        //
        // It is possible that the current realtime has drifted past the next event
        // time so we need to be careful about that and not delay in that case.
        //
        NS_ASSERT_MSG (m_synchronizer->Realtime (), 
                       "RealtimeSimulatorImpl::ProcessOneEvent (): Synchronizer reports not Realtime ()");

        //
        // tsNow is set to the normalized current real time.  When the simulation was
        // started, the current real time was effectively set to zero; so tsNow is
        // the current "real" simulation time.
        //
        // tsNext is the simulation time of the next event we want to execute.
        //
        tsNow = m_synchronizer->GetCurrentRealtime ();
        tsNext = NextTs ();

        //
        // tsDelay is therefore the real time we need to delay in order to bring the
        // real time in sync with the simulation time.  If we wait for this amount of
        // real time, we will accomplish moving the simulation time at the same rate
        // as the real time.  This is typically called "pacing" the simulation time.
        //
        // We do have to be careful if we are falling behind.  If so, tsDelay must be
        // zero.  If we're late, don't dawdle.
        // 
         
        if (tsNext <= tsNow)
          {
            NS_LOG_UNCOND("\n\nPresent Time (in ns):");
            NS_LOG_UNCOND(tsNow/1000);
            NS_LOG_UNCOND("Event Time Missed, Ns3 Time More Than RTI Time");
            NS_LOG_UNCOND("Processing Event ...");
            NS_LOG_UNCOND("\n\n");
            tsDelay = 0;
          }

        else
          {
            NS_LOG_UNCOND("\n\nPresent Time (in ns):");
            NS_LOG_UNCOND(tsNow);
            NS_LOG_UNCOND("Event in Future, Ns3 Time Less Than RTI Time");
            NS_LOG_UNCOND("Wait For a Time Advance Signal from RTI ...");
            NS_LOG_UNCOND("\n\n");
            o.ReqTimeAdv(tsNext);
            tsDelay = tsNext - tsNow;
          }

        //
        // We've figured out how long we need to delay in order to pace the 
        // simulation time with the real time.  We're going to sleep, but need
        // to work with the synchronizer to make sure we're awakened if something 
        // external happens (like a packet is received).  This next line resets
        // the synchronizer so that any future event will cause it to interrupt.
        //
        m_synchronizer->SetCondition (false);
      }

      //
      // We have a time to delay.  This time may actually not be valid anymore
      // since we released the critical section immediately above, and a real-time
      // ScheduleReal or ScheduleRealNow may have snuck in, well, between the 
      // closing brace above and this comment so to speak.  If this is the case, 
      // that schedule operation will have done a synchronizer Signal() that 
      // will set the condition variable to true and cause the Synchronize call 
      // below to return immediately.
      //
      // It's easiest to understand if you just consider a short tsDelay that only
      // requires a SpinWait down in the synchronizer.  What will happen is that 
      // whan Synchronize calls SpinWait, SpinWait will look directly at its 
      // condition variable.  Note that we set this condition variable to false 
      // inside the critical section above. 
      //
      // SpinWait will go into a forever loop until either the time has expired or
      // until the condition variable becomes true.  A true condition indicates that
      // the wait should stop.  The condition is set to true by one of the Schedule
      // methods of the simulator; so if we are in a wait down in Synchronize, and
      // a Simulator::ScheduleReal is done, the wait down in Synchronize will exit and
      // Synchronize will return false.  This means we have not actually synchronized
      // to the event expiration time.  If no real-time schedule operation is done
      // while down in Synchronize, the wait will time out and Synchronize will return 
      // true.  This indicates that we have synchronized to the event time.
      //
      // So we need to stay in this for loop, looking for the next event timestamp and 
      // attempting to sleep until its due.  If we've slept until the timestamp is due, 
      // Synchronize returns true and we break out of the sync loop.  If an external
      // event happens that requires a re-schedule, Synchronize returns false and
      // we re-evaluate our timing by continuing in the loop.
      //
      // It is expected that tsDelay become shorter as external events interrupt our
      // waits.
      //
      if (m_synchronizer->Synchronize (tsNow, tsDelay))
        {
          NS_LOG_LOGIC ("Interrupted ...");
          break;
        }
 
      //
      // If we get to this point, we have been interrupted during a wait by a real-time
      // schedule operation.  This means all bets are off regarding tsDelay and we need
      // to re-evaluate what it is we want to do.  We'll loop back around in the 
      // for-loop and start again from scratch.
      //
    }

  //
  // If we break out of the for-loop above, we have waited until the time specified
  // by the event that was at the head of the event list when we started the process.
  // Since there is a bunch of code that was executed outside a critical section (the
  // Synchronize call) we cannot be sure that the event at the head of the event list
  // is the one we think it is.  What we can be sure of is that it is time to execute
  // whatever event is at the head of this list if the list is in time order.
  //
  Scheduler::Event next;

  { 
    CriticalSection cs (m_mutex);

    // 
    // We do know we're waiting for an event, so there had better be an event on the 
    // event queue.  Let's pull it off.  When we release the critical section, the
    // event we're working on won't be on the list and so subsequent operations won't
    // mess with us.
    //
    NS_ASSERT_MSG (m_events->IsEmpty () == false, 
                   "RealtimeSimulatorImpl::ProcessOneEvent(): event queue is empty");
    next = m_events->RemoveNext ();
    m_unscheduledEvents--;

    //
    // We cannot make any assumption that "next" is the same event we originally waited 
    // for.  We can only assume that only that it must be due and cannot cause time 
    // to move backward.
    //
    NS_ASSERT_MSG (next.key.m_ts >= m_currentTs,
                   "RealtimeSimulatorImpl::ProcessOneEvent(): "
                   "next.GetTs() earlier than m_currentTs (list order error)");
    NS_LOG_LOGIC ("handle " << next.key.m_ts);

    // 
    // Update the current simulation time to be the timestamp of the event we're 
    // executing.  From the rest of the simulation's point of view, simulation time
    // is frozen until the next event is executed.
    //
    m_currentTs = next.key.m_ts;
    m_currentContext = next.key.m_context;
    m_currentUid = next.key.m_uid;

    // 
    // We're about to run the event and we've done our best to synchronize this
    // event execution time to real time.  Now, if we're in SYNC_HARD_LIMIT mode
    // we have to decide if we've done a good enough job and if we haven't, we've
    // been asked to commit ritual suicide.
    //
    // We check the simulation time against the current real time to make this
    // judgement.
    //
    if (m_synchronizationMode == SYNC_HARD_LIMIT)
      {
        uint64_t tsFinal = m_synchronizer->GetCurrentRealtime ();
        uint64_t tsJitter;

        if (tsFinal >= m_currentTs)
          {
            tsJitter = tsFinal - m_currentTs;
          }
        else
          {
            tsJitter = m_currentTs - tsFinal;
          }

        if (tsJitter > static_cast<uint64_t>(m_hardLimit.GetTimeStep ()))
          {
            NS_FATAL_ERROR ("RealtimeSimulatorImpl::ProcessOneEvent (): "
                            "Hard real-time limit exceeded (jitter = " << tsJitter << ")");
          }
      }
  }

  //
  // We have got the event we're about to execute completely disentangled from the 
  // event list so we can execute it outside a critical section without fear of someone
  // changing things out from under us.

  EventImpl *event = next.impl;
  m_synchronizer->EventStart ();
  event->Invoke ();
  m_synchronizer->EventEnd ();
  event->Unref ();
}

bool 
RtiSimulatorImpl::IsFinished (void) const
{
  NS_LOG_FUNCTION_NOARGS ();
  bool rc;
  {
    CriticalSection cs (m_mutex);
    rc = m_events->IsEmpty () || m_stop;
  }
  return rc;
}

//
// Peeks into event list.  Should be called with critical section locked.
//
uint64_t
RtiSimulatorImpl::NextTs (void) const
{
  NS_LOG_FUNCTION_NOARGS ();
  NS_ASSERT_MSG (m_events->IsEmpty () == false, 
                 "RealtimeSimulatorImpl::NextTs(): event queue is empty");
  Scheduler::Event ev = m_events->PeekNext ();
  return ev.key.m_ts;
}

void
RtiSimulatorImpl::Run (void)
{
  NS_LOG_FUNCTION_NOARGS ();

  NS_ASSERT_MSG (m_running == false, 
                 "RealtimeSimulatorImpl::Run(): Simulator already running");

  // Set the current threadId as the main threadId
  m_main = SystemThread::Self();

  m_stop = false;
  m_running = true;
  m_synchronizer->SetOrigin (m_currentTs);

  // Sleep until signalled
  uint64_t tsNow;
  uint64_t tsDelay = 1000000000; // wait time of 1 second (in nanoseconds)
 
  while (!m_stop) 
    {
      if(m_stop)
        {
                NS_LOG_LOGIC("ns3 events over");
        }
      bool process = false;
      {
        CriticalSection cs (m_mutex);

        if (!m_events->IsEmpty ())
          {
            process = true;
          }
        else
          {
            // Get current timestamp while holding the critical section
            tsNow = m_synchronizer->GetCurrentRealtime ();
          }
      }
 
      if (!process)
        {
          // Sleep until signalled
          tsNow = m_synchronizer->Synchronize (tsNow, tsDelay);

          // Re-check event queue
          continue;
        }

      ProcessOneEvent ();
    }

  //
  // If the simulator stopped naturally by lack of events, make a
  // consistency test to check that we didn't lose any events along the way.
  //
  {
    CriticalSection cs (m_mutex);

    NS_ASSERT_MSG (m_events->IsEmpty () == false || m_unscheduledEvents == 0,
                   "RealtimeSimulatorImpl::Run(): Empty queue and unprocessed events");
  }

  m_running = false;
}

bool
RtiSimulatorImpl::Running (void) const
{
  NS_LOG_FUNCTION_NOARGS ();
  return m_running;
}

bool
RtiSimulatorImpl::Realtime (void) const
{
  NS_LOG_FUNCTION_NOARGS ();
  return m_synchronizer->Realtime ();
}

void 
RtiSimulatorImpl::Stop (void)
{
  NS_LOG_FUNCTION_NOARGS ();
  m_stop = true;
}

void 
RtiSimulatorImpl::Stop (Time const &time)
{
  Simulator::Schedule (time, &Simulator::Stop);
}

//
// Schedule an event for a _relative_ time in the future.
//
EventId
RtiSimulatorImpl::Schedule (Time const &time, EventImpl *impl)
{
  NS_LOG_FUNCTION (time << impl);

  Scheduler::Event ev;
  {
    CriticalSection cs (m_mutex);
    //
    // This is the reason we had to bring the absolute time calcualtion in from the
    // simulator.h into the implementation.  Since the implementations may be 
    // multi-threaded, we need this calculation to be atomic.  You can see it is
    // here since we are running in a CriticalSection.
    //
    Time tAbsolute = Simulator::Now () + time;
    NS_ASSERT_MSG (tAbsolute.IsPositive (), "RealtimeSimulatorImpl::Schedule(): Negative time");
    NS_ASSERT_MSG (tAbsolute >= TimeStep (m_currentTs), "RealtimeSimulatorImpl::Schedule(): time < m_currentTs");
    ev.impl = impl;
    ev.key.m_ts = (uint64_t) tAbsolute.GetTimeStep ();
    ev.key.m_context = GetContext ();
    ev.key.m_uid = m_uid;
    m_uid++;
    m_unscheduledEvents++;
    m_events->Insert (ev);
    m_synchronizer->Signal ();
  }

  return EventId (impl, ev.key.m_ts, ev.key.m_context, ev.key.m_uid);
}

void
RtiSimulatorImpl::ScheduleWithContext (uint32_t context, Time const &time, EventImpl *impl)
{
  NS_LOG_FUNCTION (time << impl);

  {
    CriticalSection cs (m_mutex);
    uint64_t ts;

    if (SystemThread::Equals (m_main))
      {
        ts = m_currentTs + time.GetTimeStep ();
      }
    else
      {
        //
        // If the simulator is running, we're pacing and have a meaningful 
        // realtime clock.  If we're not, then m_currentTs is where we stopped.
        // 
        ts = m_running ? m_synchronizer->GetCurrentRealtime () : m_currentTs;
        ts += time.GetTimeStep ();
      }

    NS_ASSERT_MSG (ts >= m_currentTs, "RealtimeSimulatorImpl::ScheduleRealtime(): schedule for time < m_currentTs");
    Scheduler::Event ev;
    ev.impl = impl;
    ev.key.m_ts = ts;
    ev.key.m_context = context;
    ev.key.m_uid = m_uid;
    m_uid++;
    m_unscheduledEvents++;
    m_events->Insert (ev);
    m_synchronizer->Signal ();
  }
}

EventId
RtiSimulatorImpl::ScheduleNow (EventImpl *impl)
{
  NS_LOG_FUNCTION_NOARGS ();
  Scheduler::Event ev;
  {
    CriticalSection cs (m_mutex);

    ev.impl = impl;
    ev.key.m_ts = m_currentTs;
    ev.key.m_context = GetContext ();
    ev.key.m_uid = m_uid;
    m_uid++;
    m_unscheduledEvents++;
    m_events->Insert (ev);
    m_synchronizer->Signal ();
  }

  return EventId (impl, ev.key.m_ts, ev.key.m_context, ev.key.m_uid);
}

Time
RtiSimulatorImpl::Now (void) const
{
  return TimeStep (m_currentTs);
}

//
// Schedule an event for a _relative_ time in the future.
//
void
RtiSimulatorImpl::ScheduleRealtimeWithContext (uint32_t context, Time const &time, EventImpl *impl)
{
  NS_LOG_FUNCTION (context << time << impl);

  {
    CriticalSection cs (m_mutex);

    uint64_t ts = m_synchronizer->GetCurrentRealtime () + time.GetTimeStep ();
    NS_ASSERT_MSG (ts >= m_currentTs, "RealtimeSimulatorImpl::ScheduleRealtime(): schedule for time < m_currentTs");
    Scheduler::Event ev;
    ev.impl = impl;
    ev.key.m_ts = ts;
    ev.key.m_uid = m_uid;
    m_uid++;
    m_unscheduledEvents++;
    m_events->Insert (ev);
    m_synchronizer->Signal ();
  }
}

void
RtiSimulatorImpl::ScheduleRealtime (Time const &time, EventImpl *impl)
{
  NS_LOG_FUNCTION (time << impl);
  ScheduleRealtimeWithContext (GetContext (), time, impl);
}

void
RtiSimulatorImpl::ScheduleRealtimeNowWithContext (uint32_t context, EventImpl *impl)
{
  NS_LOG_FUNCTION (context << impl);
  {
    CriticalSection cs (m_mutex);

    //
    // If the simulator is running, we're pacing and have a meaningful 
    // realtime clock.  If we're not, then m_currentTs is were we stopped.
    // 
    uint64_t ts = m_running ? m_synchronizer->GetCurrentRealtime () : m_currentTs;
    NS_ASSERT_MSG (ts >= m_currentTs, 
                   "RealtimeSimulatorImpl::ScheduleRealtimeNowWithContext(): schedule for time < m_currentTs");
    Scheduler::Event ev;
    ev.impl = impl;
    ev.key.m_ts = ts;
    ev.key.m_uid = m_uid;
    ev.key.m_context = context;
    m_uid++;
    m_unscheduledEvents++;
    m_events->Insert (ev);
    m_synchronizer->Signal ();
  }
}

void
RtiSimulatorImpl::ScheduleRealtimeNow (EventImpl *impl)
{
  NS_LOG_FUNCTION (impl);
  ScheduleRealtimeNowWithContext (GetContext (), impl);
}

Time
RtiSimulatorImpl::RealtimeNow (void) const
{
  return TimeStep (m_synchronizer->GetCurrentRealtime ());
}

EventId
RtiSimulatorImpl::ScheduleDestroy (EventImpl *impl)
{
  NS_LOG_FUNCTION_NOARGS ();

  EventId id;
  {
    CriticalSection cs (m_mutex);

    //
    // Time doesn't really matter here (especially in realtime mode).  It is 
    // overridden by the uid of 2 which identifies this as an event to be 
    // executed at Simulator::Destroy time.
    //
    id = EventId (Ptr<EventImpl> (impl, false), m_currentTs, 0xffffffff, 2);
    m_destroyEvents.push_back (id);
    m_uid++;
  }

  return id;
}

Time 
RtiSimulatorImpl::GetDelayLeft (const EventId &id) const
{
  //
  // If the event has expired, there is no delay until it runs.  It is not the
  // case that there is a negative time until it runs.
  //
  if (IsExpired (id))
    {
      return TimeStep (0);
    }

  return TimeStep (id.GetTs () - m_currentTs);
}

void
RtiSimulatorImpl::Remove (const EventId &id)
{
  if (id.GetUid () == 2)
    {
      // destroy events.
      for (DestroyEvents::iterator i = m_destroyEvents.begin (); 
           i != m_destroyEvents.end (); 
           i++)
        {
          if (*i == id)
            {
              m_destroyEvents.erase (i);
              break;
            }
        }
      return;
    }
  if (IsExpired (id))
    {
      return;
    }

  {
    CriticalSection cs (m_mutex);

    Scheduler::Event event;
    event.impl = id.PeekEventImpl ();
    event.key.m_ts = id.GetTs ();
    event.key.m_context = id.GetContext ();
    event.key.m_uid = id.GetUid ();

    m_events->Remove (event);
    m_unscheduledEvents--;
    event.impl->Cancel ();
    event.impl->Unref ();
  }
}

void
RtiSimulatorImpl::Cancel (const EventId &id)
{
  if (IsExpired (id) == false)
    {
      id.PeekEventImpl ()->Cancel ();
    }
}

bool
RtiSimulatorImpl::IsExpired (const EventId &ev) const
{
  if (ev.GetUid () == 2)
    {
      if (ev.PeekEventImpl () == 0 ||
          ev.PeekEventImpl ()->IsCancelled ())
        {
          return true;
        }
      // destroy events.
      for (DestroyEvents::const_iterator i = m_destroyEvents.begin (); 
           i != m_destroyEvents.end (); i++)
        {
          if (*i == ev)
            {
              return false;
            }
        }
      return true;
    }

  //
  // If the time of the event is less than the current timestamp of the 
  // simulator, the simulator has gone past the invocation time of the 
  // event, so the statement ev.GetTs () < m_currentTs does mean that 
  // the event has been fired even in realtime mode.
  //
  // The same is true for the next line involving the m_currentUid.
  //
  if (ev.PeekEventImpl () == 0 ||
      ev.GetTs () < m_currentTs ||
      (ev.GetTs () == m_currentTs && ev.GetUid () <= m_currentUid) ||
      ev.PeekEventImpl ()->IsCancelled ()) 
    {
      return true;
    }
  else
    {
      return false;
    }
}

Time 
RtiSimulatorImpl::GetMaximumSimulationTime (void) const
{
  // XXX: I am fairly certain other compilers use other non-standard
  // post-fixes to indicate 64 bit constants.
  return TimeStep (0x7fffffffffffffffLL);
}

// System ID for non-distributed simulation is always zero
uint32_t 
RtiSimulatorImpl::GetSystemId (void) const
{
  return 0;
}

uint32_t
RtiSimulatorImpl::GetContext (void) const
{
  return m_currentContext;
}

void 
RtiSimulatorImpl::SetSynchronizationMode (enum SynchronizationMode mode)
{
  NS_LOG_FUNCTION (mode);
  m_synchronizationMode = mode;
}

RtiSimulatorImpl::SynchronizationMode
RtiSimulatorImpl::GetSynchronizationMode (void) const
{
  NS_LOG_FUNCTION_NOARGS ();
  return m_synchronizationMode;
}

void 
RtiSimulatorImpl::SetHardLimit (Time limit)
{
  NS_LOG_FUNCTION (limit);
  m_hardLimit = limit;
}

Time
RtiSimulatorImpl::GetHardLimit (void) const
{
  NS_LOG_FUNCTION_NOARGS ();
  return m_hardLimit;
}

} // namespace ns3