A Discrete-Event Network Simulator
API
wall-clock-synchronizer.cc
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2008 University of Washington
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation;
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  */
18 
19 
20 #include <ctime> // clock_t
21 #include <sys/time.h> // gettimeofday
22  // clock_getres: glibc < 2.17, link with librt
23 
24 #include "log.h"
25 #include "system-condition.h"
26 
28 
35 namespace ns3 {
36 
37 NS_LOG_COMPONENT_DEFINE ("WallClockSynchronizer");
38 
39 NS_OBJECT_ENSURE_REGISTERED (WallClockSynchronizer);
40 
41 TypeId
43 {
44  static TypeId tid = TypeId ("ns3::WallClockSynchronizer")
46  .SetGroupName ("Core")
47  ;
48  return tid;
49 }
50 
52 {
53  NS_LOG_FUNCTION (this);
54 //
55 // In Linux, the basic timekeeping unit is derived from a variable called HZ
56 // HZ is the frequency in hertz of the system timer. The system timer fires
57 // every 1/HZ seconds and a counter, called the jiffies counter is incremented
58 // at each tick. The time between ticks is called a jiffy (American slang for
59 // a short period of time). The ticking of the jiffies counter is how the
60 // the kernel tells time.
61 //
62 // Now, the shortest time the kernel can sleep is one jiffy since a timer
63 // has to be set to expire and trigger the process to be made ready. The
64 // Posix clock CLOCK_REALTIME is defined as a 1/HZ clock, so by doing a
65 // clock_getres () on the realtime clock we can infer the scheduler quantum
66 // and the minimimum sleep time for the system. This is most certainly NOT
67 // going to be one nanosecond even though clock_nanosleep () pretends it is.
68 //
69 // The reason this number is important is that we are going to schedule lots
70 // of waits for less time than a jiffy. The clock_nanosleep function is
71 // going to guarantee that it will sleep for AT LEAST the time specified.
72 // The least time that it will sleep is a jiffy.
73 //
74 // In order to deal with this, we are going to do a spin-wait if the simulator
75 // requires a delay less than a jiffy. This is on the order of one millisecond
76 // (999848 ns) on the ns-regression machine.
77 //
78 // If the underlying OS does not support posix clocks, we'll just assume a
79 // one millisecond quantum and deal with this as best we can
80 
81 #ifdef CLOCK_REALTIME
82  struct timespec ts;
83  clock_getres (CLOCK_REALTIME, &ts);
84  m_jiffy = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
85  NS_LOG_INFO ("Jiffy is " << m_jiffy << " ns");
86 #else
87  m_jiffy = 1000000;
88 #endif
89 }
90 
92 {
93  NS_LOG_FUNCTION (this);
94 }
95 
96 bool
98 {
99  NS_LOG_FUNCTION (this);
100  return true;
101 }
102 
103 uint64_t
105 {
106  NS_LOG_FUNCTION (this);
107  return GetNormalizedRealtime ();
108 }
109 
110 void
112 {
113  NS_LOG_FUNCTION (this << ns);
114 //
115 // In order to make sure we're really locking the simulation time to some
116 // wall-clock time, we need to be able to compare that simulation time to
117 // that wall-clock time. The wall clock will have been running for some
118 // long time and will probably have a huge count of nanoseconds in it. We
119 // save the real time away so we can subtract it from "now" later and get
120 // a count of nanoseconds in real time since the simulation started.
121 //
123  NS_LOG_INFO ("origin = " << m_realtimeOriginNano);
124 }
125 
126 int64_t
128 {
129  NS_LOG_FUNCTION (this << ns);
130 //
131 // In order to make sure we're really locking the simulation time to some
132 // wall-clock time, we need to be able to compare that simulation time to
133 // that wall-clock time. In DoSetOrigin we saved the real time at the start
134 // of the simulation away. This is the place where we subtract it from "now"
135 // to a count of nanoseconds in real time since the simulation started. We
136 // then subtract the current real time in normalized nanoseconds we just got
137 // from the normalized simulation time in nanoseconds that is passed in as
138 // the parameter ns. We return an integer difference, but in reality all of
139 // the mechanisms that cause wall-clock to simuator time drift cause events
140 // to be late. That means that the wall-clock will be higher than the
141 // simulation time and drift will be positive. I would be astonished to
142 // see a negative drift, but the possibility is admitted for other
143 // implementations; and we'll use the ability to report an early result in
144 // DoSynchronize below.
145 //
146  uint64_t nsNow = GetNormalizedRealtime ();
147 
148  if (nsNow > ns)
149  {
150 //
151 // Real time (nsNow) is larger/later than the simulator time (ns). We are
152 // behind real time and the difference (drift) is positive.
153 //
154  return (int64_t)(nsNow - ns);
155  }
156  else
157  {
158 //
159 // Real time (nsNow) is smaller/earlier than the simulator time (ns). We are
160 // ahead of real time and the difference (drift) is negative.
161 //
162  return -(int64_t)(ns - nsNow);
163  }
164 }
165 
166 bool
167 WallClockSynchronizer::DoSynchronize (uint64_t nsCurrent, uint64_t nsDelay)
168 {
169  NS_LOG_FUNCTION (this << nsCurrent << nsDelay);
170 //
171 // This is the belly of the beast. We have received two parameters from the
172 // simulator proper -- a current simulation time (nsCurrent) and a simulation
173 // time to delay which identifies the time the next event is supposed to fire.
174 //
175 // The first thing we need to do is to (try and) correct for any realtime
176 // drift that has happened in the system. In this implementation, we realize
177 // that all mechanisms for drift will cause the drift to be such that the
178 // realtime is greater than the simulation time. This typically happens when
179 // our process is put to sleep for a given time, but actually sleeps for
180 // longer. So, what we want to do is to "catch up" to realtime and delay for
181 // less time than we are actually asked to delay. DriftCorrect will return a
182 // number from 0 to nsDelay corresponding to the amount of catching-up we
183 // need to do. If we are more than nsDelay behind, we do not wait at all.
184 //
185 // Note that it will be impossible to catch up if the amount of drift is
186 // cumulatively greater than the amount of delay between events. The method
187 // GetDrift () is available to clients of the syncrhonizer to keep track of
188 // the cumulative drift. The client can assert if the drift gets out of
189 // hand, print warning messages, or just ignore the situation and hope it will
190 // go away.
191 //
192  uint64_t ns = DriftCorrect (nsCurrent, nsDelay);
193  NS_LOG_INFO ("Synchronize ns = " << ns);
194 //
195 // Once we've decided on how long we need to delay, we need to split this
196 // time into sleep waits and busy waits. The reason for this is described
197 // in the comments for the constructor where jiffies and jiffy resolution is
198 // explained.
199 //
200 // Here, I'll just say that we need that the jiffy is the minimum resolution
201 // of the system clock. It can only sleep in blocks of time equal to a jiffy.
202 // If we want to be more accurate than a jiffy (we do) then we need to sleep
203 // for some number of jiffies and then busy wait for any leftover time.
204 //
205  uint64_t numberJiffies = ns / m_jiffy;
206  NS_LOG_INFO ("Synchronize numberJiffies = " << numberJiffies);
207 //
208 // This is where the real world interjects its very ugly head. The code
209 // immediately below reflects the fact that a sleep is actually quite probably
210 // going to end up sleeping for some number of jiffies longer than you wanted.
211 // This is because your system is going to be off doing other unimportant
212 // stuff during that extra time like running file systems and networks. What
213 // we want to do is to ask the system to sleep enough less than the requested
214 // delay so that it comes back early most of the time (coming back early is
215 // fine, coming back late is bad). If we can convince the system to come back
216 // early (most of the time), then we can busy-wait until the requested
217 // completion time actually comes around (most of the time).
218 //
219 // The tradeoff here is, of course, that the less time we spend sleeping, the
220 // more accurately we will sync up; but the more CPU time we will spend busy
221 // waiting (doing nothing).
222 //
223 // I'm not really sure about this number -- a boss of mine once said, "pick
224 // a number and it'll be wrong." But this works for now.
225 //
226 // \todo Hardcoded tunable parameter below.
227 //
228  if (numberJiffies > 3)
229  {
230  NS_LOG_INFO ("SleepWait for " << numberJiffies * m_jiffy << " ns");
231  NS_LOG_INFO ("SleepWait until " << nsCurrent + numberJiffies * m_jiffy
232  << " ns");
233 //
234 // SleepWait is interruptible. If it returns true it meant that the sleep
235 // went until the end. If it returns false, it means that the sleep was
236 // interrupted by a Signal. In this case, we need to return and let the
237 // simulator re-evaluate what to do.
238 //
239  if (SleepWait ((numberJiffies - 3) * m_jiffy) == false)
240  {
241  NS_LOG_INFO ("SleepWait interrupted");
242  return false;
243  }
244  }
245  NS_LOG_INFO ("Done with SleepWait");
246 //
247 // We asked the system to sleep for some number of jiffies, but that doesn't
248 // mean we actually did. Let's re-evaluate what we need to do here. Maybe
249 // we're already late. Probably the "real" delay time left has little to do
250 // with what we would calculate it to be naively.
251 //
252 // We are now at some Realtime. The important question now is not, "what
253 // would we calculate in a mathematicians paradise," it is, "how many
254 // nanoseconds do we need to busy-wait until we get to the Realtime that
255 // corresponds to nsCurrent + nsDelay (in simulation time). We have a handy
256 // function to do just that -- we ask for the time the realtime clock has
257 // drifted away from the simulation clock. That's our answer. If the drift
258 // is negative, we're early and we need to busy wait for that number of
259 // nanoseconds. The place were we want to be is described by the parameters
260 // we were passed by the simulator.
261 //
262  int64_t nsDrift = DoGetDrift (nsCurrent + nsDelay);
263 //
264 // If the drift is positive, we are already late and we need to just bail out
265 // of here as fast as we can. Return true to indicate that the requested time
266 // has, in fact, passed.
267 //
268  if (nsDrift >= 0)
269  {
270  NS_LOG_INFO ("Back from SleepWait: IML8 " << nsDrift);
271  return true;
272  }
273 //
274 // There are some number of nanoseconds left over and we need to wait until
275 // the time defined by nsDrift. We'll do a SpinWait since the usual case
276 // will be that we are doing this Spinwait after we've gotten a rough delay
277 // using the SleepWait above. If SpinWait completes to the end, it will
278 // return true; if it is interrupted by a signal it will return false.
279 //
280  NS_LOG_INFO ("SpinWait until " << nsCurrent + nsDelay);
281  return SpinWait (nsCurrent + nsDelay);
282 }
283 
284 void
286 {
287  NS_LOG_FUNCTION (this);
288 
289  m_condition.SetCondition (true);
290  m_condition.Signal ();
291 }
292 
293 void
295 {
296  NS_LOG_FUNCTION (this << cond);
297  m_condition.SetCondition (cond);
298 }
299 
300 void
302 {
303  NS_LOG_FUNCTION (this);
305 }
306 
307 uint64_t
309 {
310  NS_LOG_FUNCTION (this);
312 }
313 
314 bool
316 {
317  NS_LOG_FUNCTION (this << ns);
318 // We just sit here and spin, wasting CPU cycles until we get to the right
319 // time or are told to leave.
320  for (;;)
321  {
322  if (GetNormalizedRealtime () >= ns)
323  {
324  return true;
325  }
326  if (m_condition.GetCondition ())
327  {
328  return false;
329  }
330  }
331 // Quiet compiler
332  return true;
333 }
334 
335 bool
337 {
338  NS_LOG_FUNCTION (this << ns);
339  return m_condition.TimedWait (ns);
340 }
341 
342 uint64_t
343 WallClockSynchronizer::DriftCorrect (uint64_t nsNow, uint64_t nsDelay)
344 {
345  NS_LOG_FUNCTION (this << nsNow << nsDelay);
346  int64_t drift = DoGetDrift (nsNow);
347 //
348 // If we're running late, drift will be positive and we need to correct by
349 // delaying for less time. If we're early for some bizarre reason, we don't
350 // do anything since we'll almost instantly self-correct.
351 //
352  if (drift < 0)
353  {
354  return nsDelay;
355  }
356 //
357 // If we've drifted out of sync by less than the requested delay, then just
358 // subtract the drift from the delay and fix up the drift in one go. If we
359 // have more drift than delay, then we just play catch up as fast as possible
360 // by not delaying at all.
361 //
362  uint64_t correction = (uint64_t)drift;
363  if (correction <= nsDelay)
364  {
365  return nsDelay - correction;
366  }
367  else
368  {
369  return 0;
370  }
371 }
372 
373 uint64_t
375 {
376  NS_LOG_FUNCTION (this);
377  struct timeval tvNow;
378  gettimeofday (&tvNow, NULL);
379  return TimevalToNs (&tvNow);
380 }
381 
382 uint64_t
384 {
385  NS_LOG_FUNCTION (this);
386  return GetRealtime () - m_realtimeOriginNano;
387 }
388 
389 void
390 WallClockSynchronizer::NsToTimeval (int64_t ns, struct timeval *tv)
391 {
392  NS_LOG_FUNCTION (this << ns << tv);
393  NS_ASSERT ((ns % US_PER_NS) == 0);
394  tv->tv_sec = static_cast<long> (ns / NS_PER_SEC);
395  tv->tv_usec = (ns % NS_PER_SEC) / US_PER_NS;
396 }
397 
398 uint64_t
400 {
401  NS_LOG_FUNCTION (this << tv);
402  uint64_t nsResult = tv->tv_sec * NS_PER_SEC + tv->tv_usec * US_PER_NS;
403  NS_ASSERT ((nsResult % US_PER_NS) == 0);
404  return nsResult;
405 }
406 
407 void
409  struct timeval *tv1,
410  struct timeval *tv2,
411  struct timeval *result)
412 {
413  NS_LOG_FUNCTION (this << tv1 << tv2 << result);
414  result->tv_sec = tv1->tv_sec + tv2->tv_sec;
415  result->tv_usec = tv1->tv_usec + tv2->tv_usec;
416  if (result->tv_usec > (int64_t)US_PER_SEC)
417  {
418  ++result->tv_sec;
419  result->tv_usec %= US_PER_SEC;
420  }
421 }
422 
423 } // namespace ns3
ns3::TypeId
a unique identifier for an interface.
Definition: type-id.h:59
NS_LOG_COMPONENT_DEFINE
#define NS_LOG_COMPONENT_DEFINE(name)
Define a Log component with a specific name.
Definition: log.h:205
ns3::WallClockSynchronizer::DoSetOrigin
virtual void DoSetOrigin(uint64_t ns)
Establish a correspondence between a simulation time and a wall-clock (real) time.
Definition: wall-clock-synchronizer.cc:111
NS_OBJECT_ENSURE_REGISTERED
#define NS_OBJECT_ENSURE_REGISTERED(type)
Register an Object subclass with the TypeId system.
Definition: object-base.h:45
NS_ASSERT
#define NS_ASSERT(condition)
At runtime, in debugging builds, if this condition is not true, the program prints the source file,...
Definition: assert.h:67
ns3::SystemCondition::SetCondition
void SetCondition(bool condition)
Set the value of the underlying condition.
Definition: unix-system-condition.cc:222
ns3::WallClockSynchronizer::DoSynchronize
virtual bool DoSynchronize(uint64_t nsCurrent, uint64_t nsDelay)
Wait until the real time is in sync with the specified simulation time.
Definition: wall-clock-synchronizer.cc:167
ns3
Every class exported by the ns3 library is enclosed in the ns3 namespace.
ns3::WallClockSynchronizer::DriftCorrect
uint64_t DriftCorrect(uint64_t nsNow, uint64_t nsDelay)
Compute a correction to the nominal delay to account for realtime drift since the last DoSynchronize.
Definition: wall-clock-synchronizer.cc:343
ns3::WallClockSynchronizer::GetNormalizedRealtime
uint64_t GetNormalizedRealtime(void)
Get the current normalized real time, in ns.
Definition: wall-clock-synchronizer.cc:383
ns3::Synchronizer
Base class used for synchronizing the simulation events to some real time "wall clock....
Definition: synchronizer.h:52
ns3::WallClockSynchronizer::m_jiffy
uint64_t m_jiffy
Size of the system clock tick, as reported by clock_getres, in ns.
Definition: wall-clock-synchronizer.h:193
ns3::WallClockSynchronizer::GetTypeId
static TypeId GetTypeId(void)
Get the registered TypeId for this class.
Definition: wall-clock-synchronizer.cc:42
ns3::WallClockSynchronizer::US_PER_SEC
static const uint64_t US_PER_SEC
Conversion constant between μs and seconds.
Definition: wall-clock-synchronizer.h:87
ns3::TypeId::SetParent
TypeId SetParent(TypeId tid)
Set the parent TypeId.
Definition: type-id.cc:923
ns3::WallClockSynchronizer::SpinWait
bool SpinWait(uint64_t ns)
Do a busy-wait until the normalized realtime equals the argument or the condition variable becomes tr...
Definition: wall-clock-synchronizer.cc:315
ns3::SystemCondition::Signal
void Signal(void)
Release one thread if waiting for the condition to be true.
Definition: unix-system-condition.cc:236
ns3::WallClockSynchronizer::NS_PER_SEC
static const uint64_t NS_PER_SEC
Conversion constant between ns and s.
Definition: wall-clock-synchronizer.h:89
NS_LOG_INFO
#define NS_LOG_INFO(msg)
Use NS_LOG to output a message of level LOG_INFO.
Definition: log.h:281
wall-clock-synchronizer.h
ns3::WallClockSynchronizer declaration.
ns3::WallClockSynchronizer::GetRealtime
uint64_t GetRealtime(void)
Get the current absolute real time (in ns since the epoch).
Definition: wall-clock-synchronizer.cc:374
ns3::WallClockSynchronizer::DoEventStart
virtual void DoEventStart(void)
Record the normalized real time at which the current event is starting execution.
Definition: wall-clock-synchronizer.cc:301
ns3::WallClockSynchronizer::DoSetCondition
virtual void DoSetCondition(bool cond)
Set the condition variable to tell a possible simulator thread waiting in the Synchronize method that...
Definition: wall-clock-synchronizer.cc:294
ns3::WallClockSynchronizer::TimevalToNs
uint64_t TimevalToNs(struct timeval *tv)
Convert a timeval to absolute time, in ns.
Definition: wall-clock-synchronizer.cc:399
ns3::WallClockSynchronizer::WallClockSynchronizer
WallClockSynchronizer()
Constructor.
Definition: wall-clock-synchronizer.cc:51
ns3::WallClockSynchronizer::DoRealtime
virtual bool DoRealtime(void)
Return true if this synchronizer is actually synchronizing to a realtime clock.
Definition: wall-clock-synchronizer.cc:97
ns3::WallClockSynchronizer::DoGetDrift
virtual int64_t DoGetDrift(uint64_t ns)
Get the drift between the real time clock used to synchronize the simulation and the current simulati...
Definition: wall-clock-synchronizer.cc:127
ns3::WallClockSynchronizer::SleepWait
bool SleepWait(uint64_t ns)
Put our process to sleep for some number of nanoseconds.
Definition: wall-clock-synchronizer.cc:336
system-condition.h
ns3::SystemCondition declaration.
log.h
Debug message logging.
ns3::WallClockSynchronizer::DoGetCurrentRealtime
virtual uint64_t DoGetCurrentRealtime(void)
Retrieve the value of the origin of the underlying normalized wall clock time in Time resolution unit...
Definition: wall-clock-synchronizer.cc:104
ns3::SystemCondition::TimedWait
bool TimedWait(uint64_t ns)
Wait a maximum of ns nanoseconds for the condition to be true.
Definition: unix-system-condition.cc:257
ns3::WallClockSynchronizer::DoSignal
virtual void DoSignal(void)
Tell a possible simulator thread waiting in the DoSynchronize method that an event has happened which...
Definition: wall-clock-synchronizer.cc:285
ns3::WallClockSynchronizer::~WallClockSynchronizer
virtual ~WallClockSynchronizer()
Destructor.
Definition: wall-clock-synchronizer.cc:91
NS_LOG_FUNCTION
#define NS_LOG_FUNCTION(parameters)
If log level LOG_FUNCTION is enabled, this macro will output all input parameters separated by ",...
Definition: log-macros-enabled.h:244
ns3::Synchronizer::m_realtimeOriginNano
uint64_t m_realtimeOriginNano
The real time, in ns, when SetOrigin was called.
Definition: synchronizer.h:314
ns3::WallClockSynchronizer::m_nsEventStart
uint64_t m_nsEventStart
Time recorded by DoEventStart.
Definition: wall-clock-synchronizer.h:195
ns3::WallClockSynchronizer::TimevalAdd
void TimevalAdd(struct timeval *tv1, struct timeval *tv2, struct timeval *result)
Add two timeval.
Definition: wall-clock-synchronizer.cc:408
ns3::WallClockSynchronizer::NsToTimeval
void NsToTimeval(int64_t ns, struct timeval *tv)
Convert an absolute time in ns to a timeval.
Definition: wall-clock-synchronizer.cc:390
ns3::SystemCondition::GetCondition
bool GetCondition(void)
Get the value of the underlying condition.
Definition: unix-system-condition.cc:229
ns3::WallClockSynchronizer::US_PER_NS
static const uint64_t US_PER_NS
Conversion constant between μs and ns.
Definition: wall-clock-synchronizer.h:85
ns3::WallClockSynchronizer::m_condition
SystemCondition m_condition
Thread synchronizer.
Definition: wall-clock-synchronizer.h:198
ns3::WallClockSynchronizer::DoEventEnd
virtual uint64_t DoEventEnd(void)
Return the amount of real time elapsed since the last call to EventStart.
Definition: wall-clock-synchronizer.cc:308