--- a/src/core/model/nstime.h Sat Dec 08 10:11:07 2012 -0800
+++ b/src/core/model/nstime.h Sat Dec 08 21:23:44 2012 -0800
@@ -26,6 +26,7 @@
#include "int64x64.h"
#include <stdint.h>
#include <cmath>
+#include <set>
#include <ostream>
namespace ns3 {
@@ -36,115 +37,48 @@
*/
/**
* \ingroup time
- * \brief keep track of time unit.
- *
- * This template class is used to keep track of the value
- * of a specific time unit: the type TimeUnit<1> is used to
- * keep track of seconds, the type TimeUnit<2> is used to keep
- * track of seconds squared, the type TimeUnit<-1> is used to
- * keep track of 1/seconds, etc.
- *
- * This base class defines all the functionality shared by all
- * these time unit objects: it defines all the classic arithmetic
- * operators +, -, *, /, and all the classic comparison operators:
- * ==, !=, <, >, <=, >=. It is thus easy to add, substract, or
- * multiply multiple TimeUnit objects. The return type of any such
- * arithmetic expression is always a TimeUnit object.
- *
- * The ns3::uint64_t, ns3::Time, ns3::TimeSquare, and ns3::TimeInvert classes
- * are aliases for the TimeUnit<0>, TimeUnit<1>, TimeUnit<2> and TimeUnit<-1>
- * types respectively.
+ * \brief Keep track of time values and allow control of global simulation resolution.
*
- * For example:
- * \code
- * Time<1> t1 = Seconds (10.0);
- * Time<1> t2 = Seconds (10.0);
- * Time<2> t3 = t1 * t2;
- * Time<0> t4 = t1 / t2;
- * Time<3> t5 = t3 * t1;
- * Time<-2> t6 = t1 / t5;
- * TimeSquare t7 = t3;
- * uint64_t s = t4;
- * \endcode
- *
- * If you try to assign the result of an expression which does not
- * match the type of the variable it is assigned to, you will get a
- * compiler error. For example, the following will not compile:
- * \code
- * Time<1> = Seconds (10.0) * Seconds (1.5);
- * \endcode
- *
- * You can also use the following non-member functions to manipulate
- * any of these ns3::TimeUnit object:
- * - \ref ns3-Time-Abs ns3::Abs
- * - \ref ns3-Time-Max ns3::Max
- * - \ref ns3-Time-Min ns3::Min
- */
-/**
- * \ingroup time
- * \brief keep track of time values and allow control of global simulation resolution
- *
- * This class defines all the classic C++ arithmetic
- * operators +, -, *, /, and all the classic comparison operators:
+ * This class defines the classic addition/subtraction C++ arithmetic
+ * operators +, -, +=, -=, and all the classic comparison operators:
* ==, !=, <, >, <=, >=. It is thus easy to add, substract, or
- * multiply multiple Time objects.
- *
- * The ns3::uint64_t, ns3::TimeSquare, and ns3::TimeInvert classes
- * are backward-compatibility aliases for ns3::Time.
+ * compare Time objects.
*
* For example:
* \code
* Time t1 = Seconds (10.0);
* Time t2 = Seconds (10.0);
- * Time t3 = t1 * t2;
- * Time t4 = t1 / t2;
- * Time t5 = t3 * t1;
- * Time t6 = t1 / t5;
- * Time t7 = t3;
+ * Time t3 = t1;
+ * t3 += t2;
* \endcode
*
* You can also use the following non-member functions to manipulate
* any of these ns3::Time object:
- * - \ref ns3-Time-Abs ns3::Abs
- * - \ref ns3-Time-Max ns3::Max
- * - \ref ns3-Time-Min ns3::Min
+ * - \ref Abs()
+ * - \ref Max()
+ * - \ref Min()
*
- * This class also controls
- * the resolution of the underlying time value . The default resolution
- * is nanoseconds. That is, TimeStep (1).GetNanoSeconds () will return
- * 1. It is possible to either increase or decrease the resolution and the
- * code tries really hard to make this easy.
+ * This class also controls the resolution of the underlying time value.
+ * The resolution is the smallest representable time interval.
+ * The default resolution is nanoseconds.
*
- * If your resolution is X (say, nanoseconds) and if you create Time objects
- * with a lower resolution (say, picoseconds), don't expect that this
- * code will return 1: PicoSeconds (1).GetPicoSeconds (). It will most
- * likely return 0 because the Time object has only 64 bits of fractional
- * precision which means that PicoSeconds (1) is stored as a 64-bit aproximation
- * of 1/1000 in the Time object. If you later multiply it again by the exact
- * value 1000, the result is unlikely to be 1 exactly. It will be close to
- * 1 but not exactly 1.
- *
- * In general, it is thus a really bad idea to try to use time objects of a
- * resolution higher than the global resolution controlled through
- * Time::SetResolution. If you do need to use picoseconds, it's thus best
- * to switch the global resolution to picoseconds to avoid nasty surprises.
+ * To change the resolution, use SetResolution(). All Time objects created
+ * before the call to SetResolution() will be updated to the new resolution.
+ * This can only be done once! (Tracking each Time object uses 4 pointers.
+ * For speed, once we convert the existing instances we discard the recording
+ * data structure and stop tracking new instances, so we have no way
+ * to do a second conversion.)
*
- * Another important issue to keep in mind is that if you increase the
- * global resolution, you also implicitely decrease the range of your simulation.
- * i.e., the global simulation time is stored in a 64 bit integer whose interpretation
- * will depend on the global resolution so, 2^64 picoseconds which is the maximum
- * duration of your simulation if the global resolution is picoseconds
- * is smaller than 2^64 nanoseconds which is the maximum duration of your simulation
- * if the global resolution is nanoseconds.
+ * Because of the memory (and modest construction cost) of tracking Time
+ * objects, simulations should explicitly choose a resolution before
+ * calling Simulator::Run ().
*
- * Finally, don't even think about ever changing the global resolution after
- * creating Time objects: all Time objects created before the call to SetResolution
- * will contain values which are not updated to the new resolution. In practice,
- * the default value for the attributes of many models is indeed calculated
- * before the main function of the main program enters. Because of this, if you
- * use one of these models (and it's likely), it's going to be hard to change
- * the global simulation resolution in a way which gives reasonable results. This
- * issue has been filed as bug 954 in the ns-3 bugzilla installation.
+ * If you increase the global resolution, you also implicitly decrease
+ * the range of your simulation. The global simulation time is stored
+ * in a 64 bit integer, whose interpretation will depend on the global
+ * resolution. Therefore the maximum duration of your simulation,
+ * if you use picoseconds, is 2^64 ps = 2^24 s = 7 months, whereas,
+ * had you used nanoseconds, you could have run for 584 years.
*/
class Time
{
@@ -154,12 +88,12 @@
*/
enum Unit
{
- S = 0,
- MS = 1,
- US = 2,
- NS = 3,
- PS = 4,
- FS = 5,
+ S = 0, //!< second
+ MS = 1, //!< millisecond
+ US = 2, //!< microsecond
+ NS = 3, //!< nanosecond
+ PS = 4, //!< picosecond
+ FS = 5, //!< femtosecond
LAST = 6
};
@@ -168,44 +102,66 @@
m_data = o.m_data;
return *this;
}
+ inline Time &operator = (const int64_t &value)
+ {
+ m_data = value;
+ return *this;
+ }
inline Time ()
: m_data ()
- {}
+ {
+ TimeSet (this);
+ }
inline Time(const Time &o)
: m_data (o.m_data)
- {}
+ {
+ TimeSet (this);
+ }
explicit inline Time (double v)
: m_data (lround (v))
- {}
+ {
+ TimeSet (this);
+ }
explicit inline Time (int v)
: m_data (v)
- {}
+ {
+ TimeSet (this);
+ }
explicit inline Time (long int v)
: m_data (v)
- {}
+ {
+ TimeSet (this);
+ }
explicit inline Time (long long int v)
: m_data (v)
- {}
+ {
+ TimeSet (this);
+ }
explicit inline Time (unsigned int v)
: m_data (v)
- {}
+ {
+ TimeSet (this);
+ }
explicit inline Time (unsigned long int v)
: m_data (v)
- {}
+ {
+ TimeSet (this);
+ }
explicit inline Time (unsigned long long int v)
: m_data (v)
- {}
-
+ {
+ TimeSet (this);
+ }
/**
- * \brief String constructor
- * Construct Time object from common time expressions like "
- * 1ms" or "10s". Supported units include:
- * - s (seconds)
- * - ms (milliseconds)
- * - us (microseconds)
- * - ns (nanoseconds)
- * - ps (picoseconds)
- * - fs (femtoseconds)
+ * \brief Construct Time object from common time expressions like "1ms"
+ *
+ * Supported units include:
+ * - `s` (seconds)
+ * - `ms` (milliseconds)
+ * - `us` (microseconds)
+ * - `ns` (nanoseconds)
+ * - `ps` (picoseconds)
+ * - `fs` (femtoseconds)
*
* There can be no white space between the numerical portion
* and the units. Any otherwise malformed string causes a fatal error to
@@ -215,6 +171,14 @@
explicit Time (const std::string & s);
/**
+ * Destructor
+ */
+ ~Time ()
+ {
+ TimeUnset (this);
+ }
+
+ /**
* \return true if the time is zero, false otherwise.
*/
inline bool IsZero (void) const
@@ -249,7 +213,9 @@
{
return m_data > 0;
}
-
+ /**
+ * \return -1,0,+1 if `this < o`, `this == o`, or `this > o`
+ */
inline int Compare (const Time &o) const
{
return (m_data < o.m_data) ? -1 : (m_data == o.m_data) ? 0 : 1;
@@ -305,8 +271,7 @@
return ToInteger (Time::FS);
}
/**
- * \returns an approximation of the time stored in this
- * instance in the units specified in m_tsPrecision.
+ * \returns the raw time value, in the current units
*/
inline int64_t GetTimeStep (void) const
{
@@ -436,30 +401,38 @@
}
explicit inline Time (const int64x64_t &value)
: m_data (value.GetHigh ())
- {}
+ {
+ TimeSet (this);
+ }
inline static Time From (const int64x64_t &value)
{
return Time (value);
}
private:
+ /**
+ * How to convert between other units and the current unit
+ */
struct Information
{
- bool toMul;
- bool fromMul;
- uint64_t factor;
- int64x64_t timeTo;
- int64x64_t timeFrom;
+ bool toMul; //!< Multiply when converting To, otherwise divide
+ bool fromMul; //!< Multiple when converting From, otherwise divide
+ uint64_t factor; //!< Ratio of this unit / current unit
+ int64x64_t timeTo; //!< Multiplier to convert to this unit
+ int64x64_t timeFrom; //!< Multiplier to convert from this unit
};
+ /**
+ * Current time unit, and conversion info.
+ */
struct Resolution
{
- struct Information info[LAST];
- enum Time::Unit unit;
+ struct Information info[LAST]; //!< Conversion info from current unit
+ enum Time::Unit unit; //!< Current time unit
};
static inline struct Resolution *PeekResolution (void)
{
- static struct Time::Resolution resolution = GetNsResolution ();
+ static struct Time::Resolution resolution = SetDefaultNsResolution ();
return &resolution;
}
static inline struct Information *PeekInformation (enum Unit timeUnit)
@@ -467,8 +440,51 @@
return &(PeekResolution ()->info[timeUnit]);
}
- static struct Resolution GetNsResolution (void);
- static void SetResolution (enum Unit unit, struct Resolution *resolution);
+ static struct Resolution SetDefaultNsResolution (void);
+ static void SetResolution (enum Unit unit, struct Resolution *resolution,
+ const bool convert = true);
+
+ /**
+ * Record all instances of Time, so we can rescale them when
+ * the resolution changes.
+ *
+ * \internal
+ *
+ * We use a std::set so we can remove the record easily when
+ * ~Time() is called.
+ *
+ * We don't use Ptr<Time>, because we would have to bloat every Time
+ * instance with SimpleRefCount<Time>.
+ *
+ * Seems like this should be std::set< Time * const >, but
+ * http://stackoverflow.com/questions/5526019/compile-errors-stdset-with-const-members
+ * (and gcc 4.2) say no.
+ */
+ typedef std::set< Time * > TimesSet;
+ /**
+ * Get the TimesSet instance.
+ *
+ * \param [in] deleteMe If true delete the TimesSet, so that it returns a null pointer ever after
+ */
+ static TimesSet * GetTimesSet ( const bool deleteMe = false );
+ /**
+ * Helper to clean up at Simulator::Run
+ */
+ static void DeleteTimesSet ();
+ /**
+ * Record a Time instance with the TimesSet
+ */
+ static void TimeSet (Time * const time);
+ /**
+ * Remove a Time instance from the TimesSet, called by ~Time()
+ */
+ static void TimeUnset (Time * const time);
+
+
+ /**
+ * Convert existing Times to the new unit.
+ */
+ static void ConvertTimes (const enum Unit unit);
friend bool operator == (const Time &lhs, const Time &rhs);
friend bool operator != (const Time &lhs, const Time &rhs);
@@ -537,8 +553,8 @@
}
/**
- * \anchor ns3-Time-Abs
- * \relates ns3::TimeUnit
+ * Absolute value function for Time
+ *
* \param time the input value
* \returns the absolute value of the input value.
*/
@@ -547,8 +563,6 @@
return Time ((time.m_data < 0) ? -time.m_data : time.m_data);
}
/**
- * \anchor ns3-Time-Max
- * \relates ns3::TimeUnit
* \param ta the first value
* \param tb the seconds value
* \returns the max of the two input values.
@@ -558,8 +572,6 @@
return Time ((ta.m_data < tb.m_data) ? tb : ta);
}
/**
- * \anchor ns3-Time-Min
- * \relates ns3::TimeUnit
* \param ta the first value
* \param tb the seconds value
* \returns the min of the two input values.
@@ -570,7 +582,19 @@
}
+/**
+ * \brief Time output streamer.
+ *
+ * Generates output such as "3.96ns"
+ * \relates ns3::Time
+ */
std::ostream& operator<< (std::ostream& os, const Time & time);
+/**
+ * \brief Time input streamer
+ *
+ * Uses the Time::Time (std::string) constructor
+ * \relates ns3::Time
+ */
std::istream& operator>> (std::istream& is, Time & time);
/**
@@ -582,6 +606,7 @@
* Simulator::Schedule (Seconds (5.0), ...);
* \endcode
* \param seconds seconds value
+ * \relates ns3::Time
*/
inline Time Seconds (double seconds)
{
@@ -597,6 +622,7 @@
* Simulator::Schedule (MilliSeconds (5), ...);
* \endcode
* \param ms milliseconds value
+ * \relates ns3::Time
*/
inline Time MilliSeconds (uint64_t ms)
{
@@ -611,6 +637,7 @@
* Simulator::Schedule (MicroSeconds (5), ...);
* \endcode
* \param us microseconds value
+ * \relates ns3::Time
*/
inline Time MicroSeconds (uint64_t us)
{
@@ -625,6 +652,7 @@
* Simulator::Schedule (NanoSeconds (5), ...);
* \endcode
* \param ns nanoseconds value
+ * \relates ns3::Time
*/
inline Time NanoSeconds (uint64_t ns)
{
@@ -639,6 +667,7 @@
* Simulator::Schedule (PicoSeconds (5), ...);
* \endcode
* \param ps picoseconds value
+ * \relates ns3::Time
*/
inline Time PicoSeconds (uint64_t ps)
{
@@ -653,6 +682,7 @@
* Simulator::Schedule (FemtoSeconds (5), ...);
* \endcode
* \param fs femtoseconds value
+ * \relates ns3::Time
*/
inline Time FemtoSeconds (uint64_t fs)
{
@@ -660,26 +690,50 @@
}
+/**
+ * \see Seconds(double)
+ * \relates ns3::Time
+ */
inline Time Seconds (int64x64_t seconds)
{
return Time::From (seconds, Time::S);
}
+/**
+ * \see MilliSeconds(uint64_t)
+ * \relates ns3::Time
+ */
inline Time MilliSeconds (int64x64_t ms)
{
return Time::From (ms, Time::MS);
}
+/**
+ * \see MicroSeconds(uint64_t)
+ * \relates ns3::Time
+ */
inline Time MicroSeconds (int64x64_t us)
{
return Time::From (us, Time::US);
}
+/**
+ * \see NanoSeconds(uint64_t)
+ * \relates ns3::Time
+ */
inline Time NanoSeconds (int64x64_t ns)
{
return Time::From (ns, Time::NS);
}
+/**
+ * \see PicoSeconds(uint64_t)
+ * \relates ns3::Time
+ */
inline Time PicoSeconds (int64x64_t ps)
{
return Time::From (ps, Time::PS);
}
+/**
+ * \see FemtoSeconds(uint64_t)
+ * \relates ns3::Time
+ */
inline Time FemtoSeconds (int64x64_t fs)
{
return Time::From (fs, Time::FS);
--- a/src/core/model/time.cc Sat Dec 08 10:11:07 2012 -0800
+++ b/src/core/model/time.cc Sat Dec 08 21:23:44 2012 -0800
@@ -21,22 +21,22 @@
*/
#include "nstime.h"
#include "abort.h"
+#include "log.h"
#include "global-value.h"
#include "enum.h"
#include "string.h"
#include "object.h"
#include "config.h"
-#include "log.h"
+#include "simulator.h"
#include <cmath>
#include <sstream>
+NS_LOG_COMPONENT_DEFINE ("Time");
+
namespace ns3 {
-NS_LOG_COMPONENT_DEFINE ("Time");
-
Time::Time (const std::string& s)
{
- NS_LOG_FUNCTION (this << &s);
std::string::size_type n = s.find_first_not_of ("+-0123456789.");
if (n != std::string::npos)
{ // Found non-numeric
@@ -48,62 +48,82 @@
if (trailer == std::string ("s"))
{
*this = Time::FromDouble (r, Time::S);
- return;
}
- if (trailer == std::string ("ms"))
+ else if (trailer == std::string ("ms"))
{
*this = Time::FromDouble (r, Time::MS);
- return;
}
- if (trailer == std::string ("us"))
+ else if (trailer == std::string ("us"))
{
*this = Time::FromDouble (r, Time::US);
- return;
}
- if (trailer == std::string ("ns"))
+ else if (trailer == std::string ("ns"))
{
*this = Time::FromDouble (r, Time::NS);
- return;
}
- if (trailer == std::string ("ps"))
+ else if (trailer == std::string ("ps"))
{
*this = Time::FromDouble (r, Time::PS);
- return;
}
- if (trailer == std::string ("fs"))
+ else if (trailer == std::string ("fs"))
{
*this = Time::FromDouble (r, Time::FS);
- return;
}
- NS_ABORT_MSG ("Can't Parse Time " << s);
+ else
+ {
+ NS_ABORT_MSG ("Can't Parse Time " << s);
+ }
}
- // else
- // they didn't provide units, assume seconds
- std::istringstream iss;
- iss.str (s);
- double v;
- iss >> v;
- *this = Time::FromDouble (v, Time::S);
+ else
+ {
+ // they didn't provide units, assume seconds
+ std::istringstream iss;
+ iss.str (s);
+ double v;
+ iss >> v;
+ *this = Time::FromDouble (v, Time::S);
+ }
+
+ TimeSet (this);
}
+// static
struct Time::Resolution
-Time::GetNsResolution (void)
+Time::SetDefaultNsResolution (void)
{
- NS_LOG_FUNCTION_NOARGS ();
+ NS_LOG_FUNCTION_NOARGS();
struct Resolution resolution;
- SetResolution (Time::NS, &resolution);
+ SetResolution (Time::NS, &resolution, false);
return resolution;
}
+
+// static
void
Time::SetResolution (enum Unit resolution)
{
NS_LOG_FUNCTION (resolution);
SetResolution (resolution, PeekResolution ());
}
+
+// static
+enum Time::Unit
+Time::GetResolution (void)
+{
+ NS_LOG_FUNCTION_NOARGS();
+ return PeekResolution ()->unit;
+}
+
+// static
void
-Time::SetResolution (enum Unit unit, struct Resolution *resolution)
+Time::SetResolution (enum Unit unit, struct Resolution *resolution,
+ const bool convert /* = true */)
{
- NS_LOG_FUNCTION (unit << resolution);
+ NS_LOG_FUNCTION (unit << resolution << convert);
+ if (convert)
+ { // We have to convert old values
+ ConvertTimes (unit);
+ }
+
int8_t power [LAST] = { 15, 12, 9, 6, 3, 0};
for (int i = 0; i < Time::LAST; i++)
{
@@ -136,18 +156,115 @@
}
resolution->unit = unit;
}
-enum Time::Unit
-Time::GetResolution (void)
+
+
+// static
+Time::TimesSet *
+Time::GetTimesSet ( const bool deleteMe /* = false */ )
{
- NS_LOG_FUNCTION_NOARGS ();
- return PeekResolution ()->unit;
+ static TimesSet * times = new TimesSet;
+
+ if (deleteMe)
+ {
+ NS_LOG_LOGIC ("deleting TimesSet");
+ if (times)
+ {
+ delete times;
+ }
+ times = 0;
+ }
+
+ return times;
+}
+
+// static
+void
+Time::DeleteTimesSet ()
+{
+ NS_LOG_FUNCTION_NOARGS();
+ Time::GetTimesSet (true);
}
+// static
+void
+Time::TimeSet (Time * const time)
+{
+ NS_ASSERT (time != 0);
+
+ TimesSet * times = GetTimesSet();
+ if (times)
+ {
+ std::pair< TimesSet::iterator, bool> ret;
+ ret = times->insert ( time);
+ NS_LOG_LOGIC ("\t[" << times->size () << "] recording " << time);
+
+ if (ret.second == false)
+ {
+ NS_LOG_WARN ("already recorded " << time << "!");
+ }
+ // If this is the first Time, schedule the cleanup.
+ if (times->size () == 1)
+ {
+ // We schedule here, after the first event has been added,
+ // rather than in GetTimesSet when the set is empty.
+ // Scheduling there creates another Time, which
+ // finds an empty set and schedules an event . . .
+ // Doing it here, the schedule creates the second Time,
+ // which doesn't recurse.
+ NS_LOG_LOGIC ("scheduling DeleteTimesSet()");
+ Simulator::Schedule ( Seconds (0), & DeleteTimesSet);
+ }
+ }
+}
+
+// static
+void
+Time::TimeUnset (Time * const time)
+{
+ NS_ASSERT (time != 0);
+ TimesSet * times = GetTimesSet ();
+ if (times)
+ {
+ NS_ASSERT_MSG (times->count (time) == 1,
+ "Time object " << time << " registered "
+ << times->count (time) << " times (should be 1)." );
+
+ TimesSet::size_type num = times->erase (time);
+ if (num != 1)
+ {
+ NS_LOG_WARN ("unexpected result erasing " << time << "!");
+ NS_LOG_WARN ("got " << num << ", expected 1");
+ }
+ else
+ {
+ NS_LOG_LOGIC ("\t[" << times->size () << "] removing " << time);
+ }
+ }
+}
+
+// static
+void
+Time::ConvertTimes (const enum Unit unit)
+{
+ NS_LOG_FUNCTION_NOARGS();
+ TimesSet * times = GetTimesSet ();
+ NS_ASSERT_MSG (times != 0, "No Time registry. Time::SetResolution () called mare than once?");
+
+ for ( TimesSet::iterator it = times->begin();
+ it != times->end();
+ it++ )
+ {
+ Time * const tp = *it;
+ (*tp) = tp->ToInteger (unit);
+ }
+
+ NS_LOG_LOGIC ("logged " << GetTimesSet ()->size () << " Time objects.");
+ GetTimesSet (true);
+}
std::ostream&
operator<< (std::ostream& os, const Time & time)
{
- NS_LOG_FUNCTION (&os << time);
std::string unit;
switch (Time::GetResolution ())
{
@@ -180,7 +297,6 @@
}
std::istream& operator>> (std::istream& is, Time & time)
{
- NS_LOG_FUNCTION (&is << time);
std::string value;
is >> value;
time = Time (value);