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')