1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2005,2006 INRIA
4  * Copyright (c) 2007 Emmanuelle Laprise
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation;
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
20  * TimeStep support by Emmanuelle Laprise <emmanuelle.laprise@bluekazoo.ca>
21  */
22 #include "nstime.h"
23 #include "abort.h"
24 #include "system-mutex.h"
25 #include "log.h"
26 #include <cmath>    // pow
27 #include <iomanip>  // showpos
28 #include <sstream>
29 
30 /**
31  * \file
32  * \ingroup time
33  * ns3::Time, ns3::TimeWithUnit
34  * and ns3::TimeValue attribute value implementations.
35  */
36 
37 namespace ns3 {
38 
39 NS_LOG_COMPONENT_DEFINE_MASK ("Time", ns3::LOG_PREFIX_TIME);
40 
41 /** Unnamed namespace */
42 namespace {
43 
44   /** Scaling coefficients, exponents, and look up table for unit. */
45   /** @{ */
46   /** Scaling exponent, relative to smallest unit. */
47   //                                      Y,   D,  H, MIN,  S, MS, US, NS, PS, FS
48   const int8_t  UNIT_POWER[Time::LAST] = {     17,  17, 17,  16, 15, 12,  9,  6,  3,  0 };
49   /** Scaling coefficient, relative to smallest unit. */
50   const int32_t UNIT_COEFF[Time::LAST] = { 315360, 864, 36,   6,  1,  1,  1,  1,  1,  1 };
51 
52 
53   /**
54    * Scale a unit to the smallest unit.
55    * \param u The unit to scale
56    * \returns The value of \pname{u} in terms of the smallest defined unit.
57    */
58   long double
Scale(Time::Unit u)59   Scale (Time::Unit u)
60   {
61     return UNIT_COEFF[u] * std::pow (10L, UNIT_POWER[u]);
62   }
63 
64   /** Initializer for \c UNIT_VALUE */
65   long double *
InitUnitValue(void)66   InitUnitValue (void)
67   {
68     static long double values[Time::LAST];
69     for (auto u = static_cast<int> (Time::Y); u != static_cast<int> (Time::LAST); ++u)
70       {
71         values [u] = Scale (static_cast<Time::Unit> (u));
72       }
73     return values;
74   }
75 
76   /** Value of each unit, in terms of the smallest defined unit. */
77   const long double * UNIT_VALUE = InitUnitValue ();
78 
79   /** @} */
80 
81 }  // unnamed namespace
82 
83 
84 // The set of marked times
85 // static
86 Time::MarkedTimes * Time::g_markingTimes = 0;
87 
88 /**
89  * \internal
90  * Get mutex for critical sections around modification of Time::g_markingTimes
91  *
92  * \returns The static mutex to control access to Time::g_markingTimes.
93  *
94  * \relates Time
95  */
96 SystemMutex &
GetMarkingMutex()97 GetMarkingMutex ()
98 {
99   static SystemMutex g_markingMutex;
100   return g_markingMutex;
101 }
102 
103 
104 // Function called to force static initialization
105 // static
StaticInit()106 bool Time::StaticInit ()
107 {
108   static bool firstTime = true;
109 
110   CriticalSection critical (GetMarkingMutex ());
111 
112   if (firstTime)
113     {
114       if (!g_markingTimes)
115         {
116           static MarkedTimes markingTimes;
117           g_markingTimes = &markingTimes;
118         }
119       else
120         {
121           NS_LOG_ERROR ("firstTime but g_markingTimes != 0");
122         }
123 
124       // Schedule the cleanup.
125       // We'd really like:
126       //   NS_LOG_LOGIC ("scheduling ClearMarkedTimes()");
127       //   Simulator::Schedule ( Seconds (0), & ClearMarkedTimes);
128       //   [or even better:  Simulator::AtStart ( & ClearMarkedTimes ); ]
129       // But this triggers a static initialization order error,
130       // since the Simulator static initialization may not have occurred.
131       // Instead, we call ClearMarkedTimes directly from Simulator::Run ()
132       firstTime = false;
133     }
134 
135   return firstTime;
136 }
137 
138 
Time(const std::string & s)139 Time::Time (const std::string& s)
140 {
141   NS_LOG_FUNCTION (this << &s);
142   std::string::size_type n = s.find_first_not_of ("+-0123456789.eE");
143   if (n != std::string::npos)
144     { // Found non-numeric
145       std::istringstream iss;
146       iss.str (s.substr (0, n));
147       double r;
148       iss >> r;
149       std::string trailer = s.substr (n, std::string::npos);
150       if (trailer == std::string ("s"))
151         {
152           *this = Time::FromDouble (r, Time::S);
153         }
154       else if (trailer == std::string ("ms"))
155         {
156           *this = Time::FromDouble (r, Time::MS);
157         }
158       else if (trailer == std::string ("us"))
159         {
160           *this = Time::FromDouble (r, Time::US);
161         }
162       else if (trailer == std::string ("ns"))
163         {
164           *this = Time::FromDouble (r, Time::NS);
165         }
166       else if (trailer == std::string ("ps"))
167         {
168           *this = Time::FromDouble (r, Time::PS);
169         }
170       else if (trailer == std::string ("fs"))
171         {
172           *this = Time::FromDouble (r, Time::FS);
173         }
174       else if (trailer == std::string ("min"))
175         {
176           *this = Time::FromDouble (r, Time::MIN);
177         }
178       else if (trailer == std::string ("h"))
179         {
180           *this = Time::FromDouble (r, Time::H);
181         }
182       else if (trailer == std::string ("d"))
183         {
184           *this = Time::FromDouble (r, Time::D);
185         }
186       else if (trailer == std::string ("y"))
187         {
188           *this = Time::FromDouble (r, Time::Y);
189         }
190       else
191         {
192           NS_ABORT_MSG ("Can't Parse Time " << s);
193         }
194     }
195   else
196     {
197       // they didn't provide units, assume seconds
198       std::istringstream iss;
199       iss.str (s);
200       double v;
201       iss >> v;
202       *this = Time::FromDouble (v, Time::S);
203     }
204 
205   if (g_markingTimes)
206     {
207       Mark (this);
208     }
209 }
210 
211 // static
212 struct Time::Resolution
SetDefaultNsResolution(void)213 Time::SetDefaultNsResolution (void)
214 {
215   NS_LOG_FUNCTION_NOARGS ();
216   struct Resolution resolution;
217   SetResolution (Time::NS, &resolution, false);
218   return resolution;
219 }
220 
221 // static
222 void
SetResolution(enum Unit resolution)223 Time::SetResolution (enum Unit resolution)
224 {
225   NS_LOG_FUNCTION (resolution);
226   SetResolution (resolution, PeekResolution ());
227 }
228 
229 
230 // static
231 void
SetResolution(enum Unit unit,struct Resolution * resolution,const bool convert)232 Time::SetResolution (enum Unit unit, struct Resolution *resolution,
233                      const bool convert /* = true */)
234 {
235   NS_LOG_FUNCTION (resolution);
236   if (convert)
237     {
238       // We have to convert existing Times with the old
239       // conversion values, so do it first
240       ConvertTimes (unit);
241     }
242 
243   for (int i = 0; i < Time::LAST; i++)
244     {
245       int shift = UNIT_POWER[i] - UNIT_POWER[(int)unit];
246       int quotient = 1;
247       if (UNIT_COEFF[i] > UNIT_COEFF[(int) unit])
248         {
249           quotient = UNIT_COEFF[i] / UNIT_COEFF[(int) unit];
250           NS_ASSERT (quotient * UNIT_COEFF[(int) unit] == UNIT_COEFF[i]);
251         }
252       else if (UNIT_COEFF[i] < UNIT_COEFF[(int) unit])
253         {
254           quotient = UNIT_COEFF[(int) unit] / UNIT_COEFF[i];
255           NS_ASSERT (quotient * UNIT_COEFF[i] == UNIT_COEFF[(int) unit]);
256         }
257       NS_LOG_DEBUG ("SetResolution for unit " << (int) unit <<
258                     " loop iteration " << i <<
259                     " has shift " << shift << " has quotient " << quotient);
260       int64_t factor = static_cast<int64_t> (std::pow (10, std::fabs (shift)) * quotient);
261       double realFactor = std::pow (10, (double) shift)
262         * static_cast<double> (UNIT_COEFF[i]) / UNIT_COEFF[(int) unit];
263       NS_LOG_DEBUG ("SetResolution factor " << factor << " real factor " << realFactor);
264       struct Information *info = &resolution->info[i];
265       info->factor = factor;
266       // here we could equivalently check for realFactor == 1.0 but it's better
267       // to avoid checking equality of doubles
268       if (shift == 0 && quotient == 1)
269         {
270           info->timeFrom = int64x64_t (1);
271           info->timeTo = int64x64_t (1);
272           info->toMul = true;
273           info->fromMul = true;
274         }
275       else if (realFactor > 1)
276         {
277           info->timeFrom = int64x64_t (factor);
278           info->timeTo = int64x64_t::Invert (factor);
279           info->toMul = false;
280           info->fromMul = true;
281         }
282       else
283         {
284           NS_ASSERT (realFactor < 1);
285           info->timeFrom = int64x64_t::Invert (factor);
286           info->timeTo = int64x64_t (factor);
287           info->toMul = true;
288           info->fromMul = false;
289         }
290     }
291   resolution->unit = unit;
292 }
293 
294 
295 // static
296 void
ClearMarkedTimes()297 Time::ClearMarkedTimes ()
298 {
299   /**
300    * \internal
301    *
302    * We're called by Simulator::Run, which knows nothing about the mutex,
303    * so we need a critical section here.
304    *
305    * It would seem natural to use this function at the end of
306    * ConvertTimes, but that function already has the mutex.
307    * Our SystemMutex throws a fatal error if we try to lock it more than
308    * once in the same thread (at least in the unix implementation),
309    * so calling this function from ConvertTimes is a bad idea.
310    *
311    * Instead, we copy this body into ConvertTimes.
312    */
313 
314   CriticalSection critical (GetMarkingMutex ());
315 
316   NS_LOG_FUNCTION_NOARGS ();
317   if (g_markingTimes)
318     {
319       NS_LOG_LOGIC ("clearing MarkedTimes");
320       g_markingTimes->erase (g_markingTimes->begin (), g_markingTimes->end ());
321       g_markingTimes = 0;
322     }
323 }  // Time::ClearMarkedTimes
324 
325 
326 // static
327 void
Mark(Time * const time)328 Time::Mark (Time * const time)
329 {
330   CriticalSection critical (GetMarkingMutex ());
331 
332   NS_LOG_FUNCTION (time);
333   NS_ASSERT (time != 0);
334 
335   // Repeat the g_markingTimes test here inside the CriticalSection,
336   // since earlier test was outside and might be stale.
337   if (g_markingTimes)
338     {
339       std::pair< MarkedTimes::iterator, bool> ret;
340 
341       ret = g_markingTimes->insert ( time);
342       NS_LOG_LOGIC ("\t[" << g_markingTimes->size () << "] recording " << time);
343 
344       if (ret.second == false)
345         {
346           NS_LOG_WARN ("already recorded " << time << "!");
347         }
348     }
349 }  // Time::Mark ()
350 
351 
352 // static
353 void
Clear(Time * const time)354 Time::Clear (Time * const time)
355 {
356   CriticalSection critical (GetMarkingMutex ());
357 
358   NS_LOG_FUNCTION (time);
359   NS_ASSERT (time != 0);
360 
361   if (g_markingTimes)
362     {
363       NS_ASSERT_MSG (g_markingTimes->count (time) == 1,
364                      "Time object " << time <<
365                      " registered " << g_markingTimes->count (time) <<
366                      " times (should be 1)." );
367 
368       MarkedTimes::size_type num = g_markingTimes->erase (time);
369       if (num != 1)
370         {
371           NS_LOG_WARN ("unexpected result erasing " << time << "!");
372           NS_LOG_WARN ("got " << num << ", expected 1");
373         }
374       else
375         {
376           NS_LOG_LOGIC ("\t[" << g_markingTimes->size () << "] removing  " << time);
377         }
378     }
379 }  // Time::Clear ()
380 
381 
382 // static
383 void
ConvertTimes(const enum Unit unit)384 Time::ConvertTimes (const enum Unit unit)
385 {
386   CriticalSection critical (GetMarkingMutex ());
387 
388   NS_LOG_FUNCTION_NOARGS ();
389 
390   NS_ASSERT_MSG (g_markingTimes != 0,
391                  "No MarkedTimes registry. "
392                  "Time::SetResolution () called more than once?");
393 
394   for ( MarkedTimes::iterator it = g_markingTimes->begin ();
395         it != g_markingTimes->end ();
396         it++ )
397     {
398       Time * const tp = *it;
399       if ( !((tp->m_data == std::numeric_limits<int64_t>::min ())
400              || (tp->m_data == std::numeric_limits<int64_t>::max ())
401              )
402            )
403         {
404           tp->m_data = tp->ToInteger (unit);
405         }
406     }
407 
408   NS_LOG_LOGIC ("logged " << g_markingTimes->size () << " Time objects.");
409 
410   // Body of ClearMarkedTimes
411   // Assert above already guarantees g_markingTimes != 0
412   NS_LOG_LOGIC ("clearing MarkedTimes");
413   g_markingTimes->erase (g_markingTimes->begin (), g_markingTimes->end ());
414   g_markingTimes = 0;
415 
416 }  // Time::ConvertTimes ()
417 
418 
419 // static
420 enum Time::Unit
GetResolution(void)421 Time::GetResolution (void)
422 {
423   // No function log b/c it interferes with operator<<
424   return PeekResolution ()->unit;
425 }
426 
427 
428 TimeWithUnit
As(const enum Unit unit) const429 Time::As (const enum Unit unit /* = Time::AUTO */) const
430 {
431   return TimeWithUnit (*this, unit);
432 }
433 
434 
435 std::ostream &
operator <<(std::ostream & os,const Time & time)436 operator << (std::ostream & os, const Time & time)
437 {
438   os << time.As (Time::GetResolution ());
439   return os;
440 }
441 
442 
443 std::ostream &
operator <<(std::ostream & os,const TimeWithUnit & timeU)444 operator << (std::ostream & os, const TimeWithUnit & timeU)
445 {
446 
447   std::string label;
448   Time::Unit unit = timeU.m_unit;
449 
450   if (unit == Time::AUTO)
451     {
452       long double value = static_cast<long double> (timeU.m_time.GetTimeStep ());
453       // convert to finest scale (fs)
454       value *= Scale (Time::GetResolution ());
455       // find the best unit
456       int u = Time::Y;
457       while (u != Time::LAST && UNIT_VALUE[u] > value)
458         {
459           ++u;
460         }
461       if (u == Time::LAST)
462         {
463           --u;
464         }
465       unit = static_cast<Time::Unit> (u);
466     }
467 
468   switch (unit)
469     {
470       // *NS_CHECK_STYLE_OFF*
471     case Time::Y:    label = "y";    break;
472     case Time::D:    label = "d";    break;
473     case Time::H:    label = "h";    break;
474     case Time::MIN:  label = "min";  break;
475     case Time::S:    label = "s";    break;
476     case Time::MS:   label = "ms";   break;
477     case Time::US:   label = "us";   break;
478     case Time::NS:   label = "ns";   break;
479     case Time::PS:   label = "ps";   break;
480     case Time::FS:   label = "fs";   break;
481       // *NS_CHECK_STYLE_ON*
482 
483     case Time::LAST:
484     case Time::AUTO:
485     default:
486       NS_ABORT_MSG ("can't be reached");
487       label = "unreachable";
488       break;
489     }
490 
491   double v = timeU.m_time.ToDouble (unit);
492 
493   // Note: we must copy the "original" format flags because we have to modify them.
494   // std::ios_base::showpos is to print the "+" in front of the number for positive,
495   // std::ios_base::right is to add (eventual) extra space in front of the number.
496   //   the eventual extra space might be due to a std::setw (_number_), and
497   //   normally it would be printed after the number and before the time unit label.
498 
499   std::ios_base::fmtflags ff = os.flags ();
500 
501   os << std::showpos << std::right << v << label;
502 
503   // And here we have to restore what we changed.
504   if (!(ff & std::ios_base::showpos))
505     {
506       os << std::noshowpos;
507     }
508   if (ff & std::ios_base::left)
509     {
510       os << std::left;
511     }
512   else if (ff & std::ios_base::internal)
513     {
514       os << std::internal;
515     }
516 
517   return os;
518 }
519 
520 
521 std::istream &
operator >>(std::istream & is,Time & time)522 operator >> (std::istream & is, Time & time)
523 {
524   std::string value;
525   is >> value;
526   time = Time (value);
527   return is;
528 }
529 
530 ATTRIBUTE_VALUE_IMPLEMENT (Time);
531 
532 Ptr<const AttributeChecker>
MakeTimeChecker(const Time min,const Time max)533 MakeTimeChecker (const Time min, const Time max)
534 {
535   NS_LOG_FUNCTION (min << max);
536 
537   struct Checker : public AttributeChecker
538   {
539     Checker (const Time minValue, const Time maxValue)
540       : m_minValue (minValue),
541         m_maxValue (maxValue)
542     {}
543     virtual bool Check (const AttributeValue &value) const
544     {
545       NS_LOG_FUNCTION (&value);
546       const TimeValue *v = dynamic_cast<const TimeValue *> (&value);
547       if (v == 0)
548         {
549           return false;
550         }
551       return v->Get () >= m_minValue && v->Get () <= m_maxValue;
552     }
553     virtual std::string GetValueTypeName (void) const
554     {
555       NS_LOG_FUNCTION_NOARGS ();
556       return "ns3::TimeValue";
557     }
558     virtual bool HasUnderlyingTypeInformation (void) const
559     {
560       NS_LOG_FUNCTION_NOARGS ();
561       return true;
562     }
563     virtual std::string GetUnderlyingTypeInformation (void) const
564     {
565       NS_LOG_FUNCTION_NOARGS ();
566       std::ostringstream oss;
567       oss << "Time" << " " << m_minValue << ":" << m_maxValue;
568       return oss.str ();
569     }
570     virtual Ptr<AttributeValue> Create (void) const
571     {
572       NS_LOG_FUNCTION_NOARGS ();
573       return ns3::Create<TimeValue> ();
574     }
575     virtual bool Copy (const AttributeValue &source, AttributeValue &destination) const
576     {
577       NS_LOG_FUNCTION (&source << &destination);
578       const TimeValue *src = dynamic_cast<const TimeValue *> (&source);
579       TimeValue *dst = dynamic_cast<TimeValue *> (&destination);
580       if (src == 0 || dst == 0)
581         {
582           return false;
583         }
584       *dst = *src;
585       return true;
586     }
587     Time m_minValue;
588     Time m_maxValue;
589   } *checker = new Checker (min, max);
590   return Ptr<const AttributeChecker> (checker, false);
591 }
592 
593 
594 } // namespace ns3
595 
596