A Discrete-Event Network Simulator
API
Loading...
Searching...
No Matches
time.cc
Go to the documentation of this file.
1/*
2 * Copyright (c) 2005,2006 INRIA
3 * Copyright (c) 2007 Emmanuelle Laprise
4 *
5 * SPDX-License-Identifier: GPL-2.0-only
6 *
7 * Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
8 * TimeStep support by Emmanuelle Laprise <emmanuelle.laprise@bluekazoo.ca>
9 */
10#include "abort.h"
11#include "log.h"
12#include "nstime.h"
13
14#include <cmath> // pow
15#include <mutex>
16#include <sstream>
17
18/**
19 * @file
20 * @ingroup time
21 * ns3::Time, ns3::TimeWithUnit
22 * and ns3::TimeValue attribute value implementations.
23 */
24
25namespace ns3
26{
27
29
30/** Unnamed namespace */
31namespace
32{
33
34/** Scaling coefficients, exponents, and look up table for unit. */
35/** @{ */
36/** Scaling exponent, relative to smallest unit. */
37// Y, D, H, MIN, S, MS, US, NS, PS, FS
38const int8_t UNIT_POWER[Time::LAST] = {17, 17, 17, 16, 15, 12, 9, 6, 3, 0};
39/** Scaling coefficient, relative to smallest unit. */
40const int32_t UNIT_COEFF[Time::LAST] = {315360, 864, 36, 6, 1, 1, 1, 1, 1, 1};
41
42/**
43 * Scale a unit to the smallest unit.
44 * @param u The unit to scale
45 * @returns The value of \pname{u} in terms of the smallest defined unit.
46 */
47long double
49{
50 return UNIT_COEFF[u] * std::pow(10L, UNIT_POWER[u]);
51}
52
53/**
54 * Initializer for \c UNIT_VALUE
55 * @returns The array of scale factors between units.
56 */
57long double*
59{
60 static long double values[Time::LAST];
61 for (auto u = static_cast<int>(Time::Y); u != static_cast<int>(Time::LAST); ++u)
62 {
63 values[u] = Scale(static_cast<Time::Unit>(u));
64 }
65 return values;
66}
67
68/** Value of each unit, in terms of the smallest defined unit. */
69const long double* UNIT_VALUE = InitUnitValue();
70
71/** @} */
72
73} // unnamed namespace
74
75// The set of marked times
76// static
78
79/// The static mutex for critical sections around modification of Time::g_markingTimes.
80static std::mutex g_markingMutex;
81
82// Function called to force static initialization
83// static
84bool
86{
87 static bool firstTime = true;
88
89 std::unique_lock lock{g_markingMutex};
90
91 if (firstTime)
92 {
93 if (!g_markingTimes)
94 {
95 static MarkedTimes markingTimes;
96 g_markingTimes = &markingTimes;
97 }
98 else
99 {
100 NS_LOG_ERROR("firstTime but g_markingTimes != 0");
101 }
102
103 // Schedule the cleanup.
104 // We'd really like:
105 // NS_LOG_LOGIC ("scheduling ClearMarkedTimes()");
106 // Simulator::Schedule ( Seconds (0), & ClearMarkedTimes);
107 // [or even better: Simulator::AtStart ( & ClearMarkedTimes ); ]
108 // But this triggers a static initialization order error,
109 // since the Simulator static initialization may not have occurred.
110 // Instead, we call ClearMarkedTimes directly from Simulator::Run ()
111 firstTime = false;
112 }
113
114 return firstTime;
115}
116
117Time::Time(const std::string& s)
118{
119 NS_LOG_FUNCTION(this << &s);
120 std::string::size_type n = s.find_first_not_of("+-0123456789.eE");
121 if (n != std::string::npos)
122 { // Found non-numeric
123 std::istringstream iss;
124 iss.str(s.substr(0, n));
125 double r;
126 iss >> r;
127 std::string trailer = s.substr(n, std::string::npos);
128 if (trailer == "s")
129 {
130 *this = Time::FromDouble(r, Time::S);
131 }
132 else if (trailer == "ms")
133 {
134 *this = Time::FromDouble(r, Time::MS);
135 }
136 else if (trailer == "us")
137 {
138 *this = Time::FromDouble(r, Time::US);
139 }
140 else if (trailer == "ns")
141 {
142 *this = Time::FromDouble(r, Time::NS);
143 }
144 else if (trailer == "ps")
145 {
146 *this = Time::FromDouble(r, Time::PS);
147 }
148 else if (trailer == "fs")
149 {
150 *this = Time::FromDouble(r, Time::FS);
151 }
152 else if (trailer == "min")
153 {
154 *this = Time::FromDouble(r, Time::MIN);
155 }
156 else if (trailer == "h")
157 {
158 *this = Time::FromDouble(r, Time::H);
159 }
160 else if (trailer == "d")
161 {
162 *this = Time::FromDouble(r, Time::D);
163 }
164 else if (trailer == "y")
165 {
166 *this = Time::FromDouble(r, Time::Y);
167 }
168 else
169 {
170 NS_ABORT_MSG("Can't Parse Time " << s);
171 }
172 }
173 else
174 {
175 // they didn't provide units, assume seconds
176 std::istringstream iss;
177 iss.str(s);
178 double v;
179 iss >> v;
180 *this = Time::FromDouble(v, Time::S);
181 }
182
183 if (MarkingTimes())
184 {
185 Mark(this);
186 }
187}
188
189// static
192{
194 static Resolution resolution;
195 SetResolution(Time::NS, &resolution, false);
196 return resolution;
197}
198
199// static
200void
202{
203 NS_LOG_FUNCTION(resolution);
204 SetResolution(resolution, PeekResolution());
205}
206
207// static
208void
209Time::SetResolution(Unit unit, Resolution* resolution, const bool convert /* = true */)
210{
211 NS_LOG_FUNCTION(resolution);
212 if (convert)
213 {
214 // We have to convert existing Times with the old
215 // conversion values, so do it first
216 ConvertTimes(unit);
217 }
218
219 for (int i = 0; i < Time::LAST; i++)
220 {
221 int shift = UNIT_POWER[i] - UNIT_POWER[(int)unit];
222 int quotient = 1;
223 if (UNIT_COEFF[i] > UNIT_COEFF[(int)unit])
224 {
225 quotient = UNIT_COEFF[i] / UNIT_COEFF[(int)unit];
226 NS_ASSERT(quotient * UNIT_COEFF[(int)unit] == UNIT_COEFF[i]);
227 }
228 else if (UNIT_COEFF[i] < UNIT_COEFF[(int)unit])
229 {
230 quotient = UNIT_COEFF[(int)unit] / UNIT_COEFF[i];
231 NS_ASSERT(quotient * UNIT_COEFF[i] == UNIT_COEFF[(int)unit]);
232 }
233 NS_LOG_DEBUG("SetResolution for unit " << (int)unit << " loop iteration " << i
234 << " has shift " << shift << " has quotient "
235 << quotient);
236
237 Information* info = &resolution->info[i];
238 if ((std::pow(10, std::fabs(shift)) * quotient) >
239 static_cast<double>(std::numeric_limits<int64_t>::max()))
240 {
241 NS_LOG_DEBUG("SetResolution for unit " << (int)unit << " loop iteration " << i
242 << " marked as INVALID");
243 info->isValid = false;
244 continue;
245 }
246 auto factor = static_cast<int64_t>(std::pow(10, std::fabs(shift)) * quotient);
247 double realFactor = std::pow(10, (double)shift) * static_cast<double>(UNIT_COEFF[i]) /
248 UNIT_COEFF[(int)unit];
249 NS_LOG_DEBUG("SetResolution factor " << factor << " real factor " << realFactor);
250 info->factor = factor;
251 // here we could equivalently check for realFactor == 1.0 but it's better
252 // to avoid checking equality of doubles
253 if (shift == 0 && quotient == 1)
254 {
255 info->timeFrom = int64x64_t(1);
256 info->timeTo = int64x64_t(1);
257 info->toMul = true;
258 info->fromMul = true;
259 info->isValid = true;
260 }
261 else if (realFactor > 1)
262 {
263 info->timeFrom = int64x64_t(factor);
264 info->timeTo = int64x64_t::Invert(factor);
265 info->toMul = false;
266 info->fromMul = true;
267 info->isValid = true;
268 }
269 else
270 {
271 NS_ASSERT(realFactor < 1);
272 info->timeFrom = int64x64_t::Invert(factor);
273 info->timeTo = int64x64_t(factor);
274 info->toMul = true;
275 info->fromMul = false;
276 info->isValid = true;
277 }
278 }
279 resolution->unit = unit;
280}
281
282bool
284{
285 return (g_markingTimes != nullptr);
286}
287
288// static
289void
291{
292 /**
293 * @internal
294 *
295 * We're called by Simulator::Run, which knows nothing about the mutex,
296 * so we need a critical section here.
297 *
298 * It would seem natural to use this function at the end of
299 * ConvertTimes, but that function already has the mutex.
300 * The mutex can not be locked more than once in the same thread,
301 * so calling this function from ConvertTimes is a bad idea.
302 *
303 * Instead, we copy this body into ConvertTimes.
304 */
305
306 std::unique_lock lock{g_markingMutex};
307
309 if (g_markingTimes)
310 {
311 NS_LOG_LOGIC("clearing MarkedTimes");
312 g_markingTimes->erase(g_markingTimes->begin(), g_markingTimes->end());
313 g_markingTimes = nullptr;
314 }
315}
316
317// static
318void
319Time::Mark(Time* const time)
320{
321 std::unique_lock lock{g_markingMutex};
322
323 NS_LOG_FUNCTION(time);
324 NS_ASSERT(time != nullptr);
325
326 // Repeat the g_markingTimes test here inside the CriticalSection,
327 // since earlier test was outside and might be stale.
328 if (g_markingTimes)
329 {
330 auto ret = g_markingTimes->insert(time);
331 NS_LOG_LOGIC("\t[" << g_markingTimes->size() << "] recording " << time);
332
333 if (!ret.second)
334 {
335 NS_LOG_WARN("already recorded " << time << "!");
336 }
337 }
338}
339
340// static
341void
342Time::Clear(Time* const time)
343{
344 std::unique_lock lock{g_markingMutex};
345
346 NS_LOG_FUNCTION(time);
347 NS_ASSERT(time != nullptr);
348
349 if (g_markingTimes)
350 {
351 NS_ASSERT_MSG(g_markingTimes->count(time) == 1,
352 "Time object " << time << " registered " << g_markingTimes->count(time)
353 << " times (should be 1).");
354
355 MarkedTimes::size_type num = g_markingTimes->erase(time);
356 if (num != 1)
357 {
358 NS_LOG_WARN("unexpected result erasing " << time << "!");
359 NS_LOG_WARN("got " << num << ", expected 1");
360 }
361 else
362 {
363 NS_LOG_LOGIC("\t[" << g_markingTimes->size() << "] removing " << time);
364 }
365 }
366}
367
368// static
369void
371{
372 std::unique_lock lock{g_markingMutex};
373
375
376 NS_ASSERT_MSG(g_markingTimes != nullptr,
377 "No MarkedTimes registry. "
378 "Time::SetResolution () called more than once?");
379
380 for (auto it = g_markingTimes->begin(); it != g_markingTimes->end(); it++)
381 {
382 Time* const tp = *it;
383 if (!(tp->m_data == std::numeric_limits<int64_t>::min() ||
384 tp->m_data == std::numeric_limits<int64_t>::max()))
385 {
386 tp->m_data = tp->ToInteger(unit);
387 }
388 }
389
390 NS_LOG_LOGIC("logged " << g_markingTimes->size() << " Time objects.");
391
392 // Body of ClearMarkedTimes
393 // Assert above already guarantees g_markingTimes != 0
394 NS_LOG_LOGIC("clearing MarkedTimes");
395 g_markingTimes->erase(g_markingTimes->begin(), g_markingTimes->end());
396 g_markingTimes = nullptr;
397}
398
399// static
402{
403 // No function log b/c it interferes with operator<<
404 return PeekResolution()->unit;
405}
406
408Time::As(const Unit unit /* = Time::AUTO */) const
409{
410 return TimeWithUnit(*this, unit);
411}
412
413std::ostream&
414operator<<(std::ostream& os, const Time& time)
415{
416 os << time.As(Time::GetResolution());
417 return os;
418}
419
420std::ostream&
421operator<<(std::ostream& os, const TimeWithUnit& timeU)
422{
423 std::string label;
424 Time::Unit unit = timeU.m_unit;
425
426 if (unit == Time::AUTO)
427 {
428 auto value = static_cast<long double>(timeU.m_time.GetTimeStep());
429 // convert to finest scale (fs)
430 value *= Scale(Time::GetResolution());
431 // find the best unit
432 int u = Time::Y;
433 while (u != Time::LAST && UNIT_VALUE[u] > value)
434 {
435 ++u;
436 }
437 if (u == Time::LAST)
438 {
439 --u;
440 }
441 unit = static_cast<Time::Unit>(u);
442 }
443
444 switch (unit)
445 {
446 case Time::Y:
447 label = "y";
448 break;
449 case Time::D:
450 label = "d";
451 break;
452 case Time::H:
453 label = "h";
454 break;
455 case Time::MIN:
456 label = "min";
457 break;
458 case Time::S:
459 label = "s";
460 break;
461 case Time::MS:
462 label = "ms";
463 break;
464 case Time::US:
465 label = "us";
466 break;
467 case Time::NS:
468 label = "ns";
469 break;
470 case Time::PS:
471 label = "ps";
472 break;
473 case Time::FS:
474 label = "fs";
475 break;
476
477 case Time::LAST:
478 case Time::AUTO:
479 default:
480 NS_ABORT_MSG("can't be reached");
481 label = "unreachable";
482 break;
483 }
484
485 double v = timeU.m_time.ToDouble(unit);
486
487 // Note: we must copy the "original" format flags because we have to modify them.
488 // std::ios_base::showpos is to print the "+" in front of the number for positive,
489 // std::ios_base::right is to add (eventual) extra space in front of the number.
490 // the eventual extra space might be due to a std::setw (_number_), and
491 // normally it would be printed after the number and before the time unit label.
492
493 std::ios_base::fmtflags ff = os.flags();
494
495 os << std::showpos << std::right << v << label;
496
497 // And here we have to restore what we changed.
498 if (!(ff & std::ios_base::showpos))
499 {
500 os << std::noshowpos;
501 }
502 if (ff & std::ios_base::left)
503 {
504 os << std::left;
505 }
506 else if (ff & std::ios_base::internal)
507 {
508 os << std::internal;
509 }
510
511 return os;
512}
513
514std::istream&
515operator>>(std::istream& is, Time& time)
516{
517 std::string value;
518 is >> value;
519 time = Time(value);
520 return is;
521}
522
524
526MakeTimeChecker(const Time min, const Time max)
527{
528 NS_LOG_FUNCTION(min << max);
529
530 struct Checker : public AttributeChecker
531 {
532 Checker(const Time minValue, const Time maxValue)
533 : m_minValue(minValue),
534 m_maxValue(maxValue)
535 {
536 }
537
538 bool Check(const AttributeValue& value) const override
539 {
540 NS_LOG_FUNCTION(&value);
541 const auto v = dynamic_cast<const TimeValue*>(&value);
542 if (v == nullptr)
543 {
544 return false;
545 }
546 return v->Get() >= m_minValue && v->Get() <= m_maxValue;
547 }
548
549 std::string GetValueTypeName() const override
550 {
552 return "ns3::TimeValue";
553 }
554
555 bool HasUnderlyingTypeInformation() const override
556 {
558 return true;
559 }
560
561 std::string GetUnderlyingTypeInformation() const override
562 {
564 std::ostringstream oss;
565 oss << "Time " << m_minValue << ":" << m_maxValue;
566 return oss.str();
567 }
568
569 Ptr<AttributeValue> Create() const override
570 {
572 return ns3::Create<TimeValue>();
573 }
574
575 bool Copy(const AttributeValue& source, AttributeValue& destination) const override
576 {
577 NS_LOG_FUNCTION(&source << &destination);
578 const auto src = dynamic_cast<const TimeValue*>(&source);
579 auto dst = dynamic_cast<TimeValue*>(&destination);
580 if (src == nullptr || dst == nullptr)
581 {
582 return false;
583 }
584 *dst = *src;
585 return true;
586 }
587
588 Time m_minValue;
589 Time m_maxValue;
590 }* checker = new Checker(min, max);
591
592 return Ptr<const AttributeChecker>(checker, false);
593}
594
595} // namespace ns3
NS_ABORT_x macro definitions.
uint32_t r
uint32_t v
uint32_t u
uint32_t quotient
Represent the type of an attribute.
Definition attribute.h:157
Hold a value for an Attribute.
Definition attribute.h:59
Smart pointer class similar to boost::intrusive_ptr.
Definition ptr.h:70
Simulation virtual time values and global simulation resolution.
Definition nstime.h:96
static void ClearMarkedTimes()
Remove all MarkedTimes.
Definition time.cc:290
static Resolution & SetDefaultNsResolution()
Set the default resolution.
Definition time.cc:191
TimeWithUnit As(const Unit unit=Time::AUTO) const
Attach a unit to a Time, to facilitate output in a specific unit.
Definition time.cc:408
static void ConvertTimes(const Unit unit)
Convert existing Times to the new unit.
Definition time.cc:370
static Unit GetResolution()
Definition time.cc:401
static bool StaticInit()
Function to force static initialization of Time.
Definition time.cc:85
static void Clear(Time *const time)
Remove a Time instance from the MarkedTimes, called by ~Time().
Definition time.cc:342
Unit
The unit to use to interpret a number representing time.
Definition nstime.h:102
@ AUTO
auto-scale output when using Time::As()
Definition nstime.h:114
@ D
day, 24 hours
Definition nstime.h:104
@ US
microsecond
Definition nstime.h:109
@ PS
picosecond
Definition nstime.h:111
@ LAST
marker for last normal value
Definition nstime.h:113
@ Y
year, 365 days
Definition nstime.h:103
@ FS
femtosecond
Definition nstime.h:112
@ H
hour, 60 minutes
Definition nstime.h:105
@ MIN
minute, 60 seconds
Definition nstime.h:106
@ MS
millisecond
Definition nstime.h:108
@ S
second
Definition nstime.h:107
@ NS
nanosecond
Definition nstime.h:110
Time()
Default constructor, with value 0.
Definition nstime.h:129
static MarkedTimes * g_markingTimes
Record of outstanding Time objects which will need conversion when the resolution is set.
Definition nstime.h:756
int64_t m_data
Virtual time value, in the current unit.
Definition nstime.h:875
static Resolution * PeekResolution()
Get the current Resolution.
Definition nstime.h:689
static void Mark(Time *const time)
Record a Time instance with the MarkedTimes.
Definition time.cc:319
static void SetResolution(Unit resolution)
Definition time.cc:201
int64_t ToInteger(Unit unit) const
Get the Time value expressed in a particular unit.
Definition nstime.h:569
static Time FromDouble(double value, Unit unit)
Create a Time equal to value in unit unit.
Definition nstime.h:518
double ToDouble(Unit unit) const
Get the Time value expressed in a particular unit.
Definition nstime.h:593
int64_t GetTimeStep() const
Get the raw time value, in the current resolution unit.
Definition nstime.h:441
static bool MarkingTimes()
Null check for g_markingTimes from outside time.cc.
Definition time.cc:283
std::set< Time * > MarkedTimes
Record all instances of Time, so we can rescale them when the resolution changes.
Definition nstime.h:741
AttributeValue implementation for Time.
Definition nstime.h:1483
A Time with attached unit, to facilitate output in that unit.
Definition nstime.h:1527
TimeWithUnit(const Time time, const Time::Unit unit)
Attach a unit to a Time.
Definition nstime.h:1535
Time m_time
The time.
Definition nstime.h:1542
Time::Unit m_unit
The unit to use in output.
Definition nstime.h:1543
High precision numerical type, implementing Q64.64 fixed precision.
static int64x64_t Invert(const uint64_t v)
Compute the inverse of an integer value.
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition assert.h:55
#define NS_ASSERT_MSG(condition, message)
At runtime, in debugging builds, if this condition is not true, the program prints the message to out...
Definition assert.h:75
Ptr< const AttributeChecker > MakeTimeChecker()
Helper to make an unbounded Time checker.
Definition nstime.h:1504
#define ATTRIBUTE_VALUE_IMPLEMENT(type)
Define the class methods belonging to attribute value class typeValue for class type.
#define NS_ABORT_MSG(msg)
Unconditional abnormal program termination with a message.
Definition abort.h:38
#define NS_LOG_ERROR(msg)
Use NS_LOG to output a message of level LOG_ERROR.
Definition log.h:246
#define NS_LOG_COMPONENT_DEFINE_MASK(name, mask)
Define a logging component with a mask.
Definition log.h:205
#define NS_LOG_DEBUG(msg)
Use NS_LOG to output a message of level LOG_DEBUG.
Definition log.h:260
#define NS_LOG_LOGIC(msg)
Use NS_LOG to output a message of level LOG_LOGIC.
Definition log.h:274
#define NS_LOG_FUNCTION_NOARGS()
Output the name of the function.
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
#define NS_LOG_WARN(msg)
Use NS_LOG to output a message of level LOG_WARN.
Definition log.h:253
Ptr< T > Create(Ts &&... args)
Create class instances by constructors with varying numbers of arguments and return them by Ptr.
Definition ptr.h:454
Debug message logging.
const long double * UNIT_VALUE
Value of each unit, in terms of the smallest defined unit.
Definition time.cc:69
const int32_t UNIT_COEFF[Time::LAST]
Scaling coefficient, relative to smallest unit.
Definition time.cc:40
const int8_t UNIT_POWER[Time::LAST]
Scaling coefficients, exponents, and look up table for unit.
Definition time.cc:38
long double * InitUnitValue()
Initializer for UNIT_VALUE.
Definition time.cc:58
long double Scale(Time::Unit u)
Scale a unit to the smallest unit.
Definition time.cc:48
Every class exported by the ns3 library is enclosed in the ns3 namespace.
std::ostream & operator<<(std::ostream &os, const Angles &a)
Definition angles.cc:148
std::istream & operator>>(std::istream &is, Angles &a)
Definition angles.cc:172
Ptr< T > Copy(Ptr< T > object)
Return a deep copy of a Ptr.
Definition ptr.h:629
@ LOG_PREFIX_TIME
Prefix all trace prints with simulation time.
Definition log.h:111
static std::mutex g_markingMutex
The static mutex for critical sections around modification of Time::g_markingTimes.
Definition time.cc:80
Declaration of classes ns3::Time and ns3::TimeWithUnit, and the TimeValue implementation classes.
How to convert between other units and the current unit.
Definition nstime.h:668
int64_t factor
Ratio of this unit / current unit.
Definition nstime.h:671
bool toMul
Multiply when converting To, otherwise divide.
Definition nstime.h:669
int64x64_t timeFrom
Multiplier to convert from this unit.
Definition nstime.h:673
bool isValid
True if the current unit can be used.
Definition nstime.h:674
bool fromMul
Multiple when converting From, otherwise divide.
Definition nstime.h:670
int64x64_t timeTo
Multiplier to convert to this unit.
Definition nstime.h:672
Current time unit, and conversion info.
Definition nstime.h:679
Time::Unit unit
Current time unit.
Definition nstime.h:681
Information info[LAST]
Conversion info from current unit.
Definition nstime.h:680