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