add realtime simulator and associated tests
authorCraig Dowell <craigdo@ee.washington.edu>
Tue, 26 Aug 2008 15:34:57 -0700
changeset 3560 5aa65b1ea001
parent 3557 acd6d5b9c40d
child 3561 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/realtime-udp-echo.cc	Tue Aug 26 15:34:57 2008 -0700
@@ -0,0 +1,125 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+// Network topology
+//
+//       n0    n1   n2   n3
+//       |     |    |    |
+//       =================
+//              LAN
+//
+// - UDP flows from n0 to n1 and back
+// - DropTail queues 
+// - Tracing of queues and packet receptions to file "udp-echo.tr"
+
+#include <fstream>
+#include "ns3/core-module.h"
+#include "ns3/simulator-module.h"
+#include "ns3/helper-module.h"
+
+using namespace ns3;
+
+NS_LOG_COMPONENT_DEFINE ("RealtimeUdpEchoExample");
+
+int 
+main (int argc, char *argv[])
+{
+  //
+  // Make the random number generators generate reproducible results.
+  //
+  RandomVariable::UseGlobalSeed (1, 1, 2, 3, 5, 8);
+
+  //
+  // Allow the user to override any of the defaults and the above Bind() at
+  // run-time, via command-line arguments
+  //
+  CommandLine cmd;
+  cmd.Parse (argc, argv);
+
+  //
+  // But since this is a realtime script, don't allow the user to mess with
+  // that.
+  //
+  GlobalValue::Bind ("SimulatorImplementationType", 
+    StringValue ("ns3::RealtimeSimulatorImpl"));
+
+  //
+  // Explicitly create the nodes required by the topology (shown above).
+  //
+  NS_LOG_INFO ("Create nodes.");
+  NodeContainer n;
+  n.Create (4);
+
+  InternetStackHelper internet;
+  internet.Install (n);
+
+  //
+  // Explicitly create the channels required by the topology (shown above).
+  //
+  NS_LOG_INFO ("Create channels.");
+  CsmaHelper csma;
+  csma.SetChannelAttribute ("DataRate", DataRateValue (DataRate(5000000)));
+  csma.SetChannelAttribute ("Delay", TimeValue (MilliSeconds (2)));
+  csma.SetDeviceAttribute ("MTU", UintegerValue (1400));
+  NetDeviceContainer d = csma.Install (n);
+
+  //
+  // We've got the "hardware" in place.  Now we need to add IP addresses.
+  //
+  NS_LOG_INFO ("Assign IP Addresses.");
+  Ipv4AddressHelper ipv4;
+  ipv4.SetBase ("10.1.1.0", "255.255.255.0");
+  Ipv4InterfaceContainer i = ipv4.Assign (d);
+
+  NS_LOG_INFO ("Create Applications.");
+
+  //
+  // Create a UdpEchoServer application on node one.
+  //
+  uint16_t port = 9;  // well-known echo port number
+  UdpEchoServerHelper server (port);
+  ApplicationContainer apps = server.Install (n.Get(1));
+  apps.Start (Seconds (1.0));
+  apps.Stop (Seconds (10.0));
+
+  //
+  // Create a UdpEchoClient application to send UDP datagrams from node zero to
+  // node one.
+  //
+  uint32_t packetSize = 1024;
+  uint32_t maxPacketCount = 500;
+  Time interPacketInterval = Seconds (0.01);
+  UdpEchoClientHelper client (i.GetAddress (1), port);
+  client.SetAttribute ("MaxPackets", UintegerValue (maxPacketCount));
+  client.SetAttribute ("Interval", TimeValue (interPacketInterval));
+  client.SetAttribute ("PacketSize", UintegerValue (packetSize));
+  apps = client.Install (n.Get (0));
+  apps.Start (Seconds (2.0));
+  apps.Stop (Seconds (10.0));
+
+  std::ofstream ascii;
+  ascii.open ("realtime-udp-echo.tr");
+  CsmaHelper::EnablePcapAll ("realtime-udp-echo");
+  CsmaHelper::EnableAsciiAll (ascii);
+
+  //
+  // Now, do the actual simulation.
+  //
+  NS_LOG_INFO ("Run Simulation.");
+  Simulator::Run ();
+  Simulator::Destroy ();
+  NS_LOG_INFO ("Done.");
+}
--- a/examples/wscript	Tue Aug 26 08:42:28 2008 -0700
+++ b/examples/wscript	Tue Aug 26 15:34:57 2008 -0700
@@ -32,6 +32,10 @@
         ['csma', 'internet-stack'])
     obj.source = 'udp-echo.cc'
 
+    obj = bld.create_ns3_program('realtime-udp-echo',
+        ['csma', 'internet-stack'])
+    obj.source = 'realtime-udp-echo.cc'
+
     obj = bld.create_ns3_program('csma-broadcast',
         ['csma', 'internet-stack'])
     obj.source = 'csma-broadcast.cc'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/regression/tests/test-realtime-udp-echo.py	Tue Aug 26 15:34:57 2008 -0700
@@ -0,0 +1,12 @@
+#! /usr/bin/env python
+
+"""Generic trace-comparison-type regression test."""
+
+import os
+import shutil
+import tracediff
+
+def run(verbose, generate, refDirName):
+    """Execute a test."""
+
+    return tracediff.run_test(verbose, generate, refDirName, "realtime-udp-echo")
--- a/src/simulator/default-simulator-impl.cc	Tue Aug 26 08:42:28 2008 -0700
+++ b/src/simulator/default-simulator-impl.cc	Tue Aug 26 15:34:57 2008 -0700
@@ -48,6 +48,10 @@
 
 DefaultSimulatorImpl::DefaultSimulatorImpl ()
 {
+  // No multithreaded stuff here, make sure EventImpl instances don't try and
+  // use any stale locking functions.
+  EventImpl::SetNoEventLock ();
+
   m_stop = false;
   m_stopAt = 0;
   // uids are allocated from 4.
--- a/src/simulator/event-impl.cc	Tue Aug 26 08:42:28 2008 -0700
+++ b/src/simulator/event-impl.cc	Tue Aug 26 15:34:57 2008 -0700
@@ -23,6 +23,7 @@
 
 namespace ns3 {
 
+EventLock *EventImpl::m_eventLock = 0;
 
 EventImpl::~EventImpl ()
 {}
@@ -31,6 +32,19 @@
   : m_cancel (false),
     m_count (1)
 {}
+
+void
+EventImpl::SetEventLock (EventLock *eventLock)
+{
+  m_eventLock = eventLock;
+}
+
+void
+EventImpl::SetNoEventLock (void)
+{
+  m_eventLock = 0;
+}
+
 void 
 EventImpl::Invoke (void)
 {
@@ -39,6 +53,7 @@
       Notify ();
     }
 }
+
 void 
 EventImpl::Cancel (void)
 {
--- a/src/simulator/event-impl.h	Tue Aug 26 08:42:28 2008 -0700
+++ b/src/simulator/event-impl.h	Tue Aug 26 15:34:57 2008 -0700
@@ -21,11 +21,69 @@
 #define EVENT_IMPL_H
 
 #include <stdint.h>
+#include "ns3/system-mutex.h"
 
 namespace ns3 {
 
 /**
  * \ingroup simulator
+ * \brief Base class for locking functionality for events.
+ *
+ * This class provides a cheap way (from the perspective of the event) to lock 
+ * an event for multithreaded access.  This is a bit of a hack, but it lets us
+ * use a whole lot of existing mechanism in the multithreaded simulator case.
+ *
+ * Here's a taste of the problem.  It makes life extraordinarily easier in the 
+ * case of interfacing real network devices to be able to have threads reading
+ * from real sockets and doing ScheduleNow calls to inject packets into ns-3.
+ * It is desirable to have Schedule* calls all work similarly as well.  That is
+ * you don't want to have to do different calls when working from an "external"
+ * thread than you do when working in the context of the main simulation thread.
+ *
+ * It turns out that basically all of the Schedule* calls return an EventId.
+ * Each EventId holds a reference to the underlying event.  Clients (see the
+ * Applications for examples) often schedule events and hold onto the EventId
+ * in case they need to cancel the event later.  The EventImpl that underlies 
+ * all of this is reference counted and sharing an unprotected reference 
+ * counted object between threads is a bad thing.
+ *
+ * There were several possible solutions:
+ *
+ * - Put a mutex into each event (costs 40 bytes for the mutex and a minumum of 
+ *   three system calls to use an event);
+ * - Work on the inheritance diagram of EventImpl to make a 
+ *   MultithreadedEventImpl and pull apart all of the MakeEvent functions to 
+ *   teach them to make the right kind of event based on a flag returned by the
+ *   simulator;
+ * - Rework the entire event mechanism to use raw pointers and avoid the entire
+ *   reference counting approach with its associated problems;
+ * - Provide a cheap way to use a shared mutex (and avoid using the mutex in 
+ *   cases where it is not required).
+ *
+ * The original prototype chose the first option since it was easy.  I am very
+ * hesitant to rework the entire event mechanism or even pull apart the MakeEvent
+ * code at this point.  We went with the last option even though it feels a bit
+ * like a hack.
+ *
+ * The result is the EventLock class.  If you have a simulator implementation that 
+ * is going to need multithreaded access to events, you need to inherit from 
+ * EventLock and provide a object that does real locking.  Give the object to the 
+ * EventImpl code by calling EventImpl::SetEventLock, take it back by calling
+ * EventImpl::SetNoEventLock or provide a new one.  The EventImpl code takes no
+ * responsibility for the object passed in.
+ *
+ * \see EventImpl
+ */
+class EventLock
+{
+public:
+  virtual ~EventLock () {};
+  virtual void Lock (void) = 0;
+  virtual void Unlock (void) = 0;
+};
+
+/**
+ * \ingroup simulator
  * \brief a simulation event
  *
  * Each subclass of this base class represents a simulation event. The
@@ -34,6 +92,8 @@
  * obviously (there are Ref and Unref methods) reference-counted and
  * most subclasses are usually created by one of the many Simulator::Schedule
  * methods.
+ *
+ * \see EventLock
  */
 class EventImpl
 {
@@ -58,12 +118,31 @@
    * Invoked by the simulation engine before calling Invoke.
    */
   bool IsCancelled (void);
+
+  /**
+   * Provide a mutex with Lock and Unlock methods to the event implementation
+   * so that it can do cheap (from the perspective of event memory usage)
+   * critical sections (mutual exclusion) in the reference counting code.
+   *
+   * \param eventLock Pointer to the EventLock object used to contain the
+   * underlying mutex.
+   */
+  static void SetEventLock (EventLock *eventLock);
+  /**
+   * Remove any reference the event implementation code may hold to to an 
+   * existing EventLock and disable the event locking feature.
+   *
+   * \see SetEventLock
+   */
+  static void SetNoEventLock (void);
+
 protected:
   virtual void Notify (void) = 0;
+
 private:
-  friend class Event;
   bool m_cancel;
   mutable uint32_t m_count;
+  static EventLock *m_eventLock;
 };
 
 }; // namespace ns3
@@ -73,13 +152,35 @@
 void
 EventImpl::Ref (void) const
 {
-  m_count++;
+  if (m_eventLock)
+    {
+      m_eventLock->Lock ();
+      m_count++;
+      m_eventLock->Unlock ();
+    }
+  else
+    {
+      m_count++;
+    }
 }
+
 void
 EventImpl::Unref (void) const
 {
-  m_count--;
-  if (m_count == 0)
+  register uint32_t c;
+
+  if (m_eventLock)
+    {
+      m_eventLock->Lock ();
+      c = --m_count;
+      m_eventLock->Unlock ();
+    }
+  else
+    {
+      c = --m_count;
+    }
+
+  if (c == 0)
     {
       delete this;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/simulator/realtime-simulator-impl.cc	Tue Aug 26 15:34:57 2008 -0700
@@ -0,0 +1,794 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "simulator.h"
+#include "realtime-simulator-impl.h"
+#include "wall-clock-synchronizer.h"
+#include "scheduler.h"
+#include "event-impl.h"
+#include "synchronizer.h"
+
+#include "ns3/ptr.h"
+#include "ns3/pointer.h"
+#include "ns3/assert.h"
+#include "ns3/fatal-error.h"
+#include "ns3/log.h"
+#include "ns3/system-mutex.h"
+#include "ns3/boolean.h"
+#include "ns3/enum.h"
+
+
+#include <math.h>
+
+NS_LOG_COMPONENT_DEFINE ("RealtimeSimulatorImpl");
+
+namespace ns3 {
+
+NS_OBJECT_ENSURE_REGISTERED (RealtimeSimulatorImpl);
+
+TypeId
+RealtimeSimulatorImpl::GetTypeId (void)
+{
+  static TypeId tid = TypeId ("ns3::RealtimeSimulatorImpl")
+    .SetParent<Object> ()
+    .AddConstructor<RealtimeSimulatorImpl> ()
+    .AddAttribute ("ReportSimulatedTime", 
+                   "Report simulated time in Simulator::Now if true, otherwise wall-clock time (will be different).",
+                   BooleanValue (true),
+                   MakeBooleanAccessor (&RealtimeSimulatorImpl::m_reportSimulatedTime),
+                   MakeBooleanChecker ())
+    .AddAttribute ("SynchronizationMode", 
+                   "What to do if the simulation cannot keep up with real time.",
+                   EnumValue (SYNC_BEST_EFFORT),
+                   MakeEnumAccessor (&RealtimeSimulatorImpl::SetSynchronizationMode),
+                   MakeEnumChecker (SYNC_BEST_EFFORT, "BestEffort",
+                                    SYNC_HARD_LIMIT, "HardLimit"))
+    .AddAttribute ("HardLimit", 
+                   "Maximum acceptable real-time jitter (used in conjunction with SynchronizationMode=HardLimit)",
+                   TimeValue (Seconds (0.1)),
+                   MakeTimeAccessor (&RealtimeSimulatorImpl::m_hardLimit),
+                   MakeTimeChecker ())
+    ;
+  return tid;
+}
+
+void
+RealtimeEventLock::Lock (void)
+{
+  m_eventMutex.Lock ();
+}
+
+void
+RealtimeEventLock::Unlock (void)
+{
+  m_eventMutex.Unlock ();
+}
+
+RealtimeSimulatorImpl::RealtimeSimulatorImpl ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+  EventImpl::SetEventLock (&m_eventLock);
+
+  m_stop = false;
+  m_stopAt = 0;
+  m_running = false;
+  // uids are allocated from 4.
+  // uid 0 is "invalid" events
+  // uid 1 is "now" events
+  // uid 2 is "destroy" events
+  m_uid = 4; 
+  // before ::Run is entered, the m_currentUid will be zero
+  m_currentUid = 0;
+  m_currentTs = 0;
+  m_unscheduledEvents = 0;
+
+  // Be very careful not to do anything that would cause a change or assignment
+  // of the underlying reference counts of m_synchronizer or you will be sorry.
+  m_synchronizer = CreateObject<WallClockSynchronizer> ();
+}
+
+RealtimeSimulatorImpl::~RealtimeSimulatorImpl ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  while (m_events->IsEmpty () == false)
+    {
+      EventId next = m_events->RemoveNext ();
+    }
+  m_events = 0;
+  m_synchronizer = 0;
+
+  EventImpl::SetNoEventLock ();
+}
+
+void
+RealtimeSimulatorImpl::Destroy ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+  //
+  // This function is only called with the private version "disconnected" from
+  // the main simulator functions.  We rely on the user not calling 
+  // Simulator::Destroy while there is a chance that a worker thread could be
+  // accessing the current instance of the private object.  In practice this
+  // means shutting down the workers and doing a Join() before calling the
+  // Simulator::Destroy().
+  //
+  while (m_destroyEvents.empty () == false) 
+    {
+      Ptr<EventImpl> ev = m_destroyEvents.front ().PeekEventImpl ();
+      m_destroyEvents.pop_front ();
+      NS_LOG_LOGIC ("handle destroy " << ev);
+      if (ev->IsCancelled () == false)
+        {
+          ev->Invoke ();
+        }
+    }
+}
+
+void
+RealtimeSimulatorImpl::SetScheduler (Ptr<Scheduler> scheduler)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+  { 
+    CriticalSection cs (m_mutex);
+
+    if (m_events != 0)
+      {
+        while (m_events->IsEmpty () == false)
+          {
+            EventId next = m_events->RemoveNext ();
+            scheduler->Insert (next);
+          }
+      }
+    m_events = scheduler;
+  }
+}
+
+Ptr<Scheduler>
+RealtimeSimulatorImpl::GetScheduler (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_events;
+}
+
+void
+RealtimeSimulatorImpl::ProcessOneEvent (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  //
+  // The idea here is to wait until the next event comes due.  In the case of
+  // a simulation not locked to realtime, the next event is due immediately and
+  // we jump time forward -- there is no real time consumed between events.  In
+  // the case of a realtime simulation, we do want real time to be consumed 
+  // between events.  It is the synchronizer that causes real time to be 
+  // consumed.
+  //
+  // Now, there is a possibility that a wait down in the call to the synchronizer
+  // will be interrupted.  In this case, we need to re-evaluate how long to wait 
+  // until the next event since the interruption may have been caused by an
+  // event inserted before the one that was on the head of the list when we 
+  // started.
+  //
+  // m_synchronizer->Synchronize will return true if the wait was completed
+  // without interruption, otherwise it will return false.  So our goal is to
+  // sit in a for loop trying to synchronize until it returns true.  If this 
+  // happens we will have successfully synchronized the execution time of the
+  // next event with the wall clock time of the synchronizer.
+  //
+
+  for (;;) 
+    {
+      uint64_t tsDelay = 0;
+      uint64_t tsNow = m_currentTs;
+      uint64_t tsNext = 0;
+      //
+      // NextTs peeks into the event list and returns the time stamp of the first 
+      // event in the list.  We need to protect this kind of access with a critical
+      // section.
+      // 
+      { 
+        CriticalSection cs (m_mutex);
+
+        NS_ASSERT_MSG (NextTs () >= m_currentTs, 
+          "RealtimeSimulatorImpl::ProcessOneEvent (): "
+          "Next event time earlier than m_currentTs (list order error)");
+        //
+        // Since we are in realtime mode, the time to delay has got to be the 
+        // difference between the current realtime and the timestamp of the next 
+        // event.  Since m_currentTs is actually the timestamp of the last event we 
+        // executed, it's not particularly meaningful for us here since real time has
+        // since elapsed.  
+        //
+        // It is possible that the current realtime has drifted past the next event
+        // time so we need to be careful about that and not delay in that case.
+        //
+        NS_ASSERT_MSG (m_synchronizer->Realtime (), 
+          "RealtimeSimulatorImpl::ProcessOneEvent (): Synchronizer reports not Realtime ()");
+
+        tsNow = m_synchronizer->GetCurrentRealtime ();
+        tsNext = NextTs ();
+
+        if (tsNext <= tsNow)
+          {
+            tsDelay = 0;
+          }
+        else
+          {
+            tsDelay = tsNext - tsNow;
+          }
+      
+        m_synchronizer->SetCondition (false);
+      }
+
+      //
+      // So, we have a time to delay.  This time may actually not be valid anymore
+      // since we released the critical section and a Schedule may have snuck in just
+      // after the closing brace above.
+      //
+      // It's easiest to understand if you just consider a short tsDelay that only
+      // requires a SpinWait down in the synchronizer.  What will happen is that 
+      // whan Synchronize calls SpinWait, SpinWait will look directly at its 
+      // condition variable.  Note that we set this condition variable to false 
+      // inside the critical section above. 
+      //
+      // SpinWait will go into a forever loop until either the time has expired or
+      // until the condition variable becomes true.  A true condition indicates that
+      // the wait should stop.  The condition is set to true by one of the Schedule
+      // methods of the simulator; so if we are in a wait down in Synchronize, and
+      // a Simulator::Schedule is done, the wait down in Synchronize will exit and
+      // Synchronize will return (false).
+      //
+      // So, we set this condition to false in a critical section.  The code that
+      // sets the condition (Simulator::Schedule) also runs protected by the same
+      // critical section mutex -- there is no race.  We call Synchronize -- if no
+      // Simulator::Schedule is done, the waits (sleep- and spin-wait) will complete
+      // and Synchronize will return true.  If a Schedule is done before we get to 
+      // Synchronize, the Synchronize code will check the condition before going to
+      // sleep.  If a Schedule happens while we are sleeping, we let the kernel wake
+      // us up.
+      //
+      // So the bottom line is that we just stay in this for loop, looking for the
+      // next timestamp and attempting to sleep until its due.  If we've slept until
+      // the timestamp is due, Synchronize() returns true and we break out of the 
+      //sync loop.  If we're interrupted we continue in the loop.  We may find that
+      // the next timestamp is actually earlier than we thought, but we continue 
+      // around the loop and reevaluate and wait for that one correctly.
+      //
+      if (m_synchronizer->Synchronize (tsNow, tsDelay))
+        {
+          NS_LOG_LOGIC ("Interrupted ...");
+          break;
+        }
+    }
+
+  //
+  // Okay, now more words.  We have slept without interruption until the 
+  // timestamp we found at the head of the event list when we started the sleep.
+  // We are now outside a critical section, so another schedule operation can
+  // sneak in.  What does this mean?  The only thing that can "go wrong" is if
+  // the new event was moved in ahead of the timestamp for which we waited.
+  //
+  // If you think about it, this is not a problem, since the best we can 
+  // possibly do is to execute the event as soon as we can execute it.  We'll
+  // be a little late, but we were late anyway.
+  //
+  // So we just go ahead and pull the first event off of the list, even though
+  // it may be the case that it's not actually the one we waited for.
+  //
+  // One final tidbit is, "what the heck time is it anyway"?  The simulator is
+  // going to want to get a timestamp from the next event and wait until the
+  // wall clock time is equal to the timestamp.  At the point when those times
+  // are the same (roughly) we get to this point and set the m_currentTs below.
+  // That's straightforward here, but you might ask, how does the next event get
+  // the time when it is inserted from an external source?
+  //
+  // The method RealtimeSimulatorImpl::ScheduleNow takes makes an event and 
+  // needs to schedule that event for immediate execution.  If the simulator is 
+  // not locked to a realtime source, the current time is m_currentTs.  If it is
+  // locked to a realtime source, we need to use the real current real time.
+  // This real time is then used to set the event execution time and will be 
+  // read by the next.GetTs below and will set the current simulation time.
+  //
+  EventId next;
+
+  { 
+    CriticalSection cs (m_mutex);
+
+    NS_ASSERT_MSG (m_events->IsEmpty () == false, 
+      "RealtimeSimulatorImpl::ProcessOneEvent(): event queue is empty");
+
+    next = m_events->RemoveNext ();
+    --m_unscheduledEvents;
+  }
+
+  NS_ASSERT_MSG (next.GetTs () >= m_currentTs,
+    "RealtimeSimulatorImpl::ProcessOneEvent(): "
+    "next.GetTs() earlier than m_currentTs (list order error)");
+  NS_LOG_LOGIC ("handle " << next.GetTs ());
+  m_currentTs = next.GetTs ();
+  m_currentUid = next.GetUid ();
+
+  // 
+  // We're about to run the event and we've done our best to synchronize this
+  // event execution time to real time.  Now, if we're in SYNC_HARD_LIMIT mode
+  // we have to decide if we've done a good enough job and if we haven't, we've
+  // been asked to commit ritual suicide.
+  //
+  if (m_synchronizationMode == SYNC_HARD_LIMIT)
+    {
+      uint64_t tsFinal = m_synchronizer->GetCurrentRealtime ();
+      uint64_t tsJitter;
+
+      if (tsFinal >= m_currentTs)
+        {
+          tsJitter = tsFinal - m_currentTs;
+        }
+      else
+        {
+          tsJitter = m_currentTs - tsFinal;
+        }
+
+      if (tsJitter > static_cast<uint64_t>(m_hardLimit.GetTimeStep ()))
+        {
+          NS_FATAL_ERROR ("RealtimeSimulatorImpl::ProcessOneEvent (): "
+                          "Hard real-time limit exceeded (jitter = " << tsJitter << ")");
+        }
+    }
+
+  EventImpl *event = next.PeekEventImpl ();
+  m_synchronizer->EventStart ();
+  event->Invoke ();
+  m_synchronizer->EventEnd ();
+}
+
+bool 
+RealtimeSimulatorImpl::IsFinished (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  bool rc;
+  {
+    CriticalSection cs (m_mutex);
+    rc = m_events->IsEmpty ();
+  }
+
+  return rc;
+}
+
+//
+// Peeks into event list.  Should be called with critical section locked.
+//
+uint64_t
+RealtimeSimulatorImpl::NextTs (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  NS_ASSERT_MSG (m_events->IsEmpty () == false, 
+    "RealtimeSimulatorImpl::NextTs(): event queue is empty");
+  EventId id = m_events->PeekNext ();
+
+  return id.GetTs ();
+}
+
+//
+// Calls NextTs().  Should be called with critical section locked.
+//
+Time
+RealtimeSimulatorImpl::Next (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return TimeStep (NextTs ());
+}
+
+void
+RealtimeSimulatorImpl::Run (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_running = true;
+  NS_ASSERT_MSG (m_currentTs == 0,
+    "RealtimeSimulatorImpl::Run(): The beginning of time is not zero");
+  m_synchronizer->SetOrigin (m_currentTs);
+
+  for (;;) 
+    {
+      bool done = false;
+
+      {
+        CriticalSection cs (m_mutex);
+        //
+        // In all cases we stop when the event list is empty.  If you are doing a 
+        // realtime simulation and you want it to extend out for some time, you must
+        // call StopAt.  In the realtime case, this will stick a placeholder event out
+        // at the end of time.
+        //
+        if (m_stop || m_events->IsEmpty ())
+          {
+            done = true;
+          }
+        //
+        // We also want to stop the simulator at some time even if there are events 
+        // that have been scheduled out in the future.  If we're in realtime mode, we 
+        // actually have time passing, so we must look at the realtime clock to see if 
+        // we're past the end time.
+        //
+        if (m_stopAt && m_stopAt <= m_synchronizer->GetCurrentRealtime ())
+          {
+            done = true;
+          }
+      }
+
+      if (done)
+        {
+          break;
+        }
+
+      ProcessOneEvent ();
+    }
+
+  //
+  // If the simulator stopped naturally by lack of events, make a
+  // consistency test to check that we didn't lose any events along the way.
+  //
+  {
+    CriticalSection cs (m_mutex);
+
+    NS_ASSERT_MSG (m_events->IsEmpty () == false || m_unscheduledEvents == 0,
+      "RealtimeSimulatorImpl::Run(): Empty queue and unprocessed events");
+  }
+
+  m_running = false;
+}
+
+bool
+RealtimeSimulatorImpl::Running (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_running;
+}
+
+bool
+RealtimeSimulatorImpl::Realtime (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_synchronizer->Realtime ();
+}
+
+void
+RealtimeSimulatorImpl::RunOneEvent (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  NS_FATAL_ERROR ("DefaultSimulatorImpl::RunOneEvent(): Not allowed in realtime simulations");
+}
+
+void 
+RealtimeSimulatorImpl::Stop (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_stop = true;
+}
+
+static void Placeholder (void) {}
+
+void 
+RealtimeSimulatorImpl::Stop (Time const &time)
+{
+  NS_LOG_FUNCTION (time);
+  NS_ASSERT_MSG (time.IsPositive (),
+    "RealtimeSimulatorImpl::Stop(): Negative time");
+
+  Time absolute = Simulator::Now () + time;
+  m_stopAt = absolute.GetTimeStep ();
+  //
+  // For the realtime case, we need a real event sitting out at the end of time
+  // to keep the simulator running (sleeping) while there are no other events 
+  // present.  If an "external" device in another thread decides to schedule an
+  // event, the sleeping synchronizer will be awakened and the new event will
+  // be run.
+  //
+  // The easiest thing to do is to call back up into the simulator to take 
+  // advantage of all of the nice event wrappers.  This will call back down into
+  // RealtimeSimulatorImpl::Schedule to do the work.
+  //
+  Simulator::Schedule (absolute, &Placeholder);
+}
+
+EventId
+RealtimeSimulatorImpl::Schedule (Time const &time, const Ptr<EventImpl> &event)
+{
+  NS_LOG_FUNCTION (time << event);
+
+  //
+  // This is a little tricky.  We get a Ptr<EventImpl> passed down to us in some
+  // thread context.  This Ptr<EventImpl> is not yet shared in any way.  It is 
+  // possible however that the calling context is not the context of the main 
+  // scheduler thread (eg. it is in the context of a separate device thread).  
+  // It would be bad (TM) if we naively wrapped the EventImpl up in an EventId 
+  // that would be accessible from multiple threads without considering thread 
+  // safety.
+  //
+  // Having multiple EventId containing Ptr<EventImpl> in different thread
+  // contexts creates a situation where atomic reference count decrements
+  // would be required (think about an event being scheduled with the calling
+  // yielding immediately afterward.  The scheduler could become ready and 
+  // fire the event which would eventually want to release the EventImpl.  If
+  // the calling thread were readied and executed in mid-release, it would also
+  // want to release the EventImpl "at the same time."  If we are careless about
+  // this, multiple deletes are sure to eventually happen as the threads 
+  // separately play with the EventImpl reference counts.
+  // 
+  // The way this all works is that we created an EventImpl in the template 
+  // function that called us, which may be in the context of a thread separate
+  // from the simulator thread.  We are managing the lifetime of the EventImpl
+  // with an intrusive reference count.  The count was initialized to one when
+  // it was created and is still one right now.  We're going to "wrap" this
+  // EventImpl in an EventId in this method.  There's an assignment of our 
+  // Ptr<EventImpl> into another Ptr<EventImpl> inside the EventId which will 
+  // bump the ref count to two.  We're going to return this EventId to the 
+  // caller so the calling thread will hold a reference to the underlying 
+  // EventImpl.  This is what causes the first half of the multithreading issue.
+  //
+  // We are going to call Insert() on the EventId to put the event into the 
+  // scheduler.  Down there, it will do a PeekEventImpl to get the pointer to
+  // the EventImpl and manually increment the reference count (to three).  From 
+  // there, the sheduler works with the EventImpl pointer.  When the EventId 
+  // below goes out of scope, the Ptr<EventImpl> destructor is run and the ref
+  // count is decremented to two.  When this function returns to the calling
+  // template function, the Ptr<EventImpl> there will go out of scope and we'll
+  // decrement the EventImpl ref count leaving it to be the one held by our
+  // scheduler directly.
+  //
+  // The scheduler removes the EventImpl in Remove() or RemoveNext().  In the
+  // case of Remove(), the scheduler is provided an Event reference and locates
+  // the corresponding EventImpl in the event list.  It gets the raw pointer to
+  // the EventImpl, erases the pointer in the list, and manually calls Unref()
+  // on the pointer.  In RemoveNext(), it gets the raw pointer from the list and
+  // assigns it to a Ptr<EventImpl> without bumping the reference count, thereby
+  // transferring ownership to a containing EventId.  This is the second half of
+  // the multithreading issue.
+  //
+  // It's clear that we cannot have a situation where the EventImpl is "owned" by
+  // multiple threads.  The calling thread is free to hold the EventId as long as
+  // it wants and manage the reference counts to the underlying EventImpl all it
+  // wants.  The scheduler is free to do the same; and will eventually release
+  // the reference in the context of thread running ProcessOneEvent().  It is 
+  // "a bad thing" (TM) if these two threads decide to release the underlying
+  // EventImpl "at the same time."
+  //
+  // The answer is to make the reference counting of the EventImpl thread safe; 
+  // which we do.  We don't want to force the event implementation to carry around
+  // a mutex, so we "lend" it one using a RealtimeEventLock object (m_eventLock)
+  // in the constructor and take it back in the destructor.
+  //
+  EventId id;
+
+  {
+    CriticalSection cs (m_mutex);
+
+    NS_ASSERT_MSG (time.IsPositive (),
+      "RealtimeSimulatorImpl::Schedule(): Negative time");
+    NS_ASSERT_MSG (
+      time >= TimeStep (m_currentTs),
+      "RealtimeSimulatorImpl::Schedule(): time < m_currentTs");
+
+    uint64_t ts = (uint64_t) time.GetTimeStep ();
+
+    id = EventId (event, ts, m_uid);
+    m_uid++;
+    ++m_unscheduledEvents;
+    m_events->Insert (id);
+    m_synchronizer->Signal ();
+  }
+
+  return id;
+}
+
+EventId
+RealtimeSimulatorImpl::ScheduleNow (const Ptr<EventImpl> &event)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  EventId id;
+  {
+    CriticalSection cs (m_mutex);
+
+    id = EventId (event, m_synchronizer->GetCurrentRealtime (), m_uid);
+    
+    m_uid++;
+    ++m_unscheduledEvents;
+    m_events->Insert (id);
+    m_synchronizer->Signal ();
+  }
+
+  return id;
+}
+
+EventId
+RealtimeSimulatorImpl::ScheduleDestroy (const Ptr<EventImpl> &event)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  EventId id;
+
+  {
+    CriticalSection cs (m_mutex);
+
+    //
+    // Time doesn't really matter here (especially in realtime mode).  It is 
+    // overridden by the uid of 2 which identifies this as an event to be 
+    // executed at Simulator::Destroy time.
+    //
+    id = EventId (event, m_currentTs, 2);
+    m_destroyEvents.push_back (id);
+    m_uid++;
+  }
+
+  return id;
+}
+
+Time
+RealtimeSimulatorImpl::Now (void) const
+{
+  //
+  // The behavior of Now depends on the setting of the "ReportSimulatedTime"
+  // attribute.  If this is set to true, then Now will pretend that the realtime
+  // synchronization is perfect and that event execution consumes no time.  This
+  // allows models written with this kind of assumption to work as they did in 
+  // non-real-time cases.  However, if the attribute is set to false, we're going
+  // to give the caller the bad news right in the face.  If it sets an event to 
+  // be executed at 10.0000000 seconds, and calls Simulator::Now it will get the 
+  // realtime clock value which almost certainly will *not* be ten seconds.  We'll
+  // get as close as possible, but we won't be perfect and we won't pretend to be.
+  // Also, if the client calls Simulator::Now multiple times in an event, she will
+  // get different times as the underlying realtime clock will have moved.  However
+  // if the simulation is not actually running, we'll use the last event timestamp
+  // as the time, which will be a precise simulation time (0 sec before the sim
+  // starts, or the end time after the simulation ends).
+  //
+  if ((m_reportSimulatedTime == false) && Running ())
+    {
+      return TimeStep (m_synchronizer->GetCurrentRealtime ());
+    }
+  else
+    {
+      return TimeStep (m_currentTs);
+    }
+}
+
+Time 
+RealtimeSimulatorImpl::GetDelayLeft (const EventId &id) const
+{
+  if (IsExpired (id))
+    {
+      return TimeStep (0);
+    }
+  else
+    {
+      return TimeStep (id.GetTs () - m_synchronizer->GetCurrentRealtime ());
+    }
+}
+
+void
+RealtimeSimulatorImpl::Remove (const EventId &ev)
+{
+  if (ev.GetUid () == 2)
+    {
+      // destroy events.
+      for (DestroyEvents::iterator i = m_destroyEvents.begin (); 
+           i != m_destroyEvents.end (); 
+           i++)
+        {
+          if (*i == ev)
+            {
+              m_destroyEvents.erase (i);
+              break;
+            }
+         }
+      return;
+    }
+  if (IsExpired (ev))
+    {
+      return;
+    }
+
+  {
+    CriticalSection cs (m_mutex);
+
+    m_events->Remove (ev);
+    --m_unscheduledEvents;
+
+    Cancel (ev);
+
+  }
+}
+
+void
+RealtimeSimulatorImpl::Cancel (const EventId &id)
+{
+  if (IsExpired (id) == false)
+    {
+      id.PeekEventImpl ()->Cancel ();
+    }
+}
+
+bool
+RealtimeSimulatorImpl::IsExpired (const EventId &ev) const
+{
+  if (ev.GetUid () == 2)
+    {
+      // destroy events.
+      for (DestroyEvents::const_iterator i = m_destroyEvents.begin (); 
+           i != m_destroyEvents.end (); i++)
+        {
+          if (*i == ev)
+            {
+              return false;
+            }
+         }
+      return true;
+    }
+  if (ev.PeekEventImpl () == 0 ||
+      ev.GetTs () < m_currentTs ||
+      (ev.GetTs () == m_currentTs &&
+       ev.GetUid () <= m_currentUid) ||
+      ev.PeekEventImpl ()->IsCancelled ()) 
+    {
+      return true;
+    }
+  else
+    {
+      return false;
+    }
+}
+
+Time 
+RealtimeSimulatorImpl::GetMaximumSimulationTime (void) const
+{
+  // XXX: I am fairly certain other compilers use other non-standard
+  // post-fixes to indicate 64 bit constants.
+  return TimeStep (0x7fffffffffffffffLL);
+}
+
+void 
+RealtimeSimulatorImpl::SetSynchronizationMode (enum SynchronizationMode mode)
+{
+  NS_LOG_FUNCTION (mode);
+  m_synchronizationMode = mode;
+}
+
+RealtimeSimulatorImpl::SynchronizationMode
+RealtimeSimulatorImpl::GetSynchronizationMode (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_synchronizationMode;
+}
+  
+void 
+RealtimeSimulatorImpl::SetHardLimit (Time limit)
+{
+  NS_LOG_FUNCTION (limit);
+  m_hardLimit = limit;
+}
+
+Time
+RealtimeSimulatorImpl::GetHardLimit (void) const
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return m_hardLimit;
+}
+  
+}; // namespace ns3
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/simulator/realtime-simulator-impl.h	Tue Aug 26 15:34:57 2008 -0700
@@ -0,0 +1,153 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef REALTIME_SIMULATOR_IMPL_H
+#define REALTIME_SIMULATOR_IMPL_H
+
+#include "simulator-impl.h"
+
+#include "scheduler.h"
+#include "synchronizer.h"
+#include "event-impl.h"
+
+#include "ns3/ptr.h"
+#include "ns3/assert.h"
+#include "ns3/log.h"
+
+#include <list>
+#include <fstream>
+
+namespace ns3 {
+
+class RealtimeEventLock : public EventLock
+{
+public:
+  void Lock (void);
+  void Unlock (void);
+private:
+  SystemMutex m_eventMutex;
+};
+
+class RealtimeSimulatorImpl : public SimulatorImpl
+{
+public:
+  static TypeId GetTypeId (void);
+
+  /**
+   * Enumeration of the types of packets supported in the class.
+   *
+   */
+  enum SynchronizationMode {
+    SYNC_BEST_EFFORT, /** Make a best effort to keep synced to real-time */
+    SYNC_HARD_LIMIT, /** Keep to real-time within a tolerance or die trying */
+  };
+
+  RealtimeSimulatorImpl ();
+  ~RealtimeSimulatorImpl ();
+
+  void Destroy ();
+
+  void EnableLogTo (char const *filename);
+
+  bool IsFinished (void) const;
+  Time Next (void) const;
+  void Stop (void);
+  void Stop (Time const &time);
+  EventId Schedule (Time const &time, const Ptr<EventImpl> &event);
+  EventId ScheduleNow (const Ptr<EventImpl> &event);
+  EventId ScheduleDestroy (const Ptr<EventImpl> &event);
+  void Remove (const EventId &ev);
+  void Cancel (const EventId &ev);
+  bool IsExpired (const EventId &ev) const;
+  virtual void RunOneEvent (void);
+  void Run (void);
+  Time Now (void) const;
+  Time GetDelayLeft (const EventId &id) const;
+  Time GetMaximumSimulationTime (void) const;
+
+  void SetScheduler (Ptr<Scheduler> scheduler);
+  Ptr<Scheduler> GetScheduler (void) const;
+
+  void SetSynchronizationMode (RealtimeSimulatorImpl::SynchronizationMode mode);
+  RealtimeSimulatorImpl::SynchronizationMode GetSynchronizationMode (void) const;
+
+  void SetHardLimit (Time limit);
+  Time GetHardLimit (void) const;
+
+private:
+  bool Running (void) const;
+  bool Realtime (void) const;
+
+  void ProcessOneEvent (void);
+  uint64_t NextTs (void) const;
+
+  typedef std::list<EventId> DestroyEvents;
+  DestroyEvents m_destroyEvents;
+  uint64_t m_stopAt;
+  bool m_stop;
+  bool m_running;
+
+  // The following variables are protected using the m_mutex
+  Ptr<Scheduler> m_events;
+  int m_unscheduledEvents;
+  uint32_t m_uid;
+  uint32_t m_currentUid;
+  uint64_t m_currentTs;
+
+  mutable SystemMutex m_mutex;
+  RealtimeEventLock m_eventLock;
+
+  Ptr<Synchronizer> m_synchronizer;
+  /*
+   * In calls to Simulator::Now we have a basic choice to make.  We can either
+   * report back the time the simulator thinks it should be, or we can report 
+   * the time it actually is.
+   *
+   * The synchronizer will make an attempt to cause these two numbers to be as
+   * close as possible to each other, but they will never be exactly the same.
+   * We give the client a choice in this respect.  
+   *
+   * If the client sets m_reportSimulatedTime to true, the behavior will be that 
+   * the simulator runs as close as possible to real time, but reports back to the
+   * client that it is running at exactly real time, and consuming no real time 
+   * as each event executes.  This allows for deterministic execution times and
+   * repeatable trace files.
+   *
+   * If the client sets m_reportSimulatedTime to false, the behavior will be that 
+   * the simulator runs as close as possible to real time, and reports back to the
+   * client the real wall-clock time whenever it asks.  Real time will be consumed
+   * as each event executes.  This allows for non-deterministic execution times and
+   * real variations in event executions.  Simulation time will be influenced by
+   * variations in host process scheduling, for example.
+   */
+  bool m_reportSimulatedTime;
+
+  /**
+   * The policy to use if the simulation cannot keep synchronized to real-time.
+   */
+  SynchronizationMode m_synchronizationMode;
+
+  /**
+   * The maximum allowable drift from real-time in SYNC_HARD_LIMIT mode.
+   */
+  Time m_hardLimit;
+};
+
+} // namespace ns3
+
+#endif /* REALTIME_SIMULATOR_IMPL_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/simulator/synchronizer.cc	Tue Aug 26 15:34:57 2008 -0700
@@ -0,0 +1,158 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "synchronizer.h"
+
+namespace ns3 {
+
+TypeId 
+Synchronizer::GetTypeId (void)
+{
+  static TypeId tid = TypeId ("Synchronizer")
+    .SetParent<Object> ()
+    ;
+  return tid;
+}
+
+Synchronizer::Synchronizer ()
+  : m_realtimeOriginNano (0), m_simOriginNano (0)
+{
+}
+
+Synchronizer::~Synchronizer ()
+{
+}
+
+  bool
+Synchronizer::Realtime (void)
+{
+  return DoRealtime ();
+}
+
+  uint64_t
+Synchronizer::GetCurrentRealtime (void)
+{
+  return NanosecondToTimeStep (DoGetCurrentRealtime ());
+}
+
+  void
+Synchronizer::SetOrigin (uint64_t ts)
+{
+  m_simOriginNano = TimeStepToNanosecond (ts);
+  DoSetOrigin (m_simOriginNano);
+}
+
+  uint64_t
+Synchronizer::GetOrigin (void)
+{
+  return NanosecondToTimeStep (m_simOriginNano);
+}
+
+  int64_t
+Synchronizer::GetDrift (uint64_t ts)
+{
+  int64_t tDrift = DoGetDrift (TimeStepToNanosecond (ts));
+
+  if (tDrift < 0) 
+    {
+      return -NanosecondToTimeStep (-tDrift);
+    } else {
+      return NanosecondToTimeStep (tDrift);
+  }
+}
+
+  bool
+Synchronizer::Synchronize (uint64_t tsCurrent, uint64_t tsDelay)
+{
+  return DoSynchronize (TimeStepToNanosecond (tsCurrent), 
+    TimeStepToNanosecond (tsDelay));
+}
+
+  void
+Synchronizer::Signal (void)
+{
+  DoSignal ();
+}
+
+  void
+Synchronizer::SetCondition (bool cond)
+{
+  DoSetCondition (cond);
+}
+
+  void
+Synchronizer::EventStart (void)
+{
+  DoEventStart ();
+}
+
+  uint64_t
+Synchronizer::EventEnd (void)
+{
+  return NanosecondToTimeStep (DoEventEnd ());
+}
+
+  uint64_t
+Synchronizer::TimeStepToNanosecond (uint64_t ts)
+{
+  switch (TimeStepPrecision::Get ()) {
+  case TimeStepPrecision::S:
+    return ts * 1000000000;
+  case TimeStepPrecision::MS:
+    return ts * 1000000;
+  case TimeStepPrecision::US:
+    return ts * 1000;
+  case TimeStepPrecision::NS:
+    return ts;
+  case TimeStepPrecision::PS:
+    return ts / 1000;
+  case TimeStepPrecision::FS:
+    return ts / 1000000;
+  default:
+    NS_ASSERT_MSG (false, "Synchronizer::TimeStepToNanosecond: "
+        "Unexpected precision not implemented");
+    return 0;
+  }
+}
+
+  uint64_t
+Synchronizer::NanosecondToTimeStep (uint64_t ns)
+{
+  switch (TimeStepPrecision::Get ()) {
+  case TimeStepPrecision::S:
+    return ns / 1000000000;
+  case TimeStepPrecision::MS:
+    return ns / 1000000;
+  case TimeStepPrecision::US:
+    return ns / 1000;
+  case TimeStepPrecision::NS:
+    return ns;
+  case TimeStepPrecision::PS:
+    return ns * 1000;
+  case TimeStepPrecision::FS:
+    return ns * 1000000;
+  default:
+    NS_ASSERT_MSG (false, "Synchronizer::NanosecondToTimeStep: "
+        "Unexpected precision not implemented");
+    return 0;
+  }
+}
+
+}; // namespace ns3
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/simulator/synchronizer.h	Tue Aug 26 15:34:57 2008 -0700
@@ -0,0 +1,334 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef SYNCHRONIZER_H
+#define SYNCHRONIZER_H
+
+#include <stdint.h>
+#include "nstime.h"
+#include "ns3/object.h"
+
+namespace ns3 {
+
+/**
+ * @brief Base class used for synchronizing the simulation events to some
+ * real time "wall clock."
+ *
+ * The simulation clock is maintained as a 64-bit integer in a unit specified
+ * by the user through the TimeStepPrecision::Set function. This means that
+ * it is not possible to specify event expiration times with anything better
+ * than this user-specified accuracy.  We use this clock for the simulation
+ * time.
+ *
+ * The real-time clock is maintained as a 64-bit integer count of nanoseconds.
+ *
+ * The synchronization between the simulation clock and the real-time clock
+ * is maintained using a combination of sleep-waiting, busy-waiting and a
+ * feedback loop.
+ */
+class Synchronizer : public Object 
+{
+public:
+  static TypeId GetTypeId (void);
+
+  Synchronizer ();
+  virtual ~Synchronizer ();
+
+/**
+ * @brief Return true if this synchronizer is actually synchronizing to a
+ * realtime clock.  The simulator sometimes needs to know this.
+ * @returns True if locked with realtime, false if not.
+ */
+  bool Realtime (void);
+
+/**
+ * @brief Retrieve the value of the origin of the underlying normalized wall
+ * clock time in simulator timestep units.
+ *
+ * @returns The normalized wall clock time (in simulator timestep units).
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::SetOrigin
+ */
+  uint64_t GetCurrentRealtime (void);
+
+/**
+ * @brief Establish a correspondence between a simulation time and the
+ * synchronizer real time.
+ *
+ * This method is expected to be called at the "instant" before simulation
+ * begins.  At this point, simulation time = 0, and a 
+ * set = 0 in this method.  We then associate this time with the current
+ * value of the real time clock that will be used to actually perform the
+ * synchronization.
+ *
+ * Subclasses are expected to implement the corresponding DoSetOrigin pure
+ * virtual method to do the actual real-time-clock-specific work of making the 
+ * correspondence mentioned above.
+ *
+ * @param ts The simulation time we should use as the origin (in simulator
+ * timestep units).
+ * @see TimeStepPrecision::Get
+ * @see TimeStepPrecision::DoSetOrigin
+ */
+  void SetOrigin (uint64_t ts);
+
+/**
+ * @brief Retrieve the value of the origin of the simulation time in 
+ * simulator timestep units.
+ *
+ * @returns The simulation time used as the origin (in simulator timestep
+ * units).
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::SetOrigin
+ */
+  uint64_t GetOrigin (void);
+
+/**
+ * @brief Retrieve the difference between the real time clock used to 
+ * synchronize the simulation and the simulation time (in simulator timestep
+ * units).
+ *
+ * @param ts Simulation timestep from the simulator interpreted as current time
+ * in the simulator.
+ * @returns Simulation timestep (in simulator timestep units) minus origin 
+ * time (stored internally in nanosecond units).
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::SetOrigin
+ * @see Synchronizer::DoGetDrift
+ */
+  int64_t GetDrift (uint64_t ts);
+
+/**
+ * @brief Wait until the real time is in sync with the specified simulation
+ * time or until the synchronizer is Sigalled.
+ *
+ * This is where the real work of synchronization is done.  The Time passed
+ * in as a parameter is the simulation time.  The job of Synchronize is to
+ * translate from simulation time to synchronizer time (in a perfect world
+ * this is the same time) and then figure out how long in real-time it needs
+ * to wait until that synchronizer / simulation time comes around.
+ *
+ * Subclasses are expected to implement the corresponding DoSynchronize pure
+ * virtual method to do the actual real-time-clock-specific work of waiting 
+ * (either busy-waiting or sleeping, or some combination thereof) until the
+ * requested simulation time.
+ *
+ * @param tsCurrent The current simulation time (in simulator timestep units).
+ * @param tsDelay The simulation time we need to wait for (in simulator
+ * timestep units).
+ * @returns True if the function ran to completion, false if it was interrupted
+ * by a Signal.
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::DoSynchronize
+ * @see Synchronizer::Signal
+ */
+  bool Synchronize (uint64_t tsCurrent, uint64_t tsDelay);
+
+/**
+ * @brief Tell a posible simulator thread waiting in the Synchronize method
+ * that an event has happened which demands a reevaluation of the wait time.
+ * This will cause the thread to wake and return to the simulator proper
+ * where it can get its bearings.
+ *
+ * @see Synchronizer::Synchronize
+ * @see Synchronizer::DoSignal
+ */
+  void Signal (void);
+
+/**
+ * @brief Set the condition variable that tells a posible simulator thread 
+ * waiting in the Synchronize method that an event has happened which demands
+ * a reevaluation of the wait time.
+ *
+ * @see Synchronizer::Signal
+ */
+  void SetCondition (bool);
+
+/**
+ * @brief Ask the synchronizer to remember what time it is.  Typically used
+ * with EventEnd to determine the real execution time of a simulation event.
+ *
+ * @see Synchronizer::EventEnd
+ * @see TimeStepPrecision::Get
+ */
+  void EventStart (void);
+
+/**
+ * @brief Ask the synchronizer to return the time step between the instant
+ * remembered during EventStart and now.  Used in conjunction with EventStart
+ * to determine the real execution time of a simulation event.
+ *
+ * @see Synchronizer::EventStart
+ * @see TimeStepPrecision::Get
+ */
+  uint64_t EventEnd (void);
+
+protected:
+/**
+ * @brief Establish a correspondence between a simulation time and a 
+ * wall-clock (real) time.
+ *
+ * @internal
+ *
+ * There are three timelines involved here:  the simulation time, the 
+ * (absolute) wall-clock time and the (relative) synchronizer real time.
+ * Calling this method makes a correspondence between the origin of the
+ * synchronizer time and the current wall-clock time.
+ *
+ * This method is expected to be called at the "instant" before simulation
+ * begins.  At this point, simulation time = 0, and synchronizer time is
+ * set = 0 in this method.  We then associate this time with the current
+ * value of the real time clock that will be used to actually perform the
+ * synchronization.
+ *
+ * Subclasses are expected to implement this method to do the actual 
+ * real-time-clock-specific work of making the correspondence mentioned above.
+ * for example, this is where the differences between Time parameters and
+ * parameters to clock_nanosleep would be dealt with. 
+ *
+ * @param ns The simulation time we need to use as the origin (normalized to
+ * nanosecond units).
+ * @see Synchronizer::SetOrigin
+ * @see TimeStepPrecision::Get
+ */
+  virtual void DoSetOrigin (uint64_t ns) = 0;
+
+/**
+ * @brief Return true if this synchronizer is actually synchronizing to a
+ * realtime clock.  The simulator sometimes needs to know this.
+ *
+ * @internal
+ *
+ * Subclasses are expected to implement this method to tell the outside world
+ * whether or not they are synchronizing to a realtime clock.
+ *
+ * @returns True if locked with realtime, false if not.
+ */
+  virtual bool DoRealtime (void) = 0;
+
+/**
+ * @brief Retrieve the value of the origin of the underlying normalized wall
+ * clock time in simulator timestep units.
+ *
+ * @internal
+ *
+ * Subclasses are expected to implement this method to do the actual
+ * real-time-clock-specific work of getting the current time.
+ *
+ * @returns The normalized wall clock time (in nanosecond units).
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::SetOrigin
+ */
+  virtual uint64_t DoGetCurrentRealtime (void) = 0;
+
+/**
+ * @brief Wait until the real time is in sync with the specified simulation
+ * time.
+ *
+ * @internal
+ *
+ * This is where the real work of synchronization is done.  The Time passed
+ * in as a parameter is the simulation time.  The job of Synchronize is to
+ * translate from simulation time to synchronizer time (in a perfect world
+ * this is the same time) and then figure out how long in real-time it needs
+ * to wait until that synchronizer / simulation time comes around.
+ *
+ * Subclasses are expected to implement this method to do the actual
+ * real-time-clock-specific work of waiting (either busy-waiting or sleeping,
+ * or some combination) until the requested simulation time.
+ *
+ * @param nsCurrent The current simulation time (normalized to nanosecond
+ * units).
+ * @param nsDelay The simulation time we need to wait for (normalized to 
+ * nanosecond units).
+ * @returns True if the function ran to completion, false if it was interrupted
+ * by a Signal.
+ * @see Synchronizer::Synchronize
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::Signal
+ */
+  virtual bool DoSynchronize (uint64_t nsCurrent, uint64_t nsDelay) = 0;
+
+/**
+ * @brief Declaration of the method used to tell a posible simulator thread 
+ * waiting in the DoSynchronize method that an event has happened which
+ * demands a reevaluation of the wait time.
+ *
+ * @see Synchronizer::Signal
+ */
+  virtual void DoSignal (void) = 0;
+
+/**
+ * @brief Declaration of the method used to set the condition variable that 
+ * tells a posible simulator thread waiting in the Synchronize method that an
+ * event has happened which demands a reevaluation of the wait time.
+ *
+ * @see Synchronizer::SetCondition
+ */
+  virtual void DoSetCondition (bool) = 0;
+
+/**
+ * @brief Declaration of method used to retrieve drift between the real time
+ * clock used to synchronize the simulation and the current simulation time.
+ *
+ * @internal
+ *
+ * @param ns Simulation timestep from the simulator normalized to nanosecond 
+ * steps.
+ * @returns Drift in nanosecond units.
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::SetOrigin
+ * @see Synchronizer::GetDrift
+ */
+  virtual int64_t DoGetDrift (uint64_t ns) = 0;
+
+  virtual void DoEventStart (void) = 0;
+  virtual uint64_t DoEventEnd (void) = 0;
+
+  uint64_t m_realtimeOriginNano;
+  uint64_t m_simOriginNano;
+
+private:
+/**
+ * @brief Convert a simulator time step (which can be steps of time in a 
+ * user-specified unit) to a normalized time step in nanosecond units.
+ *
+ * @internal
+ *
+ * @param ts The simulation time step to be normalized.
+ * @returns The simulation time step normalized to nanosecond units.
+ * @see TimeStepPrecision::Get
+ */
+  uint64_t TimeStepToNanosecond (uint64_t ts);
+
+/**
+ * @brief Convert a normalized nanosecond count into a simulator time step
+ * (which can be steps of time in a user-specified unit).
+ *
+ * @internal
+ *
+ * @param ns The nanosecond count step to be converted
+ * @returns The simulation time step to be interpreted in appropriate units.
+ * @see TimeStepPrecision::Get
+ */
+  uint64_t NanosecondToTimeStep (uint64_t ns);
+};
+
+}; // namespace ns3
+
+#endif /* SYNCHRONIZER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/simulator/wall-clock-synchronizer.cc	Tue Aug 26 15:34:57 2008 -0700
@@ -0,0 +1,490 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <time.h>
+#include <sys/time.h>
+#include <sched.h>
+
+#include "ns3/log.h"
+#include "ns3/system-condition.h"
+
+#include "wall-clock-synchronizer.h"
+
+NS_LOG_COMPONENT_DEFINE ("WallClockSynchronizer");
+
+namespace ns3 {
+
+WallClockSynchronizer::WallClockSynchronizer ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+//
+// In Linux, the basic timekeeping unit is derived from a variable called HZ
+// HZ is the frequency in hertz of the system timer.  The system timer fires 
+// every 1/HZ seconds and a counter, called the jiffies counter is incremented
+// at each tick.  The time between ticks is called a jiffy (American slang for
+// a short period of time).  The ticking of the jiffies counter is how the
+// the kernel tells time.
+//
+// Now, the shortest time the kernel can sleep is one jiffy since a timer
+// has to be set to expire and trigger the process to be made ready.  The
+// Posix clock CLOCK_REALTIME is defined as a 1/HZ clock, so by doing a
+// clock_getres () on the realtime clock we can infer the scheduler quantum
+// and the minimimum sleep time for the system.  This is most certainly NOT
+// going to be one nanosecond even though clock_nanosleep () pretends it is.
+//
+// The reason this number is important is that we are going to schedule lots
+// of waits for less time than a jiffy.  The clock_nanosleep function is
+// going to guarantee that it will sleep for AT LEAST the time specified.
+// The least time that it will sleep is a jiffy.
+//
+// In order to deal with this, we are going to do a spin-wait if the simulator
+// requires a delay less than a jiffy.  This is on the order of one millisecond
+// (999848 ns) on the ns-regression machine.
+// 
+  struct timespec ts;
+  clock_getres (CLOCK_REALTIME, &ts);
+  m_jiffy = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
+  NS_LOG_INFO ("Jiffy is " << m_jiffy << " ns");
+
+#if 0
+//
+// DANGER DANGER WILL ROBINSON
+//
+// Don't enable this code, su root and run a sim unless you really know what
+// you are doing.
+//
+  struct sched_param sp;
+  sp.sched_priority = sched_get_priority_max (SCHED_FIFO);
+  sched_setscheduler (0, SCHED_FIFO, &sp);
+#endif
+}
+
+WallClockSynchronizer::~WallClockSynchronizer ()
+{
+  NS_LOG_FUNCTION_NOARGS ();
+}
+
+  bool
+WallClockSynchronizer::DoRealtime (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return true;
+}
+
+  uint64_t
+WallClockSynchronizer::DoGetCurrentRealtime (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return GetNormalizedRealtime ();
+}
+
+  void
+WallClockSynchronizer::DoSetOrigin (uint64_t ns)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+//
+// In order to make sure we're really locking the simulation time to some 
+// wall-clock time, we need to be able to compare that simulation time to
+// that wall-clock time.  The wall clock will have been running for some
+// long time and will probably have a huge count of nanoseconds in it.  We
+// save the real time away so we can subtract it from "now" later and get
+// a count of nanoseconds in real time since the simulation started.
+//
+  m_realtimeOriginNano = GetRealtime ();
+  NS_LOG_INFO ("origin = " << m_realtimeOriginNano);
+}
+
+  int64_t
+WallClockSynchronizer::DoGetDrift (uint64_t ns)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+//
+// In order to make sure we're really locking the simulation time to some 
+// wall-clock time, we need to be able to compare that simulation time to
+// that wall-clock time.  In DoSetOrigin we saved the real time at the start
+// of the simulation away.  This is the place where we subtract it from "now"
+// to a count of nanoseconds in real time since the simulation started.  We
+// then subtract the current real time in normalized nanoseconds we just got
+// from the normalized simulation time in nanoseconds that is passed in as
+// the parameter ns.  We return an integer difference, but in reality all of
+// the mechanisms that cause wall-clock to simuator time drift cause events
+// to be late.  That means that the wall-clock will be higher than the 
+// simulation time and drift will be positive.  I would be astonished to 
+// see a negative drift, but the possibility is admitted for other 
+// implementations; and we'll use the ability to report an early result in
+// DoSynchronize below.
+//
+  uint64_t nsNow = GetNormalizedRealtime ();
+
+  if (nsNow > ns)
+    {
+//
+// Real time (nsNow) is larger/later than the simulator time (ns).  We are
+// behind real time and the difference (drift) is positive.
+//
+      return (int64_t)(nsNow - ns);
+    }
+  else
+    {
+// 
+// Real time (nsNow) is smaller/earlier than the simulator time (ns).  We are
+// ahead of real time and the difference (drift) is negative.
+//
+      return -(int64_t)(ns - nsNow);
+    }
+}
+
+  bool
+WallClockSynchronizer::DoSynchronize (uint64_t nsCurrent, uint64_t nsDelay)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+//
+// This is the belly of the beast.  We have received two parameters from the
+// simulator proper -- a current simulation time (nsCurrent) and a simulation
+// time to delay which identifies the time the next event is supposed to fire.
+//
+// The first thing we need to do is to (try and) correct for any realtime 
+// drift that has happened in the system.  In this implementation, we realize 
+// that all mechanisms for drift will cause the drift to be such that the 
+// realtime is greater than the simulation time.  This typically happens when 
+// our process is put to sleep for a given time, but actually sleeps for
+// longer.  So, what we want to do is to "catch up" to realtime and delay for
+// less time than we are actually asked to delay.  DriftCorrect will return a 
+// number from 0 to nsDelay corresponding to the amount of catching-up we
+// need to do.  If we are more than nsDelay behind, we do not wait at all.
+//
+// Note that it will be impossible to catch up if the amount of drift is 
+// cumulatively greater than the amount of delay between events.  The method
+// GetDrift () is available to clients of the syncrhonizer to keep track of
+// the cumulative drift.  The client can assert if the drift gets out of 
+// hand, print warning messages, or just ignore the situation and hope it will
+// go away.
+//
+  uint64_t ns = DriftCorrect (nsCurrent, nsDelay);
+  NS_LOG_INFO ("Synchronize ns = " << ns);
+//
+// Once we've decided on how long we need to delay, we need to split this
+// time into sleep waits and busy waits.  The reason for this is described
+// in the comments for the constructor where jiffies and jiffy resolution is
+// explained.
+//
+// Here, I'll just say that we need that the jiffy is the minimum resolution 
+// of the system clock.  It can only sleep in blocks of time equal to a jiffy.
+// If we want to be more accurate than a jiffy (we do) then we need to sleep
+// for some number of jiffies and then busy wait for any leftover time.
+//
+  uint64_t numberJiffies = ns / m_jiffy;
+  NS_LOG_INFO ("Synchronize numberJiffies = " << numberJiffies);
+//
+// This is where the real world interjects its very ugly head.  The code 
+// immediately below reflects the fact that a sleep is actually quite probably
+// going to end up sleeping for some number of jiffies longer than you wanted.
+// This is because your system is going to be off doing other unimportant 
+// stuff during that extra time like running file systems and networks.  What
+// we want to do is to ask the system to sleep enough less than the requested
+// delay so that it comes back early most of the time (coming back early is
+// fine, coming back late is bad).  If we can convince the system to come back
+// early (most of the time), then we can busy-wait until the requested
+// completion time actually comes around (most of the time).
+//
+// The tradeoff here is, of course, that the less time we spend sleeping, the
+// more accurately we will sync up; but the more CPU time we will spend busy
+// waiting (doing nothing).
+//
+// I'm not really sure about this number -- a boss of mine once said, "pick
+// a number and it'll be wrong."  But this works for now.
+//
+// XXX BUGBUG Hardcoded tunable parameter below.
+//
+  if (numberJiffies > 3)
+    {
+      NS_LOG_INFO ("SleepWait for " << numberJiffies * m_jiffy << " ns");
+      NS_LOG_INFO ("SleepWait until " << nsCurrent + numberJiffies * m_jiffy 
+        << " ns");
+//
+// SleepWait is interruptible.  If it returns true it meant that the sleep
+// went until the end.  If it returns false, it means that the sleep was 
+// interrupted by a Signal.  In this case, we need to return and let the 
+// simulator re-evaluate what to do.
+//
+      if (SleepWait ((numberJiffies - 3) * m_jiffy) == false)
+        {
+          NS_LOG_INFO ("SleepWait interrupted");
+          return false;
+        }
+    }
+  NS_LOG_INFO ("Done with SleepWait");
+//
+// We asked the system to sleep for some number of jiffies, but that doesn't 
+// mean we actually did.  Let's re-evaluate what we need to do here.  Maybe 
+// we're already late.  Probably the "real" delay time left has little to do
+// with what we would calculate it to be naively.
+//
+// We are now at some Realtime.  The important question now is not, "what
+// would we calculate in a mathematicians paradise," it is, "how many
+// nanoseconds do we need to busy-wait until we get to the Realtime that
+// corresponds to nsCurrent + nsDelay (in simulation time).  We have a handy
+// function to do just that -- we ask for the time the realtime clock has
+// drifted away from the simulation clock.  That's our answer.  If the drift
+// is negative, we're early and we need to busy wait for that number of 
+// nanoseconds.  The place were we want to be is described by the parameters
+// we were passed by the simulator.
+//
+  int64_t nsDrift = DoGetDrift (nsCurrent + nsDelay);
+//
+// If the drift is positive, we are already late and we need to just bail out
+// of here as fast as we can.  Return true to indicate that the requested time
+// has, in fact, passed.
+//
+  if (nsDrift >= 0)
+    {
+      NS_LOG_INFO ("Back from SleepWait: IML8 " << nsDrift);
+      return true;
+    }
+//
+// There are some number of nanoseconds left over and we need to wait until
+// the time defined by nsDrift.  We'll do a SpinWait since the usual case 
+// will be that we are doing this Spinwait after we've gotten a rough delay
+// using the SleepWait above.  If SpinWait completes to the end, it will 
+// return true; if it is interrupted by a signal it will return false.
+//
+  NS_LOG_INFO ("SpinWait until " << nsCurrent + nsDelay);
+  return SpinWait (nsCurrent + nsDelay);
+}
+
+  void
+WallClockSynchronizer::DoSignal (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+
+  m_condition.SetCondition (true);
+  m_condition.Signal ();
+}
+
+  void
+WallClockSynchronizer::DoSetCondition (bool cond)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_condition.SetCondition (cond);
+}
+
+  void
+WallClockSynchronizer::DoEventStart (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  m_nsEventStart = GetNormalizedRealtime ();
+}
+
+  uint64_t
+WallClockSynchronizer::DoEventEnd (void)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+  return GetNormalizedRealtime () - m_nsEventStart;
+}
+
+  bool
+WallClockSynchronizer::SpinWait (uint64_t ns)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+//
+// Do a busy-wait until the normalized realtime equals the value passed in
+// or the condition variable becomes true.  The condition becomes true if
+// an outside entity (a network device receives a packet, sets the condition
+// and signals the scheduler it needs to re-evaluate).
+// 
+// We just sit here and spin, wasting CPU cycles until we get to the right
+// time or are told to leave.
+//
+  for (;;) 
+    {
+      if (GetNormalizedRealtime () >= ns)
+        {
+          return true;
+        }
+      if (m_condition.GetCondition ())
+        {
+          return false;
+        }
+    }
+// Quiet compiler
+  return true;
+}
+
+  bool
+WallClockSynchronizer::SleepWait (uint64_t ns)
+{
+  NS_LOG_FUNCTION_NOARGS ();
+//
+// Put our process to sleep for some number of nanoseconds.  Typically this
+// will be some time equal to an integral number of jiffies.  We will usually
+// follow a call to SleepWait with a call to SpinWait to get the kind of
+// accuracy we want.
+//
+// We have to have some mechanism to wake up this sleep in case an external
+// event happens that causes a schedule event in the simulator.  This newly
+// scheduled event might be before the time we are waiting until, so we have
+// to break out of both the SleepWait and the following SpinWait to go back
+// and reschedule/resynchronize taking the new event into account.  The 
+// SystemCondition we have saved in m_condition takes care of this for us.
+//
+// This call will return if the timeout expires OR if the condition is 
+// set true by a call to WallClockSynchronizer::SetCondition (true) followed
+// by a call to WallClockSynchronizer::Signal().  In either case, we are done
+// waiting.  If the timeout happened, we TimedWait returns true; if a Signal
+// happened, false.
+//
+  return m_condition.TimedWait (ns);
+}
+
+  uint64_t
+WallClockSynchronizer::DriftCorrect (uint64_t nsNow, uint64_t nsDelay)
+{
+  int64_t drift = DoGetDrift (nsNow);
+//
+// If we're running late, drift will be positive and we need to correct by
+// delaying for less time.  If we're early for some bizarre reason, we don't
+// do anything since we'll almost instantly self-correct.
+//
+  if (drift < 0)
+    {
+      return nsDelay;
+    }
+//
+// If we've drifted out of sync by less than the requested delay, then just
+// subtract the drift from the delay and fix up the drift in one go.  If we
+// have more drift than delay, then we just play catch up as fast as possible
+// by not delaying at all.
+//
+  uint64_t correction = (uint64_t)drift;
+  if (correction <= nsDelay)
+    {
+      return nsDelay - correction;
+    }
+  else
+    {
+      return 0;
+    }
+}
+
+  uint64_t
+WallClockSynchronizer::GetRealtime (void)
+{
+//
+// I originally wrote this code to use CLOCK_PROCESS_CPUTIME_ID.  In Linux
+// this is a highly accurate realtime clock.  It turns out, though, that this
+// is a Linux bug.  This is supposed to be (posix says it is) a highly 
+// accurate cumulative running time of the process.  In Linux it is (or at
+// least was -- a bug is filed) a  highly accurate wall-clock time since the
+// process was created.  Posix defines the wall clock you must use as the
+// CLOCK_REALTIME clock.  As you can see in the constructor, the resolution
+// of the CLOCK_REALTIME clock is a jiffy.  This is not fine-grained enough
+// for us.  So, I'm using the gettimeofday clock even though it is an
+// expensive call.
+//
+// I could write some inline assembler here to get to the timestamp counter
+// (RDTSC instruction).  It's not as trivial as it sounds to get right.  
+// Google for "rdtsc cpuid" to see why.  I'm leaving this for another day.
+//
+// N.B. This returns the value of the realtime clock.  This does not return
+// the current normalized realtime that we are attempting to make equal to
+// the simulation clock.  To get that number, use GetNormalizedRealtime ().
+//
+
+#if 0
+  // This will eventually stop working in linux so don't use it
+  struct timespec tsNow;
+  clock_gettime (CLOCK_REALTIME, &tsNow);
+  return TimespecToNs (&tsNow);
+#endif
+
+  struct timeval tvNow;
+  gettimeofday (&tvNow, NULL);
+  return TimevalToNs (&tvNow);
+}
+
+  uint64_t
+WallClockSynchronizer::GetNormalizedRealtime (void)
+{
+  return GetRealtime () - m_realtimeOriginNano;
+}
+
+  void
+WallClockSynchronizer::NsToTimespec (int64_t ns, struct timespec *ts)
+{
+  NS_ASSERT ((ns % US_PER_NS) == 0);
+  ts->tv_sec = ns / NS_PER_SEC;
+  ts->tv_nsec = ns % NS_PER_SEC;
+}
+
+  void
+WallClockSynchronizer::NsToTimeval (int64_t ns, struct timeval *tv)
+{
+  NS_ASSERT ((ns % US_PER_NS) == 0);
+  tv->tv_sec = ns / NS_PER_SEC;
+  tv->tv_usec = (ns % NS_PER_SEC) / US_PER_NS;
+}
+
+  uint64_t
+WallClockSynchronizer::TimespecToNs (struct timespec *ts)
+{
+  uint64_t nsResult = ts->tv_sec * NS_PER_SEC + ts->tv_nsec;
+  NS_ASSERT ((nsResult % US_PER_NS) == 0);
+  return nsResult;
+}
+
+  uint64_t
+WallClockSynchronizer::TimevalToNs (struct timeval *tv)
+{
+  uint64_t nsResult = tv->tv_sec * NS_PER_SEC + tv->tv_usec * US_PER_NS;
+  NS_ASSERT ((nsResult % US_PER_NS) == 0);
+  return nsResult;
+}
+
+  void
+WallClockSynchronizer::TimespecAdd (
+  struct timespec *ts1, 
+  struct timespec *ts2,
+  struct timespec *result)
+{
+  result->tv_sec = ts1->tv_sec + ts2->tv_sec;
+  result->tv_nsec = ts1->tv_nsec + ts2->tv_nsec;
+  if (result->tv_nsec > (int64_t)NS_PER_SEC)
+    {
+      ++result->tv_sec;
+      result->tv_nsec %= NS_PER_SEC;
+    }
+}
+
+  void
+WallClockSynchronizer::TimevalAdd (
+  struct timeval *tv1, 
+  struct timeval *tv2,
+  struct timeval *result)
+{
+  result->tv_sec = tv1->tv_sec + tv2->tv_sec;
+  result->tv_usec = tv1->tv_usec + tv2->tv_usec;
+  if (result->tv_usec > (int64_t)US_PER_SEC)
+    {
+      ++result->tv_sec;
+      result->tv_usec %= US_PER_SEC;
+    }
+}
+
+}; // namespace ns3
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/simulator/wall-clock-synchronizer.h	Tue Aug 26 15:34:57 2008 -0700
@@ -0,0 +1,204 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2008 University of Washington
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef WALL_CLOCK_CLOCK_SYNCHRONIZER_H
+#define WALL_CLOCK_CLOCK_SYNCHRONIZER_H
+
+#include "ns3/system-condition.h"
+#include "synchronizer.h"
+
+namespace ns3 {
+
+/**
+ * @brief Class used for synchronizing the simulation events to a real-time
+ * "wall clock" using Posix Clock functions.
+ *
+ * Enable this synchronizer using:
+ *
+ *   DefaultValue::Bind ("Synchronizer", "WallClockSynchronizer");
+ *
+ * before calling any simulator functions.
+ *
+ * The simulation clock is maintained as a 64-bit integer in a unit specified
+ * by the user through the TimeStepPrecision::Set function. This means that
+ * it is not possible to specify event expiration times with anything better
+ * than this user-specified accuracy.  
+ *
+ * There are a couple of more issues at this level.  Posix clocks provide
+ * access to several clocks we could use as a wall clock.  We don't care about
+ * time in the sense of 0430 CEST, we care about some piece of hardware that
+ * ticks at some regular period.  The most accurate posix clock in this
+ * respect is the CLOCK_PROCESS_CPUTIME_ID clock.  This is a high-resolution
+ * register in the CPU.  For example, on Intel machines this corresponds to
+ * the timestamp counter (TSC) register.  The resolution of this counter will
+ * be on the order of nanoseconds.
+ *
+ * Now, just because we can measure time in nanoseconds doesn't mean we can
+ * put our process to sleep to nanosecond resolution.  We are eventualy going
+ * to use the function clock_nanosleep () to sleep until a simulation Time
+ * specified by the caller. 
+ *
+ * MORE ON JIFFIES, SLEEP, PROCESSES, etc., as required
+ *
+ * Nanosleep takes a struct timespec as an input so we have to deal with
+ * conversion between Time and struct timespec here.  They are both 
+ * interpreted as elapsed times.
+ */
+class WallClockSynchronizer : public Synchronizer
+{
+public:
+  WallClockSynchronizer ();
+  virtual ~WallClockSynchronizer ();
+
+  static const uint64_t US_PER_NS = (uint64_t)1000;
+  static const uint64_t US_PER_SEC = (uint64_t)1000000;
+  static const uint64_t NS_PER_SEC = (uint64_t)1000000000;
+
+protected:
+/**
+ * @brief Return true if this synchronizer is actually synchronizing to a
+ * realtime clock.  The simulator sometimes needs to know this.
+ *
+ * @internal
+ *
+ * Subclasses are expected to implement this method to tell the outside world
+ * whether or not they are synchronizing to a realtime clock.
+ *
+ * @returns True if locked with realtime, false if not.
+ */
+  virtual bool DoRealtime (void);
+
+/**
+ * @brief Retrieve the value of the origin of the underlying normalized wall
+ * clock time in nanosecond units.
+ *
+ * @internal
+ *
+ * Subclasses are expected to implement this method to do the actual
+ * real-time-clock-specific work of getting the current time.
+ *
+ * @returns The normalized wall clock time (in nanosecond units).
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::SetOrigin
+ */
+  virtual uint64_t DoGetCurrentRealtime (void);
+
+/**
+ * @brief Establish a correspondence between a simulation time and a 
+ * wall-clock (real) time.
+ *
+ * @internal
+ *
+ * There are three timelines involved here:  the simulation time, the 
+ * (absolute) wall-clock time and the (relative) synchronizer real time.
+ * Calling this method makes a correspondence between the origin of the
+ * synchronizer time and the current wall-clock time.
+ *
+ * This method is expected to be called at the "instant" before simulation
+ * begins.  At this point, simulation time = 0, and synchronizer time is
+ * set = 0 in this method.  We then associate this time with the current
+ * value of the real time clock that will be used to actually perform the
+ * synchronization.
+ *
+ * Subclasses are expected to implement this method to do the actual 
+ * real-time-clock-specific work of making the correspondence mentioned above.
+ * for example, this is where the differences between Time parameters and
+ * parameters to clock_nanosleep would be dealt with. 
+ *
+ * @param ns The simulation time we need to use as the origin (normalized to
+ * nanosecond units).
+ */
+  virtual void DoSetOrigin (uint64_t ns);
+
+/**
+ * @brief Declaration of method used to retrieve drift between the real time
+ * clock used to synchronize the simulation and the current simulation time.
+ *
+ * @internal
+ *
+ * @param ns Simulation timestep from the simulator normalized to nanosecond 
+ * steps.
+ * @returns Drift in nanosecond units.
+ * @see TimeStepPrecision::Get
+ * @see Synchronizer::SetOrigin
+ * @see Synchronizer::GetDrift
+ */
+  virtual int64_t DoGetDrift (uint64_t ns);
+
+/**
+ * @brief Wait until the real time is in sync with the specified simulation
+ * time.
+ *
+ * @internal
+ *
+ * This is where the real work of synchronization is done.  The Time passed
+ * in as a parameter is the simulation time.  The job of Synchronize is to
+ * translate from simulation time to synchronizer time (in a perfect world
+ * this is the same time) and then figure out how long in real-time it needs
+ * to wait until that synchronizer / simulation time comes around.
+ *
+ * Subclasses are expected to implement this method to do the actual
+ * real-time-clock-specific work of waiting (either busy-waiting or sleeping,
+ * or some combination) until the requested simulation time.
+ *
+ * @param ns The simulation time we need to wait for (normalized to nanosecond
+ * units).
+ * @see TimeStepPrecision::Get
+ */
+  virtual bool DoSynchronize (uint64_t nsCurrent, uint64_t nsDelay);
+  virtual void DoSignal (void);
+  virtual void DoSetCondition (bool cond);
+
+  virtual void DoEventStart (void);
+  virtual uint64_t DoEventEnd (void);
+
+  bool SpinWait (uint64_t);
+  bool SleepWait (uint64_t);
+
+  uint64_t DriftCorrect (uint64_t nsNow, uint64_t nsDelay);
+
+  uint64_t GetRealtime (void);
+  uint64_t GetNormalizedRealtime (void);
+
+  void NsToTimespec (int64_t ns, struct timespec *ts);
+  void NsToTimeval (int64_t ns, struct timeval *tv);
+
+  uint64_t TimespecToNs (struct timespec *ts);
+  uint64_t TimevalToNs (struct timeval *tv);
+
+  void TimespecAdd(
+    struct timespec *ts1, 
+    struct timespec *ts2, 
+    struct timespec *result);
+
+  void TimevalAdd (
+    struct timeval *tv1, 
+    struct timeval *tv2,
+    struct timeval *result);
+
+  uint64_t m_cpuTick;
+  uint64_t m_realtimeTick;
+  uint64_t m_jiffy;
+  uint64_t m_nsEventStart;
+
+  SystemCondition m_condition;
+};
+
+}; // namespace ns3
+
+#endif /* WALL_CLOCK_SYNCHRONIZER_H */
--- a/src/simulator/wscript	Tue Aug 26 08:42:28 2008 -0700
+++ b/src/simulator/wscript	Tue Aug 26 15:34:57 2008 -0700
@@ -22,7 +22,7 @@
     else:
         conf.env['USE_HIGH_PRECISION_DOUBLE'] = 0
         highprec = '128-bit integer'
-    conf.check_message_custom('high precision time', 'implementation', highprec)
+    conf.check_message_custom('high precision time','implementation',highprec)
 
     e = conf.create_header_configurator()
     e.mandatory = False
@@ -59,8 +59,11 @@
         'event-impl.cc',
         'simulator.cc',
         'default-simulator-impl.cc',
+        'realtime-simulator-impl.cc',
         'timer.cc',
         'watchdog.cc',
+        'synchronizer.cc',
+        'wall-clock-synchronizer.cc',
         ]
 
     headers = bld.create_obj('ns3header')
@@ -73,6 +76,7 @@
         'simulator.h',
         'simulator-impl.h',
         'default-simulator-impl.h',
+        'realtime-simulator-impl.h',
         'scheduler.h',
         'list-scheduler.h',
         'map-scheduler.h',
@@ -81,6 +85,8 @@
         'timer.h',
         'timer-impl.h',
         'watchdog.h',
+        'synchronizer.h',
+        'wall-clock-synchronizer.h',
         ]
 
     env = bld.env_of_name('default')