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