add realtime simulator and associated tests
authorCraig Dowell <craigdo@ee.washington.edu>
Tue Aug 26 15:34:57 2008 -0700 (17 months ago)
changeset 35795aa65b1ea001
parent 3576 acd6d5b9c40d
child 3580 e388935fa948
add realtime simulator and associated tests
examples/realtime-udp-echo.cc
examples/wscript
regression/tests/test-realtime-udp-echo.py
src/simulator/default-simulator-impl.cc
src/simulator/event-impl.cc
src/simulator/event-impl.h
src/simulator/realtime-simulator-impl.cc
src/simulator/realtime-simulator-impl.h
src/simulator/synchronizer.cc
src/simulator/synchronizer.h
src/simulator/wall-clock-synchronizer.cc
src/simulator/wall-clock-synchronizer.h
src/simulator/wscript
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/examples/realtime-udp-echo.cc	Tue Aug 26 15:34:57 2008 -0700
     1.3 @@ -0,0 +1,125 @@
     1.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
     1.5 +/*
     1.6 + * This program is free software; you can redistribute it and/or modify
     1.7 + * it under the terms of the GNU General Public License version 2 as
     1.8 + * published by the Free Software Foundation;
     1.9 + *
    1.10 + * This program is distributed in the hope that it will be useful,
    1.11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    1.12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    1.13 + * GNU General Public License for more details.
    1.14 + *
    1.15 + * You should have received a copy of the GNU General Public License
    1.16 + * along with this program; if not, write to the Free Software
    1.17 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    1.18 + */
    1.19 +
    1.20 +// Network topology
    1.21 +//
    1.22 +//       n0    n1   n2   n3
    1.23 +//       |     |    |    |
    1.24 +//       =================
    1.25 +//              LAN
    1.26 +//
    1.27 +// - UDP flows from n0 to n1 and back
    1.28 +// - DropTail queues 
    1.29 +// - Tracing of queues and packet receptions to file "udp-echo.tr"
    1.30 +
    1.31 +#include <fstream>
    1.32 +#include "ns3/core-module.h"
    1.33 +#include "ns3/simulator-module.h"
    1.34 +#include "ns3/helper-module.h"
    1.35 +
    1.36 +using namespace ns3;
    1.37 +
    1.38 +NS_LOG_COMPONENT_DEFINE ("RealtimeUdpEchoExample");
    1.39 +
    1.40 +int 
    1.41 +main (int argc, char *argv[])
    1.42 +{
    1.43 +  //
    1.44 +  // Make the random number generators generate reproducible results.
    1.45 +  //
    1.46 +  RandomVariable::UseGlobalSeed (1, 1, 2, 3, 5, 8);
    1.47 +
    1.48 +  //
    1.49 +  // Allow the user to override any of the defaults and the above Bind() at
    1.50 +  // run-time, via command-line arguments
    1.51 +  //
    1.52 +  CommandLine cmd;
    1.53 +  cmd.Parse (argc, argv);
    1.54 +
    1.55 +  //
    1.56 +  // But since this is a realtime script, don't allow the user to mess with
    1.57 +  // that.
    1.58 +  //
    1.59 +  GlobalValue::Bind ("SimulatorImplementationType", 
    1.60 +    StringValue ("ns3::RealtimeSimulatorImpl"));
    1.61 +
    1.62 +  //
    1.63 +  // Explicitly create the nodes required by the topology (shown above).
    1.64 +  //
    1.65 +  NS_LOG_INFO ("Create nodes.");
    1.66 +  NodeContainer n;
    1.67 +  n.Create (4);
    1.68 +
    1.69 +  InternetStackHelper internet;
    1.70 +  internet.Install (n);
    1.71 +
    1.72 +  //
    1.73 +  // Explicitly create the channels required by the topology (shown above).
    1.74 +  //
    1.75 +  NS_LOG_INFO ("Create channels.");
    1.76 +  CsmaHelper csma;
    1.77 +  csma.SetChannelAttribute ("DataRate", DataRateValue (DataRate(5000000)));
    1.78 +  csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
    1.79 +  csma.SetDeviceAttribute ("MTU", UintegerValue (1400));
    1.80 +  NetDeviceContainer d = csma.Install (n);
    1.81 +
    1.82 +  //
    1.83 +  // We've got the "hardware" in place.  Now we need to add IP addresses.
    1.84 +  //
    1.85 +  NS_LOG_INFO ("Assign IP Addresses.");
    1.86 +  Ipv4AddressHelper ipv4;
    1.87 +  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
    1.88 +  Ipv4InterfaceContainer i = ipv4.Assign (d);
    1.89 +
    1.90 +  NS_LOG_INFO ("Create Applications.");
    1.91 +
    1.92 +  //
    1.93 +  // Create a UdpEchoServer application on node one.
    1.94 +  //
    1.95 +  uint16_t port = 9;  // well-known echo port number
    1.96 +  UdpEchoServerHelper server (port);
    1.97 +  ApplicationContainer apps = server.Install (n.Get(1));
    1.98 +  apps.Start (Seconds (1.0));
    1.99 +  apps.Stop (Seconds (10.0));
   1.100 +
   1.101 +  //
   1.102 +  // Create a UdpEchoClient application to send UDP datagrams from node zero to
   1.103 +  // node one.
   1.104 +  //
   1.105 +  uint32_t packetSize = 1024;
   1.106 +  uint32_t maxPacketCount = 500;
   1.107 +  Time interPacketInterval = Seconds (0.01);
   1.108 +  UdpEchoClientHelper client (i.GetAddress (1), port);
   1.109 +  client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount));
   1.110 +  client.SetAttribute ("Interval", TimeValue (interPacketInterval));
   1.111 +  client.SetAttribute ("PacketSize", UintegerValue (packetSize));
   1.112 +  apps = client.Install (n.Get (0));
   1.113 +  apps.Start (Seconds (2.0));
   1.114 +  apps.Stop (Seconds (10.0));
   1.115 +
   1.116 +  std::ofstream ascii;
   1.117 +  ascii.open ("realtime-udp-echo.tr");
   1.118 +  CsmaHelper::EnablePcapAll ("realtime-udp-echo");
   1.119 +  CsmaHelper::EnableAsciiAll (ascii);
   1.120 +
   1.121 +  //
   1.122 +  // Now, do the actual simulation.
   1.123 +  //
   1.124 +  NS_LOG_INFO ("Run Simulation.");
   1.125 +  Simulator::Run ();
   1.126 +  Simulator::Destroy ();
   1.127 +  NS_LOG_INFO ("Done.");
   1.128 +}
     2.1 --- a/examples/wscript	Tue Aug 26 08:42:28 2008 -0700
     2.2 +++ b/examples/wscript	Tue Aug 26 15:34:57 2008 -0700
     2.3 @@ -32,6 +32,10 @@
     2.4          ['csma', 'internet-stack'])
     2.5      obj.source = 'udp-echo.cc'
     2.6  
     2.7 +    obj = bld.create_ns3_program('realtime-udp-echo',
     2.8 +        ['csma', 'internet-stack'])
     2.9 +    obj.source = 'realtime-udp-echo.cc'
    2.10 +
    2.11      obj = bld.create_ns3_program('csma-broadcast',
    2.12          ['csma', 'internet-stack'])
    2.13      obj.source = 'csma-broadcast.cc'
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/regression/tests/test-realtime-udp-echo.py	Tue Aug 26 15:34:57 2008 -0700
     3.3 @@ -0,0 +1,12 @@
     3.4 +#! /usr/bin/env python
     3.5 +
     3.6 +"""Generic trace-comparison-type regression test."""
     3.7 +
     3.8 +import os
     3.9 +import shutil
    3.10 +import tracediff
    3.11 +
    3.12 +def run(verbose, generate, refDirName):
    3.13 +    """Execute a test."""
    3.14 +
    3.15 +    return tracediff.run_test(verbose, generate, refDirName, "realtime-udp-echo")
     4.1 --- a/src/simulator/default-simulator-impl.cc	Tue Aug 26 08:42:28 2008 -0700
     4.2 +++ b/src/simulator/default-simulator-impl.cc	Tue Aug 26 15:34:57 2008 -0700
     4.3 @@ -48,6 +48,10 @@
     4.4  
     4.5  DefaultSimulatorImpl::DefaultSimulatorImpl ()
     4.6  {
     4.7 +  // No multithreaded stuff here, make sure EventImpl instances don't try and
     4.8 +  // use any stale locking functions.
     4.9 +  EventImpl::SetNoEventLock ();
    4.10 +
    4.11    m_stop = false;
    4.12    m_stopAt = 0;
    4.13    // uids are allocated from 4.
     5.1 --- a/src/simulator/event-impl.cc	Tue Aug 26 08:42:28 2008 -0700
     5.2 +++ b/src/simulator/event-impl.cc	Tue Aug 26 15:34:57 2008 -0700
     5.3 @@ -23,6 +23,7 @@
     5.4  
     5.5  namespace ns3 {
     5.6  
     5.7 +EventLock *EventImpl::m_eventLock = 0;
     5.8  
     5.9  EventImpl::~EventImpl ()
    5.10  {}
    5.11 @@ -31,6 +32,19 @@
    5.12    : m_cancel (false),
    5.13      m_count (1)
    5.14  {}
    5.15 +
    5.16 +void
    5.17 +EventImpl::SetEventLock (EventLock *eventLock)
    5.18 +{
    5.19 +  m_eventLock = eventLock;
    5.20 +}
    5.21 +
    5.22 +void
    5.23 +EventImpl::SetNoEventLock (void)
    5.24 +{
    5.25 +  m_eventLock = 0;
    5.26 +}
    5.27 +
    5.28  void 
    5.29  EventImpl::Invoke (void)
    5.30  {
    5.31 @@ -39,6 +53,7 @@
    5.32        Notify ();
    5.33      }
    5.34  }
    5.35 +
    5.36  void 
    5.37  EventImpl::Cancel (void)
    5.38  {
     6.1 --- a/src/simulator/event-impl.h	Tue Aug 26 08:42:28 2008 -0700
     6.2 +++ b/src/simulator/event-impl.h	Tue Aug 26 15:34:57 2008 -0700
     6.3 @@ -21,11 +21,69 @@
     6.4  #define EVENT_IMPL_H
     6.5  
     6.6  #include <stdint.h>
     6.7 +#include "ns3/system-mutex.h"
     6.8  
     6.9  namespace ns3 {
    6.10  
    6.11  /**
    6.12   * \ingroup simulator
    6.13 + * \brief Base class for locking functionality for events.
    6.14 + *
    6.15 + * This class provides a cheap way (from the perspective of the event) to lock 
    6.16 + * an event for multithreaded access.  This is a bit of a hack, but it lets us
    6.17 + * use a whole lot of existing mechanism in the multithreaded simulator case.
    6.18 + *
    6.19 + * Here's a taste of the problem.  It makes life extraordinarily easier in the 
    6.20 + * case of interfacing real network devices to be able to have threads reading
    6.21 + * from real sockets and doing ScheduleNow calls to inject packets into ns-3.
    6.22 + * It is desirable to have Schedule* calls all work similarly as well.  That is
    6.23 + * you don't want to have to do different calls when working from an "external"
    6.24 + * thread than you do when working in the context of the main simulation thread.
    6.25 + *
    6.26 + * It turns out that basically all of the Schedule* calls return an EventId.
    6.27 + * Each EventId holds a reference to the underlying event.  Clients (see the
    6.28 + * Applications for examples) often schedule events and hold onto the EventId
    6.29 + * in case they need to cancel the event later.  The EventImpl that underlies 
    6.30 + * all of this is reference counted and sharing an unprotected reference 
    6.31 + * counted object between threads is a bad thing.
    6.32 + *
    6.33 + * There were several possible solutions:
    6.34 + *
    6.35 + * - Put a mutex into each event (costs 40 bytes for the mutex and a minumum of 
    6.36 + *   three system calls to use an event);
    6.37 + * - Work on the inheritance diagram of EventImpl to make a 
    6.38 + *   MultithreadedEventImpl and pull apart all of the MakeEvent functions to 
    6.39 + *   teach them to make the right kind of event based on a flag returned by the
    6.40 + *   simulator;
    6.41 + * - Rework the entire event mechanism to use raw pointers and avoid the entire
    6.42 + *   reference counting approach with its associated problems;
    6.43 + * - Provide a cheap way to use a shared mutex (and avoid using the mutex in 
    6.44 + *   cases where it is not required).
    6.45 + *
    6.46 + * The original prototype chose the first option since it was easy.  I am very
    6.47 + * hesitant to rework the entire event mechanism or even pull apart the MakeEvent
    6.48 + * code at this point.  We went with the last option even though it feels a bit
    6.49 + * like a hack.
    6.50 + *
    6.51 + * The result is the EventLock class.  If you have a simulator implementation that 
    6.52 + * is going to need multithreaded access to events, you need to inherit from 
    6.53 + * EventLock and provide a object that does real locking.  Give the object to the 
    6.54 + * EventImpl code by calling EventImpl::SetEventLock, take it back by calling
    6.55 + * EventImpl::SetNoEventLock or provide a new one.  The EventImpl code takes no
    6.56 + * responsibility for the object passed in.
    6.57 + *
    6.58 + * \see EventImpl
    6.59 + */
    6.60 +class EventLock
    6.61 +{
    6.62 +public:
    6.63 +  virtual ~EventLock () {};
    6.64 +  virtual void Lock (void) = 0;
    6.65 +  virtual void Unlock (void) = 0;
    6.66 +};
    6.67 +
    6.68 +/**
    6.69 + * \ingroup simulator
    6.70   * \brief a simulation event
    6.71   *
    6.72   * Each subclass of this base class represents a simulation event. The
    6.73 @@ -34,6 +92,8 @@
    6.74   * obviously (there are Ref and Unref methods) reference-counted and
    6.75   * most subclasses are usually created by one of the many Simulator::Schedule
    6.76   * methods.
    6.77 + *
    6.78 + * \see EventLock
    6.79   */
    6.80  class EventImpl
    6.81  {
    6.82 @@ -58,12 +118,31 @@
    6.83     * Invoked by the simulation engine before calling Invoke.
    6.84     */
    6.85    bool IsCancelled (void);
    6.86 +
    6.87 +  /**
    6.88 +   * Provide a mutex with Lock and Unlock methods to the event implementation
    6.89 +   * so that it can do cheap (from the perspective of event memory usage)
    6.90 +   * critical sections (mutual exclusion) in the reference counting code.
    6.91 +   *
    6.92 +   * \param eventLock Pointer to the EventLock object used to contain the
    6.93 +   * underlying mutex.
    6.94 +   */
    6.95 +  static void SetEventLock (EventLock *eventLock);
    6.96 +  /**
    6.97 +   * Remove any reference the event implementation code may hold to to an 
    6.98 +   * existing EventLock and disable the event locking feature.
    6.99 +   *
   6.100 +   * \see SetEventLock
   6.101 +   */
   6.102 +  static void SetNoEventLock (void);
   6.103 +
   6.104  protected:
   6.105    virtual void Notify (void) = 0;
   6.106 +
   6.107  private:
   6.108 -  friend class Event;
   6.109    bool m_cancel;
   6.110    mutable uint32_t m_count;
   6.111 +  static EventLock *m_eventLock;
   6.112  };
   6.113  
   6.114  }; // namespace ns3
   6.115 @@ -73,13 +152,35 @@
   6.116  void
   6.117  EventImpl::Ref (void) const
   6.118  {
   6.119 -  m_count++;
   6.120 +  if (m_eventLock)
   6.121 +    {
   6.122 +      m_eventLock->Lock ();
   6.123 +      m_count++;
   6.124 +      m_eventLock->Unlock ();
   6.125 +    }
   6.126 +  else
   6.127 +    {
   6.128 +      m_count++;
   6.129 +    }
   6.130  }
   6.131 +
   6.132  void
   6.133  EventImpl::Unref (void) const
   6.134  {
   6.135 -  m_count--;
   6.136 -  if (m_count == 0)
   6.137 +  register uint32_t c;
   6.138 +
   6.139 +  if (m_eventLock)
   6.140 +    {
   6.141 +      m_eventLock->Lock ();
   6.142 +      c = --m_count;
   6.143 +      m_eventLock->Unlock ();
   6.144 +    }
   6.145 +  else
   6.146 +    {
   6.147 +      c = --m_count;
   6.148 +    }
   6.149 +
   6.150 +  if (c == 0)
   6.151      {
   6.152        delete this;
   6.153      }
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/simulator/realtime-simulator-impl.cc	Tue Aug 26 15:34:57 2008 -0700
     7.3 @@ -0,0 +1,794 @@
     7.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
     7.5 +/*
     7.6 + * Copyright (c) 2008 University of Washington
     7.7 + *
     7.8 + * This program is free software; you can redistribute it and/or modify
     7.9 + * it under the terms of the GNU General Public License version 2 as
    7.10 + * published by the Free Software Foundation;
    7.11 + *
    7.12 + * This program is distributed in the hope that it will be useful,
    7.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.15 + * GNU General Public License for more details.
    7.16 + *
    7.17 + * You should have received a copy of the GNU General Public License
    7.18 + * along with this program; if not, write to the Free Software
    7.19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    7.20 + */
    7.21 +
    7.22 +#include "simulator.h"
    7.23 +#include "realtime-simulator-impl.h"
    7.24 +#include "wall-clock-synchronizer.h"
    7.25 +#include "scheduler.h"
    7.26 +#include "event-impl.h"
    7.27 +#include "synchronizer.h"
    7.28 +
    7.29 +#include "ns3/ptr.h"
    7.30 +#include "ns3/pointer.h"
    7.31 +#include "ns3/assert.h"
    7.32 +#include "ns3/fatal-error.h"
    7.33 +#include "ns3/log.h"
    7.34 +#include "ns3/system-mutex.h"
    7.35 +#include "ns3/boolean.h"
    7.36 +#include "ns3/enum.h"
    7.37 +
    7.38 +
    7.39 +#include <math.h>
    7.40 +
    7.41 +NS_LOG_COMPONENT_DEFINE ("RealtimeSimulatorImpl");
    7.42 +
    7.43 +namespace ns3 {
    7.44 +
    7.45 +NS_OBJECT_ENSURE_REGISTERED (RealtimeSimulatorImpl);
    7.46 +
    7.47 +TypeId
    7.48 +RealtimeSimulatorImpl::GetTypeId (void)
    7.49 +{
    7.50 +  static TypeId tid = TypeId ("ns3::RealtimeSimulatorImpl")
    7.51 +    .SetParent<Object> ()
    7.52 +    .AddConstructor<RealtimeSimulatorImpl> ()
    7.53 +    .AddAttribute ("ReportSimulatedTime", 
    7.54 +                   "Report simulated time in Simulator::Now if true, otherwise wall-clock time (will be different).",
    7.55 +                   BooleanValue (true),
    7.56 +                   MakeBooleanAccessor (&RealtimeSimulatorImpl::m_reportSimulatedTime),
    7.57 +                   MakeBooleanChecker ())
    7.58 +    .AddAttribute ("SynchronizationMode", 
    7.59 +                   "What to do if the simulation cannot keep up with real time.",
    7.60 +                   EnumValue (SYNC_BEST_EFFORT),
    7.61 +                   MakeEnumAccessor (&RealtimeSimulatorImpl::SetSynchronizationMode),
    7.62 +                   MakeEnumChecker (SYNC_BEST_EFFORT, "BestEffort",
    7.63 +                                    SYNC_HARD_LIMIT, "HardLimit"))
    7.64 +    .AddAttribute ("HardLimit", 
    7.65 +                   "Maximum acceptable real-time jitter (used in conjunction with SynchronizationMode=HardLimit)",
    7.66 +                   TimeValue (Seconds (0.1)),
    7.67 +                   MakeTimeAccessor (&RealtimeSimulatorImpl::m_hardLimit),
    7.68 +                   MakeTimeChecker ())
    7.69 +    ;
    7.70 +  return tid;
    7.71 +}
    7.72 +
    7.73 +void
    7.74 +RealtimeEventLock::Lock (void)
    7.75 +{
    7.76 +  m_eventMutex.Lock ();
    7.77 +}
    7.78 +
    7.79 +void
    7.80 +RealtimeEventLock::Unlock (void)
    7.81 +{
    7.82 +  m_eventMutex.Unlock ();
    7.83 +}
    7.84 +
    7.85 +RealtimeSimulatorImpl::RealtimeSimulatorImpl ()
    7.86 +{
    7.87 +  NS_LOG_FUNCTION_NOARGS ();
    7.88 +
    7.89 +  EventImpl::SetEventLock (&m_eventLock);
    7.90 +
    7.91 +  m_stop = false;
    7.92 +  m_stopAt = 0;
    7.93 +  m_running = false;
    7.94 +  // uids are allocated from 4.
    7.95 +  // uid 0 is "invalid" events
    7.96 +  // uid 1 is "now" events
    7.97 +  // uid 2 is "destroy" events
    7.98 +  m_uid = 4; 
    7.99 +  // before ::Run is entered, the m_currentUid will be zero
   7.100 +  m_currentUid = 0;
   7.101 +  m_currentTs = 0;
   7.102 +  m_unscheduledEvents = 0;
   7.103 +
   7.104 +  // Be very careful not to do anything that would cause a change or assignment
   7.105 +  // of the underlying reference counts of m_synchronizer or you will be sorry.
   7.106 +  m_synchronizer = CreateObject<WallClockSynchronizer> ();
   7.107 +}
   7.108 +
   7.109 +RealtimeSimulatorImpl::~RealtimeSimulatorImpl ()
   7.110 +{
   7.111 +  NS_LOG_FUNCTION_NOARGS ();
   7.112 +  while (m_events->IsEmpty () == false)
   7.113 +    {
   7.114 +      EventId next = m_events->RemoveNext ();
   7.115 +    }
   7.116 +  m_events = 0;
   7.117 +  m_synchronizer = 0;
   7.118 +
   7.119 +  EventImpl::SetNoEventLock ();
   7.120 +}
   7.121 +
   7.122 +void
   7.123 +RealtimeSimulatorImpl::Destroy ()
   7.124 +{
   7.125 +  NS_LOG_FUNCTION_NOARGS ();
   7.126 +
   7.127 +  //
   7.128 +  // This function is only called with the private version "disconnected" from
   7.129 +  // the main simulator functions.  We rely on the user not calling 
   7.130 +  // Simulator::Destroy while there is a chance that a worker thread could be
   7.131 +  // accessing the current instance of the private object.  In practice this
   7.132 +  // means shutting down the workers and doing a Join() before calling the
   7.133 +  // Simulator::Destroy().
   7.134 +  //
   7.135 +  while (m_destroyEvents.empty () == false) 
   7.136 +    {
   7.137 +      Ptr<EventImpl> ev = m_destroyEvents.front ().PeekEventImpl ();
   7.138 +      m_destroyEvents.pop_front ();
   7.139 +      NS_LOG_LOGIC ("handle destroy " << ev);
   7.140 +      if (ev->IsCancelled () == false)
   7.141 +        {
   7.142 +          ev->Invoke ();
   7.143 +        }
   7.144 +    }
   7.145 +}
   7.146 +
   7.147 +void
   7.148 +RealtimeSimulatorImpl::SetScheduler (Ptr<Scheduler> scheduler)
   7.149 +{
   7.150 +  NS_LOG_FUNCTION_NOARGS ();
   7.151 +
   7.152 +  { 
   7.153 +    CriticalSection cs (m_mutex);
   7.154 +
   7.155 +    if (m_events != 0)
   7.156 +      {
   7.157 +        while (m_events->IsEmpty () == false)
   7.158 +          {
   7.159 +            EventId next = m_events->RemoveNext ();
   7.160 +            scheduler->Insert (next);
   7.161 +          }
   7.162 +      }
   7.163 +    m_events = scheduler;
   7.164 +  }
   7.165 +}
   7.166 +
   7.167 +Ptr<Scheduler>
   7.168 +RealtimeSimulatorImpl::GetScheduler (void) const
   7.169 +{
   7.170 +  NS_LOG_FUNCTION_NOARGS ();
   7.171 +  return m_events;
   7.172 +}
   7.173 +
   7.174 +void
   7.175 +RealtimeSimulatorImpl::ProcessOneEvent (void)
   7.176 +{
   7.177 +  NS_LOG_FUNCTION_NOARGS ();
   7.178 +  //
   7.179 +  // The idea here is to wait until the next event comes due.  In the case of
   7.180 +  // a simulation not locked to realtime, the next event is due immediately and
   7.181 +  // we jump time forward -- there is no real time consumed between events.  In
   7.182 +  // the case of a realtime simulation, we do want real time to be consumed 
   7.183 +  // between events.  It is the synchronizer that causes real time to be 
   7.184 +  // consumed.
   7.185 +  //
   7.186 +  // Now, there is a possibility that a wait down in the call to the synchronizer
   7.187 +  // will be interrupted.  In this case, we need to re-evaluate how long to wait 
   7.188 +  // until the next event since the interruption may have been caused by an
   7.189 +  // event inserted before the one that was on the head of the list when we 
   7.190 +  // started.
   7.191 +  //
   7.192 +  // m_synchronizer->Synchronize will return true if the wait was completed
   7.193 +  // without interruption, otherwise it will return false.  So our goal is to
   7.194 +  // sit in a for loop trying to synchronize until it returns true.  If this 
   7.195 +  // happens we will have successfully synchronized the execution time of the
   7.196 +  // next event with the wall clock time of the synchronizer.
   7.197 +  //
   7.198 +
   7.199 +  for (;;) 
   7.200 +    {
   7.201 +      uint64_t tsDelay = 0;
   7.202 +      uint64_t tsNow = m_currentTs;
   7.203 +      uint64_t tsNext = 0;
   7.204 +      //
   7.205 +      // NextTs peeks into the event list and returns the time stamp of the first 
   7.206 +      // event in the list.  We need to protect this kind of access with a critical
   7.207 +      // section.
   7.208 +      // 
   7.209 +      { 
   7.210 +        CriticalSection cs (m_mutex);
   7.211 +
   7.212 +        NS_ASSERT_MSG (NextTs () >= m_currentTs, 
   7.213 +          "RealtimeSimulatorImpl::ProcessOneEvent (): "
   7.214 +          "Next event time earlier than m_currentTs (list order error)");
   7.215 +        //
   7.216 +        // Since we are in realtime mode, the time to delay has got to be the 
   7.217 +        // difference between the current realtime and the timestamp of the next 
   7.218 +        // event.  Since m_currentTs is actually the timestamp of the last event we 
   7.219 +        // executed, it's not particularly meaningful for us here since real time has
   7.220 +        // since elapsed.  
   7.221 +        //
   7.222 +        // It is possible that the current realtime has drifted past the next event
   7.223 +        // time so we need to be careful about that and not delay in that case.
   7.224 +        //
   7.225 +        NS_ASSERT_MSG (m_synchronizer->Realtime (), 
   7.226 +          "RealtimeSimulatorImpl::ProcessOneEvent (): Synchronizer reports not Realtime ()");
   7.227 +
   7.228 +        tsNow = m_synchronizer->GetCurrentRealtime ();
   7.229 +        tsNext = NextTs ();
   7.230 +
   7.231 +        if (tsNext <= tsNow)
   7.232 +          {
   7.233 +            tsDelay = 0;
   7.234 +          }
   7.235 +        else
   7.236 +          {
   7.237 +            tsDelay = tsNext - tsNow;
   7.238 +          }
   7.239 +      
   7.240 +        m_synchronizer->SetCondition (false);
   7.241 +      }
   7.242 +
   7.243 +      //
   7.244 +      // So, we have a time to delay.  This time may actually not be valid anymore
   7.245 +      // since we released the critical section and a Schedule may have snuck in just
   7.246 +      // after the closing brace above.
   7.247 +      //
   7.248 +      // It's easiest to understand if you just consider a short tsDelay that only
   7.249 +      // requires a SpinWait down in the synchronizer.  What will happen is that 
   7.250 +      // whan Synchronize calls SpinWait, SpinWait will look directly at its 
   7.251 +      // condition variable.  Note that we set this condition variable to false 
   7.252 +      // inside the critical section above. 
   7.253 +      //
   7.254 +      // SpinWait will go into a forever loop until either the time has expired or
   7.255 +      // until the condition variable becomes true.  A true condition indicates that
   7.256 +      // the wait should stop.  The condition is set to true by one of the Schedule
   7.257 +      // methods of the simulator; so if we are in a wait down in Synchronize, and
   7.258 +      // a Simulator::Schedule is done, the wait down in Synchronize will exit and
   7.259 +      // Synchronize will return (false).
   7.260 +      //
   7.261 +      // So, we set this condition to false in a critical section.  The code that
   7.262 +      // sets the condition (Simulator::Schedule) also runs protected by the same
   7.263 +      // critical section mutex -- there is no race.  We call Synchronize -- if no
   7.264 +      // Simulator::Schedule is done, the waits (sleep- and spin-wait) will complete
   7.265 +      // and Synchronize will return true.  If a Schedule is done before we get to 
   7.266 +      // Synchronize, the Synchronize code will check the condition before going to
   7.267 +      // sleep.  If a Schedule happens while we are sleeping, we let the kernel wake
   7.268 +      // us up.
   7.269 +      //
   7.270 +      // So the bottom line is that we just stay in this for loop, looking for the
   7.271 +      // next timestamp and attempting to sleep until its due.  If we've slept until
   7.272 +      // the timestamp is due, Synchronize() returns true and we break out of the 
   7.273 +      //sync loop.  If we're interrupted we continue in the loop.  We may find that
   7.274 +      // the next timestamp is actually earlier than we thought, but we continue 
   7.275 +      // around the loop and reevaluate and wait for that one correctly.
   7.276 +      //
   7.277 +      if (m_synchronizer->Synchronize (tsNow, tsDelay))
   7.278 +        {
   7.279 +          NS_LOG_LOGIC ("Interrupted ...");
   7.280 +          break;
   7.281 +        }
   7.282 +    }
   7.283 +
   7.284 +  //
   7.285 +  // Okay, now more words.  We have slept without interruption until the 
   7.286 +  // timestamp we found at the head of the event list when we started the sleep.
   7.287 +  // We are now outside a critical section, so another schedule operation can
   7.288 +  // sneak in.  What does this mean?  The only thing that can "go wrong" is if
   7.289 +  // the new event was moved in ahead of the timestamp for which we waited.
   7.290 +  //
   7.291 +  // If you think about it, this is not a problem, since the best we can 
   7.292 +  // possibly do is to execute the event as soon as we can execute it.  We'll
   7.293 +  // be a little late, but we were late anyway.
   7.294 +  //
   7.295 +  // So we just go ahead and pull the first event off of the list, even though
   7.296 +  // it may be the case that it's not actually the one we waited for.
   7.297 +  //
   7.298 +  // One final tidbit is, "what the heck time is it anyway"?  The simulator is
   7.299 +  // going to want to get a timestamp from the next event and wait until the
   7.300 +  // wall clock time is equal to the timestamp.  At the point when those times
   7.301 +  // are the same (roughly) we get to this point and set the m_currentTs below.
   7.302 +  // That's straightforward here, but you might ask, how does the next event get
   7.303 +  // the time when it is inserted from an external source?
   7.304 +  //
   7.305 +  // The method RealtimeSimulatorImpl::ScheduleNow takes makes an event and 
   7.306 +  // needs to schedule that event for immediate execution.  If the simulator is 
   7.307 +  // not locked to a realtime source, the current time is m_currentTs.  If it is
   7.308 +  // locked to a realtime source, we need to use the real current real time.
   7.309 +  // This real time is then used to set the event execution time and will be 
   7.310 +  // read by the next.GetTs below and will set the current simulation time.
   7.311 +  //
   7.312 +  EventId next;
   7.313 +
   7.314 +  { 
   7.315 +    CriticalSection cs (m_mutex);
   7.316 +
   7.317 +    NS_ASSERT_MSG (m_events->IsEmpty () == false, 
   7.318 +      "RealtimeSimulatorImpl::ProcessOneEvent(): event queue is empty");
   7.319 +
   7.320 +    next = m_events->RemoveNext ();
   7.321 +    --m_unscheduledEvents;
   7.322 +  }
   7.323 +
   7.324 +  NS_ASSERT_MSG (next.GetTs () >= m_currentTs,
   7.325 +    "RealtimeSimulatorImpl::ProcessOneEvent(): "
   7.326 +    "next.GetTs() earlier than m_currentTs (list order error)");
   7.327 +  NS_LOG_LOGIC ("handle " << next.GetTs ());
   7.328 +  m_currentTs = next.GetTs ();
   7.329 +  m_currentUid = next.GetUid ();
   7.330 +
   7.331 +  // 
   7.332 +  // We're about to run the event and we've done our best to synchronize this
   7.333 +  // event execution time to real time.  Now, if we're in SYNC_HARD_LIMIT mode
   7.334 +  // we have to decide if we've done a good enough job and if we haven't, we've
   7.335 +  // been asked to commit ritual suicide.
   7.336 +  //
   7.337 +  if (m_synchronizationMode == SYNC_HARD_LIMIT)
   7.338 +    {
   7.339 +      uint64_t tsFinal = m_synchronizer->GetCurrentRealtime ();
   7.340 +      uint64_t tsJitter;
   7.341 +
   7.342 +      if (tsFinal >= m_currentTs)
   7.343 +        {
   7.344 +          tsJitter = tsFinal - m_currentTs;
   7.345 +        }
   7.346 +      else
   7.347 +        {
   7.348 +          tsJitter = m_currentTs - tsFinal;
   7.349 +        }
   7.350 +
   7.351 +      if (tsJitter > static_cast<uint64_t>(m_hardLimit.GetTimeStep ()))
   7.352 +        {
   7.353 +          NS_FATAL_ERROR ("RealtimeSimulatorImpl::ProcessOneEvent (): "
   7.354 +                          "Hard real-time limit exceeded (jitter = " << tsJitter << ")");
   7.355 +        }
   7.356 +    }
   7.357 +
   7.358 +  EventImpl *event = next.PeekEventImpl ();
   7.359 +  m_synchronizer->EventStart ();
   7.360 +  event->Invoke ();
   7.361 +  m_synchronizer->EventEnd ();
   7.362 +}
   7.363 +
   7.364 +bool 
   7.365 +RealtimeSimulatorImpl::IsFinished (void) const
   7.366 +{
   7.367 +  NS_LOG_FUNCTION_NOARGS ();
   7.368 +  bool rc;
   7.369 +  {
   7.370 +    CriticalSection cs (m_mutex);
   7.371 +    rc = m_events->IsEmpty ();
   7.372 +  }
   7.373 +
   7.374 +  return rc;
   7.375 +}
   7.376 +
   7.377 +//
   7.378 +// Peeks into event list.  Should be called with critical section locked.
   7.379 +//
   7.380 +uint64_t
   7.381 +RealtimeSimulatorImpl::NextTs (void) const
   7.382 +{
   7.383 +  NS_LOG_FUNCTION_NOARGS ();
   7.384 +  NS_ASSERT_MSG (m_events->IsEmpty () == false, 
   7.385 +    "RealtimeSimulatorImpl::NextTs(): event queue is empty");
   7.386 +  EventId id = m_events->PeekNext ();
   7.387 +
   7.388 +  return id.GetTs ();
   7.389 +}
   7.390 +
   7.391 +//
   7.392 +// Calls NextTs().  Should be called with critical section locked.
   7.393 +//
   7.394 +Time
   7.395 +RealtimeSimulatorImpl::Next (void) const
   7.396 +{
   7.397 +  NS_LOG_FUNCTION_NOARGS ();
   7.398 +  return TimeStep (NextTs ());
   7.399 +}
   7.400 +
   7.401 +void
   7.402 +RealtimeSimulatorImpl::Run (void)
   7.403 +{
   7.404 +  NS_LOG_FUNCTION_NOARGS ();
   7.405 +  m_running = true;
   7.406 +  NS_ASSERT_MSG (m_currentTs == 0,
   7.407 +    "RealtimeSimulatorImpl::Run(): The beginning of time is not zero");
   7.408 +  m_synchronizer->SetOrigin (m_currentTs);
   7.409 +
   7.410 +  for (;;) 
   7.411 +    {
   7.412 +      bool done = false;
   7.413 +
   7.414 +      {
   7.415 +        CriticalSection cs (m_mutex);
   7.416 +        //
   7.417 +        // In all cases we stop when the event list is empty.  If you are doing a 
   7.418 +        // realtime simulation and you want it to extend out for some time, you must
   7.419 +        // call StopAt.  In the realtime case, this will stick a placeholder event out
   7.420 +        // at the end of time.
   7.421 +        //
   7.422 +        if (m_stop || m_events->IsEmpty ())
   7.423 +          {
   7.424 +            done = true;
   7.425 +          }
   7.426 +        //
   7.427 +        // We also want to stop the simulator at some time even if there are events 
   7.428 +        // that have been scheduled out in the future.  If we're in realtime mode, we 
   7.429 +        // actually have time passing, so we must look at the realtime clock to see if 
   7.430 +        // we're past the end time.
   7.431 +        //
   7.432 +        if (m_stopAt && m_stopAt <= m_synchronizer->GetCurrentRealtime ())
   7.433 +          {
   7.434 +            done = true;
   7.435 +          }
   7.436 +      }
   7.437 +
   7.438 +      if (done)
   7.439 +        {
   7.440 +          break;
   7.441 +        }
   7.442 +
   7.443 +      ProcessOneEvent ();
   7.444 +    }
   7.445 +
   7.446 +  //
   7.447 +  // If the simulator stopped naturally by lack of events, make a
   7.448 +  // consistency test to check that we didn't lose any events along the way.
   7.449 +  //
   7.450 +  {
   7.451 +    CriticalSection cs (m_mutex);
   7.452 +
   7.453 +    NS_ASSERT_MSG (m_events->IsEmpty () == false || m_unscheduledEvents == 0,
   7.454 +      "RealtimeSimulatorImpl::Run(): Empty queue and unprocessed events");
   7.455 +  }
   7.456 +
   7.457 +  m_running = false;
   7.458 +}
   7.459 +
   7.460 +bool
   7.461 +RealtimeSimulatorImpl::Running (void) const
   7.462 +{
   7.463 +  NS_LOG_FUNCTION_NOARGS ();
   7.464 +  return m_running;
   7.465 +}
   7.466 +
   7.467 +bool
   7.468 +RealtimeSimulatorImpl::Realtime (void) const
   7.469 +{
   7.470 +  NS_LOG_FUNCTION_NOARGS ();
   7.471 +  return m_synchronizer->Realtime ();
   7.472 +}
   7.473 +
   7.474 +void
   7.475 +RealtimeSimulatorImpl::RunOneEvent (void)
   7.476 +{
   7.477 +  NS_LOG_FUNCTION_NOARGS ();
   7.478 +  NS_FATAL_ERROR ("DefaultSimulatorImpl::RunOneEvent(): Not allowed in realtime simulations");
   7.479 +}
   7.480 +
   7.481 +void 
   7.482 +RealtimeSimulatorImpl::Stop (void)
   7.483 +{
   7.484 +  NS_LOG_FUNCTION_NOARGS ();
   7.485 +  m_stop = true;
   7.486 +}
   7.487 +
   7.488 +static void Placeholder (void) {}
   7.489 +
   7.490 +void 
   7.491 +RealtimeSimulatorImpl::Stop (Time const &time)
   7.492 +{
   7.493 +  NS_LOG_FUNCTION (time);
   7.494 +  NS_ASSERT_MSG (time.IsPositive (),
   7.495 +    "RealtimeSimulatorImpl::Stop(): Negative time");
   7.496 +
   7.497 +  Time absolute = Simulator::Now () + time;
   7.498 +  m_stopAt = absolute.GetTimeStep ();
   7.499 +  //
   7.500 +  // For the realtime case, we need a real event sitting out at the end of time
   7.501 +  // to keep the simulator running (sleeping) while there are no other events 
   7.502 +  // present.  If an "external" device in another thread decides to schedule an
   7.503 +  // event, the sleeping synchronizer will be awakened and the new event will
   7.504 +  // be run.
   7.505 +  //
   7.506 +  // The easiest thing to do is to call back up into the simulator to take 
   7.507 +  // advantage of all of the nice event wrappers.  This will call back down into
   7.508 +  // RealtimeSimulatorImpl::Schedule to do the work.
   7.509 +  //
   7.510 +  Simulator::Schedule (absolute, &Placeholder);
   7.511 +}
   7.512 +
   7.513 +EventId
   7.514 +RealtimeSimulatorImpl::Schedule (Time const &time, const Ptr<EventImpl> &event)
   7.515 +{
   7.516 +  NS_LOG_FUNCTION (time << event);
   7.517 +
   7.518 +  //
   7.519 +  // This is a little tricky.  We get a Ptr<EventImpl> passed down to us in some
   7.520 +  // thread context.  This Ptr<EventImpl> is not yet shared in any way.  It is 
   7.521 +  // possible however that the calling context is not the context of the main 
   7.522 +  // scheduler thread (eg. it is in the context of a separate device thread).  
   7.523 +  // It would be bad (TM) if we naively wrapped the EventImpl up in an EventId 
   7.524 +  // that would be accessible from multiple threads without considering thread 
   7.525 +  // safety.
   7.526 +  //
   7.527 +  // Having multiple EventId containing Ptr<EventImpl> in different thread
   7.528 +  // contexts creates a situation where atomic reference count decrements
   7.529 +  // would be required (think about an event being scheduled with the calling
   7.530 +  // yielding immediately afterward.  The scheduler could become ready and 
   7.531 +  // fire the event which would eventually want to release the EventImpl.  If
   7.532 +  // the calling thread were readied and executed in mid-release, it would also
   7.533 +  // want to release the EventImpl "at the same time."  If we are careless about
   7.534 +  // this, multiple deletes are sure to eventually happen as the threads 
   7.535 +  // separately play with the EventImpl reference counts.
   7.536 +  // 
   7.537 +  // The way this all works is that we created an EventImpl in the template 
   7.538 +  // function that called us, which may be in the context of a thread separate
   7.539 +  // from the simulator thread.  We are managing the lifetime of the EventImpl
   7.540 +  // with an intrusive reference count.  The count was initialized to one when
   7.541 +  // it was created and is still one right now.  We're going to "wrap" this
   7.542 +  // EventImpl in an EventId in this method.  There's an assignment of our 
   7.543 +  // Ptr<EventImpl> into another Ptr<EventImpl> inside the EventId which will 
   7.544 +  // bump the ref count to two.  We're going to return this EventId to the 
   7.545 +  // caller so the calling thread will hold a reference to the underlying 
   7.546 +  // EventImpl.  This is what causes the first half of the multithreading issue.
   7.547 +  //
   7.548 +  // We are going to call Insert() on the EventId to put the event into the 
   7.549 +  // scheduler.  Down there, it will do a PeekEventImpl to get the pointer to
   7.550 +  // the EventImpl and manually increment the reference count (to three).  From 
   7.551 +  // there, the sheduler works with the EventImpl pointer.  When the EventId 
   7.552 +  // below goes out of scope, the Ptr<EventImpl> destructor is run and the ref
   7.553 +  // count is decremented to two.  When this function returns to the calling
   7.554 +  // template function, the Ptr<EventImpl> there will go out of scope and we'll
   7.555 +  // decrement the EventImpl ref count leaving it to be the one held by our
   7.556 +  // scheduler directly.
   7.557 +  //
   7.558 +  // The scheduler removes the EventImpl in Remove() or RemoveNext().  In the
   7.559 +  // case of Remove(), the scheduler is provided an Event reference and locates
   7.560 +  // the corresponding EventImpl in the event list.  It gets the raw pointer to
   7.561 +  // the EventImpl, erases the pointer in the list, and manually calls Unref()
   7.562 +  // on the pointer.  In RemoveNext(), it gets the raw pointer from the list and
   7.563 +  // assigns it to a Ptr<EventImpl> without bumping the reference count, thereby
   7.564 +  // transferring ownership to a containing EventId.  This is the second half of
   7.565 +  // the multithreading issue.
   7.566 +  //
   7.567 +  // It's clear that we cannot have a situation where the EventImpl is "owned" by
   7.568 +  // multiple threads.  The calling thread is free to hold the EventId as long as
   7.569 +  // it wants and manage the reference counts to the underlying EventImpl all it
   7.570 +  // wants.  The scheduler is free to do the same; and will eventually release
   7.571 +  // the reference in the context of thread running ProcessOneEvent().  It is 
   7.572 +  // "a bad thing" (TM) if these two threads decide to release the underlying
   7.573 +  // EventImpl "at the same time."
   7.574 +  //
   7.575 +  // The answer is to make the reference counting of the EventImpl thread safe; 
   7.576 +  // which we do.  We don't want to force the event implementation to carry around
   7.577 +  // a mutex, so we "lend" it one using a RealtimeEventLock object (m_eventLock)
   7.578 +  // in the constructor and take it back in the destructor.
   7.579 +  //
   7.580 +  EventId id;
   7.581 +
   7.582 +  {
   7.583 +    CriticalSection cs (m_mutex);
   7.584 +
   7.585 +    NS_ASSERT_MSG (time.IsPositive (),
   7.586 +      "RealtimeSimulatorImpl::Schedule(): Negative time");
   7.587 +    NS_ASSERT_MSG (
   7.588 +      time >= TimeStep (m_currentTs),
   7.589 +      "RealtimeSimulatorImpl::Schedule(): time < m_currentTs");
   7.590 +
   7.591 +    uint64_t ts = (uint64_t) time.GetTimeStep ();
   7.592 +
   7.593 +    id = EventId (event, ts, m_uid);
   7.594 +    m_uid++;
   7.595 +    ++m_unscheduledEvents;
   7.596 +    m_events->Insert (id);
   7.597 +    m_synchronizer->Signal ();
   7.598 +  }
   7.599 +
   7.600 +  return id;
   7.601 +}
   7.602 +
   7.603 +EventId
   7.604 +RealtimeSimulatorImpl::ScheduleNow (const Ptr<EventImpl> &event)
   7.605 +{
   7.606 +  NS_LOG_FUNCTION_NOARGS ();
   7.607 +  EventId id;
   7.608 +  {
   7.609 +    CriticalSection cs (m_mutex);
   7.610 +
   7.611 +    id = EventId (event, m_synchronizer->GetCurrentRealtime (), m_uid);
   7.612 +    
   7.613 +    m_uid++;
   7.614 +    ++m_unscheduledEvents;
   7.615 +    m_events->Insert (id);
   7.616 +    m_synchronizer->Signal ();
   7.617 +  }
   7.618 +
   7.619 +  return id;
   7.620 +}
   7.621 +
   7.622 +EventId
   7.623 +RealtimeSimulatorImpl::ScheduleDestroy (const Ptr<EventImpl> &event)
   7.624 +{
   7.625 +  NS_LOG_FUNCTION_NOARGS ();
   7.626 +  EventId id;
   7.627 +
   7.628 +  {
   7.629 +    CriticalSection cs (m_mutex);
   7.630 +
   7.631 +    //
   7.632 +    // Time doesn't really matter here (especially in realtime mode).  It is 
   7.633 +    // overridden by the uid of 2 which identifies this as an event to be 
   7.634 +    // executed at Simulator::Destroy time.
   7.635 +    //
   7.636 +    id = EventId (event, m_currentTs, 2);
   7.637 +    m_destroyEvents.push_back (id);
   7.638 +    m_uid++;
   7.639 +  }
   7.640 +
   7.641 +  return id;
   7.642 +}
   7.643 +
   7.644 +Time
   7.645 +RealtimeSimulatorImpl::Now (void) const
   7.646 +{
   7.647 +  //
   7.648 +  // The behavior of Now depends on the setting of the "ReportSimulatedTime"
   7.649 +  // attribute.  If this is set to true, then Now will pretend that the realtime
   7.650 +  // synchronization is perfect and that event execution consumes no time.  This
   7.651 +  // allows models written with this kind of assumption to work as they did in 
   7.652 +  // non-real-time cases.  However, if the attribute is set to false, we're going
   7.653 +  // to give the caller the bad news right in the face.  If it sets an event to 
   7.654 +  // be executed at 10.0000000 seconds, and calls Simulator::Now it will get the 
   7.655 +  // realtime clock value which almost certainly will *not* be ten seconds.  We'll
   7.656 +  // get as close as possible, but we won't be perfect and we won't pretend to be.
   7.657 +  // Also, if the client calls Simulator::Now multiple times in an event, she will
   7.658 +  // get different times as the underlying realtime clock will have moved.  However
   7.659 +  // if the simulation is not actually running, we'll use the last event timestamp
   7.660 +  // as the time, which will be a precise simulation time (0 sec before the sim
   7.661 +  // starts, or the end time after the simulation ends).
   7.662 +  //
   7.663 +  if ((m_reportSimulatedTime == false) && Running ())
   7.664 +    {
   7.665 +      return TimeStep (m_synchronizer->GetCurrentRealtime ());
   7.666 +    }
   7.667 +  else
   7.668 +    {
   7.669 +      return TimeStep (m_currentTs);
   7.670 +    }
   7.671 +}
   7.672 +
   7.673 +Time 
   7.674 +RealtimeSimulatorImpl::GetDelayLeft (const EventId &id) const
   7.675 +{
   7.676 +  if (IsExpired (id))
   7.677 +    {
   7.678 +      return TimeStep (0);
   7.679 +    }
   7.680 +  else
   7.681 +    {
   7.682 +      return TimeStep (id.GetTs () - m_synchronizer->GetCurrentRealtime ());
   7.683 +    }
   7.684 +}
   7.685 +
   7.686 +void
   7.687 +RealtimeSimulatorImpl::Remove (const EventId &ev)
   7.688 +{
   7.689 +  if (ev.GetUid () == 2)
   7.690 +    {
   7.691 +      // destroy events.
   7.692 +      for (DestroyEvents::iterator i = m_destroyEvents.begin (); 
   7.693 +           i != m_destroyEvents.end (); 
   7.694 +           i++)
   7.695 +        {
   7.696 +          if (*i == ev)
   7.697 +            {
   7.698 +              m_destroyEvents.erase (i);
   7.699 +              break;
   7.700 +            }
   7.701 +         }
   7.702 +      return;
   7.703 +    }
   7.704 +  if (IsExpired (ev))
   7.705 +    {
   7.706 +      return;
   7.707 +    }
   7.708 +
   7.709 +  {
   7.710 +    CriticalSection cs (m_mutex);
   7.711 +
   7.712 +    m_events->Remove (ev);
   7.713 +    --m_unscheduledEvents;
   7.714 +
   7.715 +    Cancel (ev);
   7.716 +
   7.717 +  }
   7.718 +}
   7.719 +
   7.720 +void
   7.721 +RealtimeSimulatorImpl::Cancel (const EventId &id)
   7.722 +{
   7.723 +  if (IsExpired (id) == false)
   7.724 +    {
   7.725 +      id.PeekEventImpl ()->Cancel ();
   7.726 +    }
   7.727 +}
   7.728 +
   7.729 +bool
   7.730 +RealtimeSimulatorImpl::IsExpired (const EventId &ev) const
   7.731 +{
   7.732 +  if (ev.GetUid () == 2)
   7.733 +    {
   7.734 +      // destroy events.
   7.735 +      for (DestroyEvents::const_iterator i = m_destroyEvents.begin (); 
   7.736 +           i != m_destroyEvents.end (); i++)
   7.737 +        {
   7.738 +          if (*i == ev)
   7.739 +            {
   7.740 +              return false;
   7.741 +            }
   7.742 +         }
   7.743 +      return true;
   7.744 +    }
   7.745 +  if (ev.PeekEventImpl () == 0 ||
   7.746 +      ev.GetTs () < m_currentTs ||
   7.747 +      (ev.GetTs () == m_currentTs &&
   7.748 +       ev.GetUid () <= m_currentUid) ||
   7.749 +      ev.PeekEventImpl ()->IsCancelled ()) 
   7.750 +    {
   7.751 +      return true;
   7.752 +    }
   7.753 +  else
   7.754 +    {
   7.755 +      return false;
   7.756 +    }
   7.757 +}
   7.758 +
   7.759 +Time 
   7.760 +RealtimeSimulatorImpl::GetMaximumSimulationTime (void) const
   7.761 +{
   7.762 +  // XXX: I am fairly certain other compilers use other non-standard
   7.763 +  // post-fixes to indicate 64 bit constants.
   7.764 +  return TimeStep (0x7fffffffffffffffLL);
   7.765 +}
   7.766 +
   7.767 +void 
   7.768 +RealtimeSimulatorImpl::SetSynchronizationMode (enum SynchronizationMode mode)
   7.769 +{
   7.770 +  NS_LOG_FUNCTION (mode);
   7.771 +  m_synchronizationMode = mode;
   7.772 +}
   7.773 +
   7.774 +RealtimeSimulatorImpl::SynchronizationMode
   7.775 +RealtimeSimulatorImpl::GetSynchronizationMode (void) const
   7.776 +{
   7.777 +  NS_LOG_FUNCTION_NOARGS ();
   7.778 +  return m_synchronizationMode;
   7.779 +}
   7.780 +  
   7.781 +void 
   7.782 +RealtimeSimulatorImpl::SetHardLimit (Time limit)
   7.783 +{
   7.784 +  NS_LOG_FUNCTION (limit);
   7.785 +  m_hardLimit = limit;
   7.786 +}
   7.787 +
   7.788 +Time
   7.789 +RealtimeSimulatorImpl::GetHardLimit (void) const
   7.790 +{
   7.791 +  NS_LOG_FUNCTION_NOARGS ();
   7.792 +  return m_hardLimit;
   7.793 +}
   7.794 +  
   7.795 +}; // namespace ns3
   7.796 +
   7.797 +
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/simulator/realtime-simulator-impl.h	Tue Aug 26 15:34:57 2008 -0700
     8.3 @@ -0,0 +1,153 @@
     8.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
     8.5 +/*
     8.6 + * Copyright (c) 2008 University of Washington
     8.7 + *
     8.8 + * This program is free software; you can redistribute it and/or modify
     8.9 + * it under the terms of the GNU General Public License version 2 as
    8.10 + * published by the Free Software Foundation;
    8.11 + *
    8.12 + * This program is distributed in the hope that it will be useful,
    8.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.15 + * GNU General Public License for more details.
    8.16 + *
    8.17 + * You should have received a copy of the GNU General Public License
    8.18 + * along with this program; if not, write to the Free Software
    8.19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    8.20 + */
    8.21 +
    8.22 +#ifndef REALTIME_SIMULATOR_IMPL_H
    8.23 +#define REALTIME_SIMULATOR_IMPL_H
    8.24 +
    8.25 +#include "simulator-impl.h"
    8.26 +
    8.27 +#include "scheduler.h"
    8.28 +#include "synchronizer.h"
    8.29 +#include "event-impl.h"
    8.30 +
    8.31 +#include "ns3/ptr.h"
    8.32 +#include "ns3/assert.h"
    8.33 +#include "ns3/log.h"
    8.34 +
    8.35 +#include <list>
    8.36 +#include <fstream>
    8.37 +
    8.38 +namespace ns3 {
    8.39 +
    8.40 +class RealtimeEventLock : public EventLock
    8.41 +{
    8.42 +public:
    8.43 +  void Lock (void);
    8.44 +  void Unlock (void);
    8.45 +private:
    8.46 +  SystemMutex m_eventMutex;
    8.47 +};
    8.48 +
    8.49 +class RealtimeSimulatorImpl : public SimulatorImpl
    8.50 +{
    8.51 +public:
    8.52 +  static TypeId GetTypeId (void);
    8.53 +
    8.54 +  /**
    8.55 +   * Enumeration of the types of packets supported in the class.
    8.56 +   *
    8.57 +   */
    8.58 +  enum SynchronizationMode {
    8.59 +    SYNC_BEST_EFFORT, /** Make a best effort to keep synced to real-time */
    8.60 +    SYNC_HARD_LIMIT, /** Keep to real-time within a tolerance or die trying */
    8.61 +  };
    8.62 +
    8.63 +  RealtimeSimulatorImpl ();
    8.64 +  ~RealtimeSimulatorImpl ();
    8.65 +
    8.66 +  void Destroy ();
    8.67 +
    8.68 +  void EnableLogTo (char const *filename);
    8.69 +
    8.70 +  bool IsFinished (void) const;
    8.71 +  Time Next (void) const;
    8.72 +  void Stop (void);
    8.73 +  void Stop (Time const &time);
    8.74 +  EventId Schedule (Time const &time, const Ptr<EventImpl> &event);
    8.75 +  EventId ScheduleNow (const Ptr<EventImpl> &event);
    8.76 +  EventId ScheduleDestroy (const Ptr<EventImpl> &event);
    8.77 +  void Remove (const EventId &ev);
    8.78 +  void Cancel (const EventId &ev);
    8.79 +  bool IsExpired (const EventId &ev) const;
    8.80 +  virtual void RunOneEvent (void);
    8.81 +  void Run (void);
    8.82 +  Time Now (void) const;
    8.83 +  Time GetDelayLeft (const EventId &id) const;
    8.84 +  Time GetMaximumSimulationTime (void) const;
    8.85 +
    8.86 +  void SetScheduler (Ptr<Scheduler> scheduler);
    8.87 +  Ptr<Scheduler> GetScheduler (void) const;
    8.88 +
    8.89 +  void SetSynchronizationMode (RealtimeSimulatorImpl::SynchronizationMode mode);
    8.90 +  RealtimeSimulatorImpl::SynchronizationMode GetSynchronizationMode (void) const;
    8.91 +
    8.92 +  void SetHardLimit (Time limit);
    8.93 +  Time GetHardLimit (void) const;
    8.94 +
    8.95 +private:
    8.96 +  bool Running (void) const;
    8.97 +  bool Realtime (void) const;
    8.98 +
    8.99 +  void ProcessOneEvent (void);
   8.100 +  uint64_t NextTs (void) const;
   8.101 +
   8.102 +  typedef std::list<EventId> DestroyEvents;
   8.103 +  DestroyEvents m_destroyEvents;
   8.104 +  uint64_t m_stopAt;
   8.105 +  bool m_stop;
   8.106 +  bool m_running;
   8.107 +
   8.108 +  // The following variables are protected using the m_mutex
   8.109 +  Ptr<Scheduler> m_events;
   8.110 +  int m_unscheduledEvents;
   8.111 +  uint32_t m_uid;
   8.112 +  uint32_t m_currentUid;
   8.113 +  uint64_t m_currentTs;
   8.114 +
   8.115 +  mutable SystemMutex m_mutex;
   8.116 +  RealtimeEventLock m_eventLock;
   8.117 +
   8.118 +  Ptr<Synchronizer> m_synchronizer;
   8.119 +  /*
   8.120 +   * In calls to Simulator::Now we have a basic choice to make.  We can either
   8.121 +   * report back the time the simulator thinks it should be, or we can report 
   8.122 +   * the time it actually is.
   8.123 +   *
   8.124 +   * The synchronizer will make an attempt to cause these two numbers to be as
   8.125 +   * close as possible to each other, but they will never be exactly the same.
   8.126 +   * We give the client a choice in this respect.  
   8.127 +   *
   8.128 +   * If the client sets m_reportSimulatedTime to true, the behavior will be that 
   8.129 +   * the simulator runs as close as possible to real time, but reports back to the
   8.130 +   * client that it is running at exactly real time, and consuming no real time 
   8.131 +   * as each event executes.  This allows for deterministic execution times and
   8.132 +   * repeatable trace files.
   8.133 +   *
   8.134 +   * If the client sets m_reportSimulatedTime to false, the behavior will be that 
   8.135 +   * the simulator runs as close as possible to real time, and reports back to the
   8.136 +   * client the real wall-clock time whenever it asks.  Real time will be consumed
   8.137 +   * as each event executes.  This allows for non-deterministic execution times and
   8.138 +   * real variations in event executions.  Simulation time will be influenced by
   8.139 +   * variations in host process scheduling, for example.
   8.140 +   */
   8.141 +  bool m_reportSimulatedTime;
   8.142 +
   8.143 +  /**
   8.144 +   * The policy to use if the simulation cannot keep synchronized to real-time.
   8.145 +   */
   8.146 +  SynchronizationMode m_synchronizationMode;
   8.147 +
   8.148 +  /**
   8.149 +   * The maximum allowable drift from real-time in SYNC_HARD_LIMIT mode.
   8.150 +   */
   8.151 +  Time m_hardLimit;
   8.152 +};
   8.153 +
   8.154 +} // namespace ns3
   8.155 +
   8.156 +#endif /* REALTIME_SIMULATOR_IMPL_H */
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/simulator/synchronizer.cc	Tue Aug 26 15:34:57 2008 -0700
     9.3 @@ -0,0 +1,158 @@
     9.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
     9.5 +/*
     9.6 + * Copyright (c) 2008 University of Washington
     9.7 + *
     9.8 + * This program is free software; you can redistribute it and/or modify
     9.9 + * it under the terms of the GNU General Public License version 2 as
    9.10 + * published by the Free Software Foundation;
    9.11 + *
    9.12 + * This program is distributed in the hope that it will be useful,
    9.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    9.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    9.15 + * GNU General Public License for more details.
    9.16 + *
    9.17 + * You should have received a copy of the GNU General Public License
    9.18 + * along with this program; if not, write to the Free Software
    9.19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    9.20 + */
    9.21 +
    9.22 +#include "synchronizer.h"
    9.23 +
    9.24 +namespace ns3 {
    9.25 +
    9.26 +TypeId 
    9.27 +Synchronizer::GetTypeId (void)
    9.28 +{
    9.29 +  static TypeId tid = TypeId ("Synchronizer")
    9.30 +    .SetParent<Object> ()
    9.31 +    ;
    9.32 +  return tid;
    9.33 +}
    9.34 +
    9.35 +Synchronizer::Synchronizer ()
    9.36 +  : m_realtimeOriginNano (0), m_simOriginNano (0)
    9.37 +{
    9.38 +}
    9.39 +
    9.40 +Synchronizer::~Synchronizer ()
    9.41 +{
    9.42 +}
    9.43 +
    9.44 +  bool
    9.45 +Synchronizer::Realtime (void)
    9.46 +{
    9.47 +  return DoRealtime ();
    9.48 +}
    9.49 +
    9.50 +  uint64_t
    9.51 +Synchronizer::GetCurrentRealtime (void)
    9.52 +{
    9.53 +  return NanosecondToTimeStep (DoGetCurrentRealtime ());
    9.54 +}
    9.55 +
    9.56 +  void
    9.57 +Synchronizer::SetOrigin (uint64_t ts)
    9.58 +{
    9.59 +  m_simOriginNano = TimeStepToNanosecond (ts);
    9.60 +  DoSetOrigin (m_simOriginNano);
    9.61 +}
    9.62 +
    9.63 +  uint64_t
    9.64 +Synchronizer::GetOrigin (void)
    9.65 +{
    9.66 +  return NanosecondToTimeStep (m_simOriginNano);
    9.67 +}
    9.68 +
    9.69 +  int64_t
    9.70 +Synchronizer::GetDrift (uint64_t ts)
    9.71 +{
    9.72 +  int64_t tDrift = DoGetDrift (TimeStepToNanosecond (ts));
    9.73 +
    9.74 +  if (tDrift < 0) 
    9.75 +    {
    9.76 +      return -NanosecondToTimeStep (-tDrift);
    9.77 +    } else {
    9.78 +      return NanosecondToTimeStep (tDrift);
    9.79 +  }
    9.80 +}
    9.81 +
    9.82 +  bool
    9.83 +Synchronizer::Synchronize (uint64_t tsCurrent, uint64_t tsDelay)
    9.84 +{
    9.85 +  return DoSynchronize (TimeStepToNanosecond (tsCurrent), 
    9.86 +    TimeStepToNanosecond (tsDelay));
    9.87 +}
    9.88 +
    9.89 +  void
    9.90 +Synchronizer::Signal (void)
    9.91 +{
    9.92 +  DoSignal ();
    9.93 +}
    9.94 +
    9.95 +  void
    9.96 +Synchronizer::SetCondition (bool cond)
    9.97 +{
    9.98 +  DoSetCondition (cond);
    9.99 +}
   9.100 +
   9.101 +  void
   9.102 +Synchronizer::EventStart (void)
   9.103 +{
   9.104 +  DoEventStart ();
   9.105 +}
   9.106 +
   9.107 +  uint64_t
   9.108 +Synchronizer::EventEnd (void)
   9.109 +{
   9.110 +  return NanosecondToTimeStep (DoEventEnd ());
   9.111 +}
   9.112 +
   9.113 +  uint64_t
   9.114 +Synchronizer::TimeStepToNanosecond (uint64_t ts)
   9.115 +{
   9.116 +  switch (TimeStepPrecision::Get ()) {
   9.117 +  case TimeStepPrecision::S:
   9.118 +    return ts * 1000000000;
   9.119 +  case TimeStepPrecision::MS:
   9.120 +    return ts * 1000000;
   9.121 +  case TimeStepPrecision::US:
   9.122 +    return ts * 1000;
   9.123 +  case TimeStepPrecision::NS:
   9.124 +    return ts;
   9.125 +  case TimeStepPrecision::PS:
   9.126 +    return ts / 1000;
   9.127 +  case TimeStepPrecision::FS:
   9.128 +    return ts / 1000000;
   9.129 +  default:
   9.130 +    NS_ASSERT_MSG (false, "Synchronizer::TimeStepToNanosecond: "
   9.131 +        "Unexpected precision not implemented");
   9.132 +    return 0;
   9.133 +  }
   9.134 +}
   9.135 +
   9.136 +  uint64_t
   9.137 +Synchronizer::NanosecondToTimeStep (uint64_t ns)
   9.138 +{
   9.139 +  switch (TimeStepPrecision::Get ()) {
   9.140 +  case TimeStepPrecision::S:
   9.141 +    return ns / 1000000000;
   9.142 +  case TimeStepPrecision::MS:
   9.143 +    return ns / 1000000;
   9.144 +  case TimeStepPrecision::US:
   9.145 +    return ns / 1000;
   9.146 +  case TimeStepPrecision::NS:
   9.147 +    return ns;
   9.148 +  case TimeStepPrecision::PS:
   9.149 +    return ns * 1000;
   9.150 +  case TimeStepPrecision::FS:
   9.151 +    return ns * 1000000;
   9.152 +  default:
   9.153 +    NS_ASSERT_MSG (false, "Synchronizer::NanosecondToTimeStep: "
   9.154 +        "Unexpected precision not implemented");
   9.155 +    return 0;
   9.156 +  }
   9.157 +}
   9.158 +
   9.159 +}; // namespace ns3
   9.160 +
   9.161 +
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/src/simulator/synchronizer.h	Tue Aug 26 15:34:57 2008 -0700
    10.3 @@ -0,0 +1,334 @@
    10.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
    10.5 +/*
    10.6 + * Copyright (c) 2008 University of Washington
    10.7 + *
    10.8 + * This program is free software; you can redistribute it and/or modify
    10.9 + * it under the terms of the GNU General Public License version 2 as
   10.10 + * published by the Free Software Foundation;
   10.11 + *
   10.12 + * This program is distributed in the hope that it will be useful,
   10.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   10.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10.15 + * GNU General Public License for more details.
   10.16 + *
   10.17 + * You should have received a copy of the GNU General Public License
   10.18 + * along with this program; if not, write to the Free Software
   10.19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   10.20 + */
   10.21 +
   10.22 +#ifndef SYNCHRONIZER_H
   10.23 +#define SYNCHRONIZER_H
   10.24 +
   10.25 +#include <stdint.h>
   10.26 +#include "nstime.h"
   10.27 +#include "ns3/object.h"
   10.28 +
   10.29 +namespace ns3 {
   10.30 +
   10.31 +/**
   10.32 + * @brief Base class used for synchronizing the simulation events to some
   10.33 + * real time "wall clock."
   10.34 + *
   10.35 + * The simulation clock is maintained as a 64-bit integer in a unit specified
   10.36 + * by the user through the TimeStepPrecision::Set function. This means that
   10.37 + * it is not possible to specify event expiration times with anything better
   10.38 + * than this user-specified accuracy.  We use this clock for the simulation
   10.39 + * time.
   10.40 + *
   10.41 + * The real-time clock is maintained as a 64-bit integer count of nanoseconds.
   10.42 + *
   10.43 + * The synchronization between the simulation clock and the real-time clock
   10.44 + * is maintained using a combination of sleep-waiting, busy-waiting and a
   10.45 + * feedback loop.
   10.46 + */
   10.47 +class Synchronizer : public Object 
   10.48 +{
   10.49 +public:
   10.50 +  static TypeId GetTypeId (void);
   10.51 +
   10.52 +  Synchronizer ();
   10.53 +  virtual ~Synchronizer ();
   10.54 +
   10.55 +/**
   10.56 + * @brief Return true if this synchronizer is actually synchronizing to a
   10.57 + * realtime clock.  The simulator sometimes needs to know this.
   10.58 + * @returns True if locked with realtime, false if not.
   10.59 + */
   10.60 +  bool Realtime (void);
   10.61 +
   10.62 +/**
   10.63 + * @brief Retrieve the value of the origin of the underlying normalized wall
   10.64 + * clock time in simulator timestep units.
   10.65 + *
   10.66 + * @returns The normalized wall clock time (in simulator timestep units).
   10.67 + * @see TimeStepPrecision::Get
   10.68 + * @see Synchronizer::SetOrigin
   10.69 + */
   10.70 +  uint64_t GetCurrentRealtime (void);
   10.71 +
   10.72 +/**
   10.73 + * @brief Establish a correspondence between a simulation time and the
   10.74 + * synchronizer real time.
   10.75 + *
   10.76 + * This method is expected to be called at the "instant" before simulation
   10.77 + * begins.  At this point, simulation time = 0, and a 
   10.78 + * set = 0 in this method.  We then associate this time with the current
   10.79 + * value of the real time clock that will be used to actually perform the
   10.80 + * synchronization.
   10.81 + *
   10.82 + * Subclasses are expected to implement the corresponding DoSetOrigin pure
   10.83 + * virtual method to do the actual real-time-clock-specific work of making the 
   10.84 + * correspondence mentioned above.
   10.85 + *
   10.86 + * @param ts The simulation time we should use as the origin (in simulator
   10.87 + * timestep units).
   10.88 + * @see TimeStepPrecision::Get
   10.89 + * @see TimeStepPrecision::DoSetOrigin
   10.90 + */
   10.91 +  void SetOrigin (uint64_t ts);
   10.92 +
   10.93 +/**
   10.94 + * @brief Retrieve the value of the origin of the simulation time in 
   10.95 + * simulator timestep units.
   10.96 + *
   10.97 + * @returns The simulation time used as the origin (in simulator timestep
   10.98 + * units).
   10.99 + * @see TimeStepPrecision::Get
  10.100 + * @see Synchronizer::SetOrigin
  10.101 + */
  10.102 +  uint64_t GetOrigin (void);
  10.103 +
  10.104 +/**
  10.105 + * @brief Retrieve the difference between the real time clock used to 
  10.106 + * synchronize the simulation and the simulation time (in simulator timestep
  10.107 + * units).
  10.108 + *
  10.109 + * @param ts Simulation timestep from the simulator interpreted as current time
  10.110 + * in the simulator.
  10.111 + * @returns Simulation timestep (in simulator timestep units) minus origin 
  10.112 + * time (stored internally in nanosecond units).
  10.113 + * @see TimeStepPrecision::Get
  10.114 + * @see Synchronizer::SetOrigin
  10.115 + * @see Synchronizer::DoGetDrift
  10.116 + */
  10.117 +  int64_t GetDrift (uint64_t ts);
  10.118 +
  10.119 +/**
  10.120 + * @brief Wait until the real time is in sync with the specified simulation
  10.121 + * time or until the synchronizer is Sigalled.
  10.122 + *
  10.123 + * This is where the real work of synchronization is done.  The Time passed
  10.124 + * in as a parameter is the simulation time.  The job of Synchronize is to
  10.125 + * translate from simulation time to synchronizer time (in a perfect world
  10.126 + * this is the same time) and then figure out how long in real-time it needs
  10.127 + * to wait until that synchronizer / simulation time comes around.
  10.128 + *
  10.129 + * Subclasses are expected to implement the corresponding DoSynchronize pure
  10.130 + * virtual method to do the actual real-time-clock-specific work of waiting 
  10.131 + * (either busy-waiting or sleeping, or some combination thereof) until the
  10.132 + * requested simulation time.
  10.133 + *
  10.134 + * @param tsCurrent The current simulation time (in simulator timestep units).
  10.135 + * @param tsDelay The simulation time we need to wait for (in simulator
  10.136 + * timestep units).
  10.137 + * @returns True if the function ran to completion, false if it was interrupted
  10.138 + * by a Signal.
  10.139 + * @see TimeStepPrecision::Get
  10.140 + * @see Synchronizer::DoSynchronize
  10.141 + * @see Synchronizer::Signal
  10.142 + */
  10.143 +  bool Synchronize (uint64_t tsCurrent, uint64_t tsDelay);
  10.144 +
  10.145 +/**
  10.146 + * @brief Tell a posible simulator thread waiting in the Synchronize method
  10.147 + * that an event has happened which demands a reevaluation of the wait time.
  10.148 + * This will cause the thread to wake and return to the simulator proper
  10.149 + * where it can get its bearings.
  10.150 + *
  10.151 + * @see Synchronizer::Synchronize
  10.152 + * @see Synchronizer::DoSignal
  10.153 + */
  10.154 +  void Signal (void);
  10.155 +
  10.156 +/**
  10.157 + * @brief Set the condition variable that tells a posible simulator thread 
  10.158 + * waiting in the Synchronize method that an event has happened which demands
  10.159 + * a reevaluation of the wait time.
  10.160 + *
  10.161 + * @see Synchronizer::Signal
  10.162 + */
  10.163 +  void SetCondition (bool);
  10.164 +
  10.165 +/**
  10.166 + * @brief Ask the synchronizer to remember what time it is.  Typically used
  10.167 + * with EventEnd to determine the real execution time of a simulation event.
  10.168 + *
  10.169 + * @see Synchronizer::EventEnd
  10.170 + * @see TimeStepPrecision::Get
  10.171 + */
  10.172 +  void EventStart (void);
  10.173 +
  10.174 +/**
  10.175 + * @brief Ask the synchronizer to return the time step between the instant
  10.176 + * remembered during EventStart and now.  Used in conjunction with EventStart
  10.177 + * to determine the real execution time of a simulation event.
  10.178 + *
  10.179 + * @see Synchronizer::EventStart
  10.180 + * @see TimeStepPrecision::Get
  10.181 + */
  10.182 +  uint64_t EventEnd (void);
  10.183 +
  10.184 +protected:
  10.185 +/**
  10.186 + * @brief Establish a correspondence between a simulation time and a 
  10.187 + * wall-clock (real) time.
  10.188 + *
  10.189 + * @internal
  10.190 + *
  10.191 + * There are three timelines involved here:  the simulation time, the 
  10.192 + * (absolute) wall-clock time and the (relative) synchronizer real time.
  10.193 + * Calling this method makes a correspondence between the origin of the
  10.194 + * synchronizer time and the current wall-clock time.
  10.195 + *
  10.196 + * This method is expected to be called at the "instant" before simulation
  10.197 + * begins.  At this point, simulation time = 0, and synchronizer time is
  10.198 + * set = 0 in this method.  We then associate this time with the current
  10.199 + * value of the real time clock that will be used to actually perform the
  10.200 + * synchronization.
  10.201 + *
  10.202 + * Subclasses are expected to implement this method to do the actual 
  10.203 + * real-time-clock-specific work of making the correspondence mentioned above.
  10.204 + * for example, this is where the differences between Time parameters and
  10.205 + * parameters to clock_nanosleep would be dealt with. 
  10.206 + *
  10.207 + * @param ns The simulation time we need to use as the origin (normalized to
  10.208 + * nanosecond units).
  10.209 + * @see Synchronizer::SetOrigin
  10.210 + * @see TimeStepPrecision::Get
  10.211 + */
  10.212 +  virtual void DoSetOrigin (uint64_t ns) = 0;
  10.213 +
  10.214 +/**
  10.215 + * @brief Return true if this synchronizer is actually synchronizing to a
  10.216 + * realtime clock.  The simulator sometimes needs to know this.
  10.217 + *
  10.218 + * @internal
  10.219 + *
  10.220 + * Subclasses are expected to implement this method to tell the outside world
  10.221 + * whether or not they are synchronizing to a realtime clock.
  10.222 + *
  10.223 + * @returns True if locked with realtime, false if not.
  10.224 + */
  10.225 +  virtual bool DoRealtime (void) = 0;
  10.226 +
  10.227 +/**
  10.228 + * @brief Retrieve the value of the origin of the underlying normalized wall
  10.229 + * clock time in simulator timestep units.
  10.230 + *
  10.231 + * @internal
  10.232 + *
  10.233 + * Subclasses are expected to implement this method to do the actual
  10.234 + * real-time-clock-specific work of getting the current time.
  10.235 + *
  10.236 + * @returns The normalized wall clock time (in nanosecond units).
  10.237 + * @see TimeStepPrecision::Get
  10.238 + * @see Synchronizer::SetOrigin
  10.239 + */
  10.240 +  virtual uint64_t DoGetCurrentRealtime (void) = 0;
  10.241 +
  10.242 +/**
  10.243 + * @brief Wait until the real time is in sync with the specified simulation
  10.244 + * time.
  10.245 + *
  10.246 + * @internal
  10.247 + *
  10.248 + * This is where the real work of synchronization is done.  The Time passed
  10.249 + * in as a parameter is the simulation time.  The job of Synchronize is to
  10.250 + * translate from simulation time to synchronizer time (in a perfect world
  10.251 + * this is the same time) and then figure out how long in real-time it needs
  10.252 + * to wait until that synchronizer / simulation time comes around.
  10.253 + *
  10.254 + * Subclasses are expected to implement this method to do the actual
  10.255 + * real-time-clock-specific work of waiting (either busy-waiting or sleeping,
  10.256 + * or some combination) until the requested simulation time.
  10.257 + *
  10.258 + * @param nsCurrent The current simulation time (normalized to nanosecond
  10.259 + * units).
  10.260 + * @param nsDelay The simulation time we need to wait for (normalized to 
  10.261 + * nanosecond units).
  10.262 + * @returns True if the function ran to completion, false if it was interrupted
  10.263 + * by a Signal.
  10.264 + * @see Synchronizer::Synchronize
  10.265 + * @see TimeStepPrecision::Get
  10.266 + * @see Synchronizer::Signal
  10.267 + */
  10.268 +  virtual bool DoSynchronize (uint64_t nsCurrent, uint64_t nsDelay) = 0;
  10.269 +
  10.270 +/**
  10.271 + * @brief Declaration of the method used to tell a posible simulator thread 
  10.272 + * waiting in the DoSynchronize method that an event has happened which
  10.273 + * demands a reevaluation of the wait time.
  10.274 + *
  10.275 + * @see Synchronizer::Signal
  10.276 + */
  10.277 +  virtual void DoSignal (void) = 0;
  10.278 +
  10.279 +/**
  10.280 + * @brief Declaration of the method used to set the condition variable that 
  10.281 + * tells a posible simulator thread waiting in the Synchronize method that an
  10.282 + * event has happened which demands a reevaluation of the wait time.
  10.283 + *
  10.284 + * @see Synchronizer::SetCondition
  10.285 + */
  10.286 +  virtual void DoSetCondition (bool) = 0;
  10.287 +
  10.288 +/**
  10.289 + * @brief Declaration of method used to retrieve drift between the real time
  10.290 + * clock used to synchronize the simulation and the current simulation time.
  10.291 + *
  10.292 + * @internal
  10.293 + *
  10.294 + * @param ns Simulation timestep from the simulator normalized to nanosecond 
  10.295 + * steps.
  10.296 + * @returns Drift in nanosecond units.
  10.297 + * @see TimeStepPrecision::Get
  10.298 + * @see Synchronizer::SetOrigin
  10.299 + * @see Synchronizer::GetDrift
  10.300 + */
  10.301 +  virtual int64_t DoGetDrift (uint64_t ns) = 0;
  10.302 +
  10.303 +  virtual void DoEventStart (void) = 0;
  10.304 +  virtual uint64_t DoEventEnd (void) = 0;
  10.305 +
  10.306 +  uint64_t m_realtimeOriginNano;
  10.307 +  uint64_t m_simOriginNano;
  10.308 +
  10.309 +private:
  10.310 +/**
  10.311 + * @brief Convert a simulator time step (which can be steps of time in a 
  10.312 + * user-specified unit) to a normalized time step in nanosecond units.
  10.313 + *
  10.314 + * @internal
  10.315 + *
  10.316 + * @param ts The simulation time step to be normalized.
  10.317 + * @returns The simulation time step normalized to nanosecond units.
  10.318 + * @see TimeStepPrecision::Get
  10.319 + */
  10.320 +  uint64_t TimeStepToNanosecond (uint64_t ts);
  10.321 +
  10.322 +/**
  10.323 + * @brief Convert a normalized nanosecond count into a simulator time step
  10.324 + * (which can be steps of time in a user-specified unit).
  10.325 + *
  10.326 + * @internal
  10.327 + *
  10.328 + * @param ns The nanosecond count step to be converted
  10.329 + * @returns The simulation time step to be interpreted in appropriate units.
  10.330 + * @see TimeStepPrecision::Get
  10.331 + */
  10.332 +  uint64_t NanosecondToTimeStep (uint64_t ns);
  10.333 +};
  10.334 +
  10.335 +}; // namespace ns3
  10.336 +
  10.337 +#endif /* SYNCHRONIZER_H */
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/src/simulator/wall-clock-synchronizer.cc	Tue Aug 26 15:34:57 2008 -0700
    11.3 @@ -0,0 +1,490 @@
    11.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
    11.5 +/*
    11.6 + * Copyright (c) 2008 University of Washington
    11.7 + *
    11.8 + * This program is free software; you can redistribute it and/or modify
    11.9 + * it under the terms of the GNU General Public License version 2 as
   11.10 + * published by the Free Software Foundation;
   11.11 + *
   11.12 + * This program is distributed in the hope that it will be useful,
   11.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11.15 + * GNU General Public License for more details.
   11.16 + *
   11.17 + * You should have received a copy of the GNU General Public License
   11.18 + * along with this program; if not, write to the Free Software
   11.19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   11.20 + */
   11.21 +
   11.22 +#include <time.h>
   11.23 +#include <sys/time.h>
   11.24 +#include <sched.h>
   11.25 +
   11.26 +#include "ns3/log.h"
   11.27 +#include "ns3/system-condition.h"
   11.28 +
   11.29 +#include "wall-clock-synchronizer.h"
   11.30 +
   11.31 +NS_LOG_COMPONENT_DEFINE ("WallClockSynchronizer");
   11.32 +
   11.33 +namespace ns3 {
   11.34 +
   11.35 +WallClockSynchronizer::WallClockSynchronizer ()
   11.36 +{
   11.37 +  NS_LOG_FUNCTION_NOARGS ();
   11.38 +//
   11.39 +// In Linux, the basic timekeeping unit is derived from a variable called HZ
   11.40 +// HZ is the frequency in hertz of the system timer.  The system timer fires 
   11.41 +// every 1/HZ seconds and a counter, called the jiffies counter is incremented
   11.42 +// at each tick.  The time between ticks is called a jiffy (American slang for
   11.43 +// a short period of time).  The ticking of the jiffies counter is how the
   11.44 +// the kernel tells time.
   11.45 +//
   11.46 +// Now, the shortest time the kernel can sleep is one jiffy since a timer
   11.47 +// has to be set to expire and trigger the process to be made ready.  The
   11.48 +// Posix clock CLOCK_REALTIME is defined as a 1/HZ clock, so by doing a
   11.49 +// clock_getres () on the realtime clock we can infer the scheduler quantum
   11.50 +// and the minimimum sleep time for the system.  This is most certainly NOT
   11.51 +// going to be one nanosecond even though clock_nanosleep () pretends it is.
   11.52 +//
   11.53 +// The reason this number is important is that we are going to schedule lots
   11.54 +// of waits for less time than a jiffy.  The clock_nanosleep function is
   11.55 +// going to guarantee that it will sleep for AT LEAST the time specified.
   11.56 +// The least time that it will sleep is a jiffy.
   11.57 +//
   11.58 +// In order to deal with this, we are going to do a spin-wait if the simulator
   11.59 +// requires a delay less than a jiffy.  This is on the order of one millisecond
   11.60 +// (999848 ns) on the ns-regression machine.
   11.61 +// 
   11.62 +  struct timespec ts;
   11.63 +  clock_getres (CLOCK_REALTIME, &ts);
   11.64 +  m_jiffy = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
   11.65 +  NS_LOG_INFO ("Jiffy is " << m_jiffy << " ns");
   11.66 +
   11.67 +#if 0
   11.68 +//
   11.69 +// DANGER DANGER WILL ROBINSON
   11.70 +//
   11.71 +// Don't enable this code, su root and run a sim unless you really know what
   11.72 +// you are doing.
   11.73 +//
   11.74 +  struct sched_param sp;
   11.75 +  sp.sched_priority = sched_get_priority_max (SCHED_FIFO);
   11.76 +  sched_setscheduler (0, SCHED_FIFO, &sp);
   11.77 +#endif
   11.78 +}
   11.79 +
   11.80 +WallClockSynchronizer::~WallClockSynchronizer ()
   11.81 +{
   11.82 +  NS_LOG_FUNCTION_NOARGS ();
   11.83 +}
   11.84 +
   11.85 +  bool
   11.86 +WallClockSynchronizer::DoRealtime (void)
   11.87 +{
   11.88 +  NS_LOG_FUNCTION_NOARGS ();
   11.89 +  return true;
   11.90 +}
   11.91 +
   11.92 +  uint64_t
   11.93 +WallClockSynchronizer::DoGetCurrentRealtime (void)
   11.94 +{
   11.95 +  NS_LOG_FUNCTION_NOARGS ();
   11.96 +  return GetNormalizedRealtime ();
   11.97 +}
   11.98 +
   11.99 +  void
  11.100 +WallClockSynchronizer::DoSetOrigin (uint64_t ns)
  11.101 +{
  11.102 +  NS_LOG_FUNCTION_NOARGS ();
  11.103 +//
  11.104 +// In order to make sure we're really locking the simulation time to some 
  11.105 +// wall-clock time, we need to be able to compare that simulation time to
  11.106 +// that wall-clock time.  The wall clock will have been running for some
  11.107 +// long time and will probably have a huge count of nanoseconds in it.  We
  11.108 +// save the real time away so we can subtract it from "now" later and get
  11.109 +// a count of nanoseconds in real time since the simulation started.
  11.110 +//
  11.111 +  m_realtimeOriginNano = GetRealtime ();
  11.112 +  NS_LOG_INFO ("origin = " << m_realtimeOriginNano);
  11.113 +}
  11.114 +
  11.115 +  int64_t
  11.116 +WallClockSynchronizer::DoGetDrift (uint64_t ns)
  11.117 +{
  11.118 +  NS_LOG_FUNCTION_NOARGS ();
  11.119 +//
  11.120 +// In order to make sure we're really locking the simulation time to some 
  11.121 +// wall-clock time, we need to be able to compare that simulation time to
  11.122 +// that wall-clock time.  In DoSetOrigin we saved the real time at the start
  11.123 +// of the simulation away.  This is the place where we subtract it from "now"
  11.124 +// to a count of nanoseconds in real time since the simulation started.  We
  11.125 +// then subtract the current real time in normalized nanoseconds we just got
  11.126 +// from the normalized simulation time in nanoseconds that is passed in as
  11.127 +// the parameter ns.  We return an integer difference, but in reality all of
  11.128 +// the mechanisms that cause wall-clock to simuator time drift cause events
  11.129 +// to be late.  That means that the wall-clock will be higher than the 
  11.130 +// simulation time and drift will be positive.  I would be astonished to 
  11.131 +// see a negative drift, but the possibility is admitted for other 
  11.132 +// implementations; and we'll use the ability to report an early result in
  11.133 +// DoSynchronize below.
  11.134 +//
  11.135 +  uint64_t nsNow = GetNormalizedRealtime ();
  11.136 +
  11.137 +  if (nsNow > ns)
  11.138 +    {
  11.139 +//
  11.140 +// Real time (nsNow) is larger/later than the simulator time (ns).  We are
  11.141 +// behind real time and the difference (drift) is positive.
  11.142 +//
  11.143 +      return (int64_t)(nsNow - ns);
  11.144 +    }
  11.145 +  else
  11.146 +    {
  11.147 +// 
  11.148 +// Real time (nsNow) is smaller/earlier than the simulator time (ns).  We are
  11.149 +// ahead of real time and the difference (drift) is negative.
  11.150 +//
  11.151 +      return -(int64_t)(ns - nsNow);
  11.152 +    }
  11.153 +}
  11.154 +
  11.155 +  bool
  11.156 +WallClockSynchronizer::DoSynchronize (uint64_t nsCurrent, uint64_t nsDelay)
  11.157 +{
  11.158 +  NS_LOG_FUNCTION_NOARGS ();
  11.159 +//
  11.160 +// This is the belly of the beast.  We have received two parameters from the
  11.161 +// simulator proper -- a current simulation time (nsCurrent) and a simulation
  11.162 +// time to delay which identifies the time the next event is supposed to fire.
  11.163 +//
  11.164 +// The first thing we need to do is to (try and) correct for any realtime 
  11.165 +// drift that has happened in the system.  In this implementation, we realize 
  11.166 +// that all mechanisms for drift will cause the drift to be such that the 
  11.167 +// realtime is greater than the simulation time.  This typically happens when 
  11.168 +// our process is put to sleep for a given time, but actually sleeps for
  11.169 +// longer.  So, what we want to do is to "catch up" to realtime and delay for
  11.170 +// less time than we are actually asked to delay.  DriftCorrect will return a 
  11.171 +// number from 0 to nsDelay corresponding to the amount of catching-up we
  11.172 +// need to do.  If we are more than nsDelay behind, we do not wait at all.
  11.173 +//
  11.174 +// Note that it will be impossible to catch up if the amount of drift is 
  11.175 +// cumulatively greater than the amount of delay between events.  The method
  11.176 +// GetDrift () is available to clients of the syncrhonizer to keep track of
  11.177 +// the cumulative drift.  The client can assert if the drift gets out of 
  11.178 +// hand, print warning messages, or just ignore the situation and hope it will
  11.179 +// go away.
  11.180 +//
  11.181 +  uint64_t ns = DriftCorrect (nsCurrent, nsDelay);
  11.182 +  NS_LOG_INFO ("Synchronize ns = " << ns);
  11.183 +//
  11.184 +// Once we've decided on how long we need to delay, we need to split this
  11.185 +// time into sleep waits and busy waits.  The reason for this is described
  11.186 +// in the comments for the constructor where jiffies and jiffy resolution is
  11.187 +// explained.
  11.188 +//
  11.189 +// Here, I'll just say that we need that the jiffy is the minimum resolution 
  11.190 +// of the system clock.  It can only sleep in blocks of time equal to a jiffy.
  11.191 +// If we want to be more accurate than a jiffy (we do) then we need to sleep
  11.192 +// for some number of jiffies and then busy wait for any leftover time.
  11.193 +//
  11.194 +  uint64_t numberJiffies = ns / m_jiffy;
  11.195 +  NS_LOG_INFO ("Synchronize numberJiffies = " << numberJiffies);
  11.196 +//
  11.197 +// This is where the real world interjects its very ugly head.  The code 
  11.198 +// immediately below reflects the fact that a sleep is actually quite probably
  11.199 +// going to end up sleeping for some number of jiffies longer than you wanted.
  11.200 +// This is because your system is going to be off doing other unimportant 
  11.201 +// stuff during that extra time like running file systems and networks.  What
  11.202 +// we want to do is to ask the system to sleep enough less than the requested
  11.203 +// delay so that it comes back early most of the time (coming back early is
  11.204 +// fine, coming back late is bad).  If we can convince the system to come back
  11.205 +// early (most of the time), then we can busy-wait until the requested
  11.206 +// completion time actually comes around (most of the time).
  11.207 +//
  11.208 +// The tradeoff here is, of course, that the less time we spend sleeping, the
  11.209 +// more accurately we will sync up; but the more CPU time we will spend busy
  11.210 +// waiting (doing nothing).
  11.211 +//
  11.212 +// I'm not really sure about this number -- a boss of mine once said, "pick
  11.213 +// a number and it'll be wrong."  But this works for now.
  11.214 +//
  11.215 +// XXX BUGBUG Hardcoded tunable parameter below.
  11.216 +//
  11.217 +  if (numberJiffies > 3)
  11.218 +    {
  11.219 +      NS_LOG_INFO ("SleepWait for " << numberJiffies * m_jiffy << " ns");
  11.220 +      NS_LOG_INFO ("SleepWait until " << nsCurrent + numberJiffies * m_jiffy 
  11.221 +        << " ns");
  11.222 +//
  11.223 +// SleepWait is interruptible.  If it returns true it meant that the sleep
  11.224 +// went until the end.  If it returns false, it means that the sleep was 
  11.225 +// interrupted by a Signal.  In this case, we need to return and let the 
  11.226 +// simulator re-evaluate what to do.
  11.227 +//
  11.228 +      if (SleepWait ((numberJiffies - 3) * m_jiffy) == false)
  11.229 +        {
  11.230 +          NS_LOG_INFO ("SleepWait interrupted");
  11.231 +          return false;
  11.232 +        }
  11.233 +    }
  11.234 +  NS_LOG_INFO ("Done with SleepWait");
  11.235 +//
  11.236 +// We asked the system to sleep for some number of jiffies, but that doesn't 
  11.237 +// mean we actually did.  Let's re-evaluate what we need to do here.  Maybe 
  11.238 +// we're already late.  Probably the "real" delay time left has little to do
  11.239 +// with what we would calculate it to be naively.
  11.240 +//
  11.241 +// We are now at some Realtime.  The important question now is not, "what
  11.242 +// would we calculate in a mathematicians paradise," it is, "how many
  11.243 +// nanoseconds do we need to busy-wait until we get to the Realtime that
  11.244 +// corresponds to nsCurrent + nsDelay (in simulation time).  We have a handy
  11.245 +// function to do just that -- we ask for the time the realtime clock has
  11.246 +// drifted away from the simulation clock.  That's our answer.  If the drift
  11.247 +// is negative, we're early and we need to busy wait for that number of 
  11.248 +// nanoseconds.  The place were we want to be is described by the parameters
  11.249 +// we were passed by the simulator.
  11.250 +//
  11.251 +  int64_t nsDrift = DoGetDrift (nsCurrent + nsDelay);
  11.252 +//
  11.253 +// If the drift is positive, we are already late and we need to just bail out
  11.254 +// of here as fast as we can.  Return true to indicate that the requested time
  11.255 +// has, in fact, passed.
  11.256 +//
  11.257 +  if (nsDrift >= 0)
  11.258 +    {
  11.259 +      NS_LOG_INFO ("Back from SleepWait: IML8 " << nsDrift);
  11.260 +      return true;
  11.261 +    }
  11.262 +//
  11.263 +// There are some number of nanoseconds left over and we need to wait until
  11.264 +// the time defined by nsDrift.  We'll do a SpinWait since the usual case 
  11.265 +// will be that we are doing this Spinwait after we've gotten a rough delay
  11.266 +// using the SleepWait above.  If SpinWait completes to the end, it will 
  11.267 +// return true; if it is interrupted by a signal it will return false.
  11.268 +//
  11.269 +  NS_LOG_INFO ("SpinWait until " << nsCurrent + nsDelay);
  11.270 +  return SpinWait (nsCurrent + nsDelay);
  11.271 +}
  11.272 +
  11.273 +  void
  11.274 +WallClockSynchronizer::DoSignal (void)
  11.275 +{
  11.276 +  NS_LOG_FUNCTION_NOARGS ();
  11.277 +
  11.278 +  m_condition.SetCondition (true);
  11.279 +  m_condition.Signal ();
  11.280 +}
  11.281 +
  11.282 +  void
  11.283 +WallClockSynchronizer::DoSetCondition (bool cond)
  11.284 +{
  11.285 +  NS_LOG_FUNCTION_NOARGS ();
  11.286 +  m_condition.SetCondition (cond);
  11.287 +}
  11.288 +
  11.289 +  void
  11.290 +WallClockSynchronizer::DoEventStart (void)
  11.291 +{
  11.292 +  NS_LOG_FUNCTION_NOARGS ();
  11.293 +  m_nsEventStart = GetNormalizedRealtime ();
  11.294 +}
  11.295 +
  11.296 +  uint64_t
  11.297 +WallClockSynchronizer::DoEventEnd (void)
  11.298 +{
  11.299 +  NS_LOG_FUNCTION_NOARGS ();
  11.300 +  return GetNormalizedRealtime () - m_nsEventStart;
  11.301 +}
  11.302 +
  11.303 +  bool
  11.304 +WallClockSynchronizer::SpinWait (uint64_t ns)
  11.305 +{
  11.306 +  NS_LOG_FUNCTION_NOARGS ();
  11.307 +//
  11.308 +// Do a busy-wait until the normalized realtime equals the value passed in
  11.309 +// or the condition variable becomes true.  The condition becomes true if
  11.310 +// an outside entity (a network device receives a packet, sets the condition
  11.311 +// and signals the scheduler it needs to re-evaluate).
  11.312 +// 
  11.313 +// We just sit here and spin, wasting CPU cycles until we get to the right
  11.314 +// time or are told to leave.
  11.315 +//
  11.316 +  for (;;) 
  11.317 +    {
  11.318 +      if (GetNormalizedRealtime () >= ns)
  11.319 +        {
  11.320 +          return true;
  11.321 +        }
  11.322 +      if (m_condition.GetCondition ())
  11.323 +        {
  11.324 +          return false;
  11.325 +        }
  11.326 +    }
  11.327 +// Quiet compiler
  11.328 +  return true;
  11.329 +}
  11.330 +
  11.331 +  bool
  11.332 +WallClockSynchronizer::SleepWait (uint64_t ns)
  11.333 +{
  11.334 +  NS_LOG_FUNCTION_NOARGS ();
  11.335 +//
  11.336 +// Put our process to sleep for some number of nanoseconds.  Typically this
  11.337 +// will be some time equal to an integral number of jiffies.  We will usually
  11.338 +// follow a call to SleepWait with a call to SpinWait to get the kind of
  11.339 +// accuracy we want.
  11.340 +//
  11.341 +// We have to have some mechanism to wake up this sleep in case an external
  11.342 +// event happens that causes a schedule event in the simulator.  This newly
  11.343 +// scheduled event might be before the time we are waiting until, so we have
  11.344 +// to break out of both the SleepWait and the following SpinWait to go back
  11.345 +// and reschedule/resynchronize taking the new event into account.  The 
  11.346 +// SystemCondition we have saved in m_condition takes care of this for us.
  11.347 +//
  11.348 +// This call will return if the timeout expires OR if the condition is 
  11.349 +// set true by a call to WallClockSynchronizer::SetCondition (true) followed
  11.350 +// by a call to WallClockSynchronizer::Signal().  In either case, we are done
  11.351 +// waiting.  If the timeout happened, we TimedWait returns true; if a Signal
  11.352 +// happened, false.
  11.353 +//
  11.354 +  return m_condition.TimedWait (ns);
  11.355 +}
  11.356 +
  11.357 +  uint64_t
  11.358 +WallClockSynchronizer::DriftCorrect (uint64_t nsNow, uint64_t nsDelay)
  11.359 +{
  11.360 +  int64_t drift = DoGetDrift (nsNow);
  11.361 +//
  11.362 +// If we're running late, drift will be positive and we need to correct by
  11.363 +// delaying for less time.  If we're early for some bizarre reason, we don't
  11.364 +// do anything since we'll almost instantly self-correct.
  11.365 +//
  11.366 +  if (drift < 0)
  11.367 +    {
  11.368 +      return nsDelay;
  11.369 +    }
  11.370 +//
  11.371 +// If we've drifted out of sync by less than the requested delay, then just
  11.372 +// subtract the drift from the delay and fix up the drift in one go.  If we
  11.373 +// have more drift than delay, then we just play catch up as fast as possible
  11.374 +// by not delaying at all.
  11.375 +//
  11.376 +  uint64_t correction = (uint64_t)drift;
  11.377 +  if (correction <= nsDelay)
  11.378 +    {
  11.379 +      return nsDelay - correction;
  11.380 +    }
  11.381 +  else
  11.382 +    {
  11.383 +      return 0;
  11.384 +    }
  11.385 +}
  11.386 +
  11.387 +  uint64_t
  11.388 +WallClockSynchronizer::GetRealtime (void)
  11.389 +{
  11.390 +//
  11.391 +// I originally wrote this code to use CLOCK_PROCESS_CPUTIME_ID.  In Linux
  11.392 +// this is a highly accurate realtime clock.  It turns out, though, that this
  11.393 +// is a Linux bug.  This is supposed to be (posix says it is) a highly 
  11.394 +// accurate cumulative running time of the process.  In Linux it is (or at
  11.395 +// least was -- a bug is filed) a  highly accurate wall-clock time since the
  11.396 +// process was created.  Posix defines the wall clock you must use as the
  11.397 +// CLOCK_REALTIME clock.  As you can see in the constructor, the resolution
  11.398 +// of the CLOCK_REALTIME clock is a jiffy.  This is not fine-grained enough
  11.399 +// for us.  So, I'm using the gettimeofday clock even though it is an
  11.400 +// expensive call.
  11.401 +//
  11.402 +// I could write some inline assembler here to get to the timestamp counter
  11.403 +// (RDTSC instruction).  It's not as trivial as it sounds to get right.  
  11.404 +// Google for "rdtsc cpuid" to see why.  I'm leaving this for another day.
  11.405 +//
  11.406 +// N.B. This returns the value of the realtime clock.  This does not return
  11.407 +// the current normalized realtime that we are attempting to make equal to
  11.408 +// the simulation clock.  To get that number, use GetNormalizedRealtime ().
  11.409 +//
  11.410 +
  11.411 +#if 0
  11.412 +  // This will eventually stop working in linux so don't use it
  11.413 +  struct timespec tsNow;
  11.414 +  clock_gettime (CLOCK_REALTIME, &tsNow);
  11.415 +  return TimespecToNs (&tsNow);
  11.416 +#endif
  11.417 +
  11.418 +  struct timeval tvNow;
  11.419 +  gettimeofday (&tvNow, NULL);
  11.420 +  return TimevalToNs (&tvNow);
  11.421 +}
  11.422 +
  11.423 +  uint64_t
  11.424 +WallClockSynchronizer::GetNormalizedRealtime (void)
  11.425 +{
  11.426 +  return GetRealtime () - m_realtimeOriginNano;
  11.427 +}
  11.428 +
  11.429 +  void
  11.430 +WallClockSynchronizer::NsToTimespec (int64_t ns, struct timespec *ts)
  11.431 +{
  11.432 +  NS_ASSERT ((ns % US_PER_NS) == 0);
  11.433 +  ts->tv_sec = ns / NS_PER_SEC;
  11.434 +  ts->tv_nsec = ns % NS_PER_SEC;
  11.435 +}
  11.436 +
  11.437 +  void
  11.438 +WallClockSynchronizer::NsToTimeval (int64_t ns, struct timeval *tv)
  11.439 +{
  11.440 +  NS_ASSERT ((ns % US_PER_NS) == 0);
  11.441 +  tv->tv_sec = ns / NS_PER_SEC;
  11.442 +  tv->tv_usec = (ns % NS_PER_SEC) / US_PER_NS;
  11.443 +}
  11.444 +
  11.445 +  uint64_t
  11.446 +WallClockSynchronizer::TimespecToNs (struct timespec *ts)
  11.447 +{
  11.448 +  uint64_t nsResult = ts->tv_sec * NS_PER_SEC + ts->tv_nsec;
  11.449 +  NS_ASSERT ((nsResult % US_PER_NS) == 0);
  11.450 +  return nsResult;
  11.451 +}
  11.452 +
  11.453 +  uint64_t
  11.454 +WallClockSynchronizer::TimevalToNs (struct timeval *tv)
  11.455 +{
  11.456 +  uint64_t nsResult = tv->tv_sec * NS_PER_SEC + tv->tv_usec * US_PER_NS;
  11.457 +  NS_ASSERT ((nsResult % US_PER_NS) == 0);
  11.458 +  return nsResult;
  11.459 +}
  11.460 +
  11.461 +  void
  11.462 +WallClockSynchronizer::TimespecAdd (
  11.463 +  struct timespec *ts1, 
  11.464 +  struct timespec *ts2,
  11.465 +  struct timespec *result)
  11.466 +{
  11.467 +  result->tv_sec = ts1->tv_sec + ts2->tv_sec;
  11.468 +  result->tv_nsec = ts1->tv_nsec + ts2->tv_nsec;
  11.469 +  if (result->tv_nsec > (int64_t)NS_PER_SEC)
  11.470 +    {
  11.471 +      ++result->tv_sec;
  11.472 +      result->tv_nsec %= NS_PER_SEC;
  11.473 +    }
  11.474 +}
  11.475 +
  11.476 +  void
  11.477 +WallClockSynchronizer::TimevalAdd (
  11.478 +  struct timeval *tv1, 
  11.479 +  struct timeval *tv2,
  11.480 +  struct timeval *result)
  11.481 +{
  11.482 +  result->tv_sec = tv1->tv_sec + tv2->tv_sec;
  11.483 +  result->tv_usec = tv1->tv_usec + tv2->tv_usec;
  11.484 +  if (result->tv_usec > (int64_t)US_PER_SEC)
  11.485 +    {
  11.486 +      ++result->tv_sec;
  11.487 +      result->tv_usec %= US_PER_SEC;
  11.488 +    }
  11.489 +}
  11.490 +
  11.491 +}; // namespace ns3
  11.492 +
  11.493 +
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/src/simulator/wall-clock-synchronizer.h	Tue Aug 26 15:34:57 2008 -0700
    12.3 @@ -0,0 +1,204 @@
    12.4 +/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
    12.5 +/*
    12.6 + * Copyright (c) 2008 University of Washington
    12.7 + *
    12.8 + * This program is free software; you can redistribute it and/or modify
    12.9 + * it under the terms of the GNU General Public License version 2 as
   12.10 + * published by the Free Software Foundation;
   12.11 + *
   12.12 + * This program is distributed in the hope that it will be useful,
   12.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12.15 + * GNU General Public License for more details.
   12.16 + *
   12.17 + * You should have received a copy of the GNU General Public License
   12.18 + * along with this program; if not, write to the Free Software
   12.19 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   12.20 + */
   12.21 +
   12.22 +#ifndef WALL_CLOCK_CLOCK_SYNCHRONIZER_H
   12.23 +#define WALL_CLOCK_CLOCK_SYNCHRONIZER_H
   12.24 +
   12.25 +#include "ns3/system-condition.h"
   12.26 +#include "synchronizer.h"
   12.27 +
   12.28 +namespace ns3 {
   12.29 +
   12.30 +/**
   12.31 + * @brief Class used for synchronizing the simulation events to a real-time
   12.32 + * "wall clock" using Posix Clock functions.
   12.33 + *
   12.34 + * Enable this synchronizer using:
   12.35 + *
   12.36 + *   DefaultValue::Bind ("Synchronizer", "WallClockSynchronizer");
   12.37 + *
   12.38 + * before calling any simulator functions.
   12.39 + *
   12.40 + * The simulation clock is maintained as a 64-bit integer in a unit specified
   12.41 + * by the user through the TimeStepPrecision::Set function. This means that
   12.42 + * it is not possible to specify event expiration times with anything better
   12.43 + * than this user-specified accuracy.  
   12.44 + *
   12.45 + * There are a couple of more issues at this level.  Posix clocks provide
   12.46 + * access to several clocks we could use as a wall clock.  We don't care about
   12.47 + * time in the sense of 0430 CEST, we care about some piece of hardware that
   12.48 + * ticks at some regular period.  The most accurate posix clock in this
   12.49 + * respect is the CLOCK_PROCESS_CPUTIME_ID clock.  This is a high-resolution
   12.50 + * register in the CPU.  For example, on Intel machines this corresponds to
   12.51 + * the timestamp counter (TSC) register.  The resolution of this counter will
   12.52 + * be on the order of nanoseconds.
   12.53 + *
   12.54 + * Now, just because we can measure time in nanoseconds doesn't mean we can
   12.55 + * put our process to sleep to nanosecond resolution.  We are eventualy going
   12.56 + * to use the function clock_nanosleep () to sleep until a simulation Time
   12.57 + * specified by the caller. 
   12.58 + *
   12.59 + * MORE ON JIFFIES, SLEEP, PROCESSES, etc., as required
   12.60 + *
   12.61 + * Nanosleep takes a struct timespec as an input so we have to deal with
   12.62 + * conversion between Time and struct timespec here.  They are both 
   12.63 + * interpreted as elapsed times.
   12.64 + */
   12.65 +class WallClockSynchronizer : public Synchronizer
   12.66 +{
   12.67 +public:
   12.68 +  WallClockSynchronizer ();
   12.69 +  virtual ~WallClockSynchronizer ();
   12.70 +
   12.71 +  static const uint64_t US_PER_NS = (uint64_t)1000;
   12.72 +  static const uint64_t US_PER_SEC = (uint64_t)1000000;
   12.73 +  static const uint64_t NS_PER_SEC = (uint64_t)1000000000;
   12.74 +
   12.75 +protected:
   12.76 +/**
   12.77 + * @brief Return true if this synchronizer is actually synchronizing to a
   12.78 + * realtime clock.  The simulator sometimes needs to know this.
   12.79 + *
   12.80 + * @internal
   12.81 + *
   12.82 + * Subclasses are expected to implement this method to tell the outside world
   12.83 + * whether or not they are synchronizing to a realtime clock.
   12.84 + *
   12.85 + * @returns True if locked with realtime, false if not.
   12.86 + */
   12.87 +  virtual bool DoRealtime (void);
   12.88 +
   12.89 +/**
   12.90 + * @brief Retrieve the value of the origin of the underlying normalized wall
   12.91 + * clock time in nanosecond units.
   12.92 + *
   12.93 + * @internal
   12.94 + *
   12.95 + * Subclasses are expected to implement this method to do the actual
   12.96 + * real-time-clock-specific work of getting the current time.
   12.97 + *
   12.98 + * @returns The normalized wall clock time (in nanosecond units).
   12.99 + * @see TimeStepPrecision::Get
  12.100 + * @see Synchronizer::SetOrigin
  12.101 + */
  12.102 +  virtual uint64_t DoGetCurrentRealtime (void);
  12.103 +
  12.104 +/**
  12.105 + * @brief Establish a correspondence between a simulation time and a 
  12.106 + * wall-clock (real) time.
  12.107 + *
  12.108 + * @internal
  12.109 + *
  12.110 + * There are three timelines involved here:  the simulation time, the 
  12.111 + * (absolute) wall-clock time and the (relative) synchronizer real time.
  12.112 + * Calling this method makes a correspondence between the origin of the
  12.113 + * synchronizer time and the current wall-clock time.
  12.114 + *
  12.115 + * This method is expected to be called at the "instant" before simulation
  12.116 + * begins.  At this point, simulation time = 0, and synchronizer time is
  12.117 + * set = 0 in this method.  We then associate this time with the current
  12.118 + * value of the real time clock that will be used to actually perform the
  12.119 + * synchronization.
  12.120 + *
  12.121 + * Subclasses are expected to implement this method to do the actual 
  12.122 + * real-time-clock-specific work of making the correspondence mentioned above.
  12.123 + * for example, this is where the differences between Time parameters and
  12.124 + * parameters to clock_nanosleep would be dealt with. 
  12.125 + *
  12.126 + * @param ns The simulation time we need to use as the origin (normalized to
  12.127 + * nanosecond units).
  12.128 + */
  12.129 +  virtual void DoSetOrigin (uint64_t ns);
  12.130 +
  12.131 +/**
  12.132 + * @brief Declaration of method used to retrieve drift between the real time
  12.133 + * clock used to synchronize the simulation and the current simulation time.
  12.134 + *
  12.135 + * @internal
  12.136 + *
  12.137 + * @param ns Simulation timestep from the simulator normalized to nanosecond 
  12.138 + * steps.
  12.139 + * @returns Drift in nanosecond units.
  12.140 + * @see TimeStepPrecision::Get
  12.141 + * @see Synchronizer::SetOrigin
  12.142 + * @see Synchronizer::GetDrift
  12.143 + */
  12.144 +  virtual int64_t DoGetDrift (uint64_t ns);
  12.145 +
  12.146 +/**
  12.147 + * @brief Wait until the real time is in sync with the specified simulation
  12.148 + * time.
  12.149 + *
  12.150 + * @internal
  12.151 + *
  12.152 + * This is where the real work of synchronization is done.  The Time passed
  12.153 + * in as a parameter is the simulation time.  The job of Synchronize is to
  12.154 + * translate from simulation time to synchronizer time (in a perfect world
  12.155 + * this is the same time) and then figure out how long in real-time it needs
  12.156 + * to wait until that synchronizer / simulation time comes around.
  12.157 + *
  12.158 + * Subclasses are expected to implement this method to do the actual
  12.159 + * real-time-clock-specific work of waiting (either busy-waiting or sleeping,
  12.160 + * or some combination) until the requested simulation time.
  12.161 + *
  12.162 + * @param ns The simulation time we need to wait for (normalized to nanosecond
  12.163 + * units).
  12.164 + * @see TimeStepPrecision::Get
  12.165 + */
  12.166 +  virtual bool DoSynchronize (uint64_t nsCurrent, uint64_t nsDelay);
  12.167 +  virtual void DoSignal (void);
  12.168 +  virtual void DoSetCondition (bool cond);
  12.169 +
  12.170 +  virtual void DoEventStart (void);
  12.171 +  virtual uint64_t DoEventEnd (void);
  12.172 +
  12.173 +  bool SpinWait (uint64_t);
  12.174 +  bool SleepWait (uint64_t);
  12.175 +
  12.176 +  uint64_t DriftCorrect (uint64_t nsNow, uint64_t nsDelay);
  12.177 +
  12.178 +  uint64_t GetRealtime (void);
  12.179 +  uint64_t GetNormalizedRealtime (void);
  12.180 +
  12.181 +  void NsToTimespec (int64_t ns, struct timespec *ts);
  12.182 +  void NsToTimeval (int64_t ns, struct timeval *tv);
  12.183 +
  12.184 +  uint64_t TimespecToNs (struct timespec *ts);
  12.185 +  uint64_t TimevalToNs (struct timeval *tv);
  12.186 +
  12.187 +  void TimespecAdd(
  12.188 +    struct timespec *ts1, 
  12.189 +    struct timespec *ts2, 
  12.190 +    struct timespec *result);
  12.191 +
  12.192 +  void TimevalAdd (
  12.193 +    struct timeval *tv1, 
  12.194 +    struct timeval *tv2,
  12.195 +    struct timeval *result);
  12.196 +
  12.197 +  uint64_t m_cpuTick;
  12.198 +  uint64_t m_realtimeTick;
  12.199 +  uint64_t m_jiffy;
  12.200 +  uint64_t m_nsEventStart;
  12.201 +
  12.202 +  SystemCondition m_condition;
  12.203 +};
  12.204 +
  12.205 +}; // namespace ns3
  12.206 +
  12.207 +#endif /* WALL_CLOCK_SYNCHRONIZER_H */
    13.1 --- a/src/simulator/wscript	Tue Aug 26 08:42:28 2008 -0700
    13.2 +++ b/src/simulator/wscript	Tue Aug 26 15:34:57 2008 -0700
    13.3 @@ -22,7 +22,7 @@
    13.4      else:
    13.5          conf.env['USE_HIGH_PRECISION_DOUBLE'] = 0
    13.6          highprec = '128-bit integer'
    13.7 -    conf.check_message_custom('high precision time', 'implementation', highprec)
    13.8 +    conf.check_message_custom('high precision time','implementation',highprec)
    13.9  
   13.10      e = conf.create_header_configurator()
   13.11      e.mandatory = False
   13.12 @@ -59,8 +59,11 @@
   13.13          'event-impl.cc',
   13.14          'simulator.cc',
   13.15          'default-simulator-impl.cc',
   13.16 +        'realtime-simulator-impl.cc',
   13.17          'timer.cc',
   13.18          'watchdog.cc',
   13.19 +        'synchronizer.cc',
   13.20 +        'wall-clock-synchronizer.cc',
   13.21          ]
   13.22  
   13.23      headers = bld.create_obj('ns3header')
   13.24 @@ -73,6 +76,7 @@
   13.25          'simulator.h',
   13.26          'simulator-impl.h',
   13.27          'default-simulator-impl.h',
   13.28 +        'realtime-simulator-impl.h',
   13.29          'scheduler.h',
   13.30          'list-scheduler.h',
   13.31          'map-scheduler.h',
   13.32 @@ -81,6 +85,8 @@
   13.33          'timer.h',
   13.34          'timer-impl.h',
   13.35          'watchdog.h',
   13.36 +        'synchronizer.h',
   13.37 +        'wall-clock-synchronizer.h',
   13.38          ]
   13.39  
   13.40      env = bld.env_of_name('default')