A Discrete-Event Network Simulator
API
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 #include <ctime> // for clock_getres
20 #include <sys/time.h>
21 
22 #include "log.h"
23 #include "system-condition.h"
24 
26 
27 NS_LOG_COMPONENT_DEFINE ("WallClockSynchronizer");
28 
29 namespace ns3 {
30 
32 {
33  NS_LOG_FUNCTION (this);
34 //
35 // In Linux, the basic timekeeping unit is derived from a variable called HZ
36 // HZ is the frequency in hertz of the system timer. The system timer fires
37 // every 1/HZ seconds and a counter, called the jiffies counter is incremented
38 // at each tick. The time between ticks is called a jiffy (American slang for
39 // a short period of time). The ticking of the jiffies counter is how the
40 // the kernel tells time.
41 //
42 // Now, the shortest time the kernel can sleep is one jiffy since a timer
43 // has to be set to expire and trigger the process to be made ready. The
44 // Posix clock CLOCK_REALTIME is defined as a 1/HZ clock, so by doing a
45 // clock_getres () on the realtime clock we can infer the scheduler quantum
46 // and the minimimum sleep time for the system. This is most certainly NOT
47 // going to be one nanosecond even though clock_nanosleep () pretends it is.
48 //
49 // The reason this number is important is that we are going to schedule lots
50 // of waits for less time than a jiffy. The clock_nanosleep function is
51 // going to guarantee that it will sleep for AT LEAST the time specified.
52 // The least time that it will sleep is a jiffy.
53 //
54 // In order to deal with this, we are going to do a spin-wait if the simulator
55 // requires a delay less than a jiffy. This is on the order of one millisecond
56 // (999848 ns) on the ns-regression machine.
57 //
58 // If the underlying OS does not support posix clocks, we'll just assume a
59 // one millisecond quantum and deal with this as best we can
60 
61 #ifdef CLOCK_REALTIME
62  struct timespec ts;
63  clock_getres (CLOCK_REALTIME, &ts);
64  m_jiffy = ts.tv_sec * NS_PER_SEC + ts.tv_nsec;
65  NS_LOG_INFO ("Jiffy is " << m_jiffy << " ns");
66 #else
67  m_jiffy = 1000000;
68 #endif
69 }
70 
72 {
73  NS_LOG_FUNCTION (this);
74 }
75 
76 bool
78 {
79  NS_LOG_FUNCTION (this);
80  return true;
81 }
82 
83 uint64_t
85 {
86  NS_LOG_FUNCTION (this);
87  return GetNormalizedRealtime ();
88 }
89 
90 void
92 {
93  NS_LOG_FUNCTION (this << ns);
94 //
95 // In order to make sure we're really locking the simulation time to some
96 // wall-clock time, we need to be able to compare that simulation time to
97 // that wall-clock time. The wall clock will have been running for some
98 // long time and will probably have a huge count of nanoseconds in it. We
99 // save the real time away so we can subtract it from "now" later and get
100 // a count of nanoseconds in real time since the simulation started.
101 //
103  NS_LOG_INFO ("origin = " << m_realtimeOriginNano);
104 }
105 
106 int64_t
108 {
109  NS_LOG_FUNCTION (this << ns);
110 //
111 // In order to make sure we're really locking the simulation time to some
112 // wall-clock time, we need to be able to compare that simulation time to
113 // that wall-clock time. In DoSetOrigin we saved the real time at the start
114 // of the simulation away. This is the place where we subtract it from "now"
115 // to a count of nanoseconds in real time since the simulation started. We
116 // then subtract the current real time in normalized nanoseconds we just got
117 // from the normalized simulation time in nanoseconds that is passed in as
118 // the parameter ns. We return an integer difference, but in reality all of
119 // the mechanisms that cause wall-clock to simuator time drift cause events
120 // to be late. That means that the wall-clock will be higher than the
121 // simulation time and drift will be positive. I would be astonished to
122 // see a negative drift, but the possibility is admitted for other
123 // implementations; and we'll use the ability to report an early result in
124 // DoSynchronize below.
125 //
126  uint64_t nsNow = GetNormalizedRealtime ();
127 
128  if (nsNow > ns)
129  {
130 //
131 // Real time (nsNow) is larger/later than the simulator time (ns). We are
132 // behind real time and the difference (drift) is positive.
133 //
134  return (int64_t)(nsNow - ns);
135  }
136  else
137  {
138 //
139 // Real time (nsNow) is smaller/earlier than the simulator time (ns). We are
140 // ahead of real time and the difference (drift) is negative.
141 //
142  return -(int64_t)(ns - nsNow);
143  }
144 }
145 
146 bool
147 WallClockSynchronizer::DoSynchronize (uint64_t nsCurrent, uint64_t nsDelay)
148 {
149  NS_LOG_FUNCTION (this << nsCurrent << nsDelay);
150 //
151 // This is the belly of the beast. We have received two parameters from the
152 // simulator proper -- a current simulation time (nsCurrent) and a simulation
153 // time to delay which identifies the time the next event is supposed to fire.
154 //
155 // The first thing we need to do is to (try and) correct for any realtime
156 // drift that has happened in the system. In this implementation, we realize
157 // that all mechanisms for drift will cause the drift to be such that the
158 // realtime is greater than the simulation time. This typically happens when
159 // our process is put to sleep for a given time, but actually sleeps for
160 // longer. So, what we want to do is to "catch up" to realtime and delay for
161 // less time than we are actually asked to delay. DriftCorrect will return a
162 // number from 0 to nsDelay corresponding to the amount of catching-up we
163 // need to do. If we are more than nsDelay behind, we do not wait at all.
164 //
165 // Note that it will be impossible to catch up if the amount of drift is
166 // cumulatively greater than the amount of delay between events. The method
167 // GetDrift () is available to clients of the syncrhonizer to keep track of
168 // the cumulative drift. The client can assert if the drift gets out of
169 // hand, print warning messages, or just ignore the situation and hope it will
170 // go away.
171 //
172  uint64_t ns = DriftCorrect (nsCurrent, nsDelay);
173  NS_LOG_INFO ("Synchronize ns = " << ns);
174 //
175 // Once we've decided on how long we need to delay, we need to split this
176 // time into sleep waits and busy waits. The reason for this is described
177 // in the comments for the constructor where jiffies and jiffy resolution is
178 // explained.
179 //
180 // Here, I'll just say that we need that the jiffy is the minimum resolution
181 // of the system clock. It can only sleep in blocks of time equal to a jiffy.
182 // If we want to be more accurate than a jiffy (we do) then we need to sleep
183 // for some number of jiffies and then busy wait for any leftover time.
184 //
185  uint64_t numberJiffies = ns / m_jiffy;
186  NS_LOG_INFO ("Synchronize numberJiffies = " << numberJiffies);
187 //
188 // This is where the real world interjects its very ugly head. The code
189 // immediately below reflects the fact that a sleep is actually quite probably
190 // going to end up sleeping for some number of jiffies longer than you wanted.
191 // This is because your system is going to be off doing other unimportant
192 // stuff during that extra time like running file systems and networks. What
193 // we want to do is to ask the system to sleep enough less than the requested
194 // delay so that it comes back early most of the time (coming back early is
195 // fine, coming back late is bad). If we can convince the system to come back
196 // early (most of the time), then we can busy-wait until the requested
197 // completion time actually comes around (most of the time).
198 //
199 // The tradeoff here is, of course, that the less time we spend sleeping, the
200 // more accurately we will sync up; but the more CPU time we will spend busy
201 // waiting (doing nothing).
202 //
203 // I'm not really sure about this number -- a boss of mine once said, "pick
204 // a number and it'll be wrong." But this works for now.
205 //
206 // \todo Hardcoded tunable parameter below.
207 //
208  if (numberJiffies > 3)
209  {
210  NS_LOG_INFO ("SleepWait for " << numberJiffies * m_jiffy << " ns");
211  NS_LOG_INFO ("SleepWait until " << nsCurrent + numberJiffies * m_jiffy
212  << " ns");
213 //
214 // SleepWait is interruptible. If it returns true it meant that the sleep
215 // went until the end. If it returns false, it means that the sleep was
216 // interrupted by a Signal. In this case, we need to return and let the
217 // simulator re-evaluate what to do.
218 //
219  if (SleepWait ((numberJiffies - 3) * m_jiffy) == false)
220  {
221  NS_LOG_INFO ("SleepWait interrupted");
222  return false;
223  }
224  }
225  NS_LOG_INFO ("Done with SleepWait");
226 //
227 // We asked the system to sleep for some number of jiffies, but that doesn't
228 // mean we actually did. Let's re-evaluate what we need to do here. Maybe
229 // we're already late. Probably the "real" delay time left has little to do
230 // with what we would calculate it to be naively.
231 //
232 // We are now at some Realtime. The important question now is not, "what
233 // would we calculate in a mathematicians paradise," it is, "how many
234 // nanoseconds do we need to busy-wait until we get to the Realtime that
235 // corresponds to nsCurrent + nsDelay (in simulation time). We have a handy
236 // function to do just that -- we ask for the time the realtime clock has
237 // drifted away from the simulation clock. That's our answer. If the drift
238 // is negative, we're early and we need to busy wait for that number of
239 // nanoseconds. The place were we want to be is described by the parameters
240 // we were passed by the simulator.
241 //
242  int64_t nsDrift = DoGetDrift (nsCurrent + nsDelay);
243 //
244 // If the drift is positive, we are already late and we need to just bail out
245 // of here as fast as we can. Return true to indicate that the requested time
246 // has, in fact, passed.
247 //
248  if (nsDrift >= 0)
249  {
250  NS_LOG_INFO ("Back from SleepWait: IML8 " << nsDrift);
251  return true;
252  }
253 //
254 // There are some number of nanoseconds left over and we need to wait until
255 // the time defined by nsDrift. We'll do a SpinWait since the usual case
256 // will be that we are doing this Spinwait after we've gotten a rough delay
257 // using the SleepWait above. If SpinWait completes to the end, it will
258 // return true; if it is interrupted by a signal it will return false.
259 //
260  NS_LOG_INFO ("SpinWait until " << nsCurrent + nsDelay);
261  return SpinWait (nsCurrent + nsDelay);
262 }
263 
264 void
266 {
267  NS_LOG_FUNCTION (this);
268 
269  m_condition.SetCondition (true);
270  m_condition.Signal ();
271 }
272 
273 void
275 {
276  NS_LOG_FUNCTION (this << cond);
277  m_condition.SetCondition (cond);
278 }
279 
280 void
282 {
283  NS_LOG_FUNCTION (this);
285 }
286 
287 uint64_t
289 {
290  NS_LOG_FUNCTION (this);
292 }
293 
294 bool
296 {
297  NS_LOG_FUNCTION (this << ns);
298 //
299 // Do a busy-wait until the normalized realtime equals the value passed in
300 // or the condition variable becomes true. The condition becomes true if
301 // an outside entity (a network device receives a packet, sets the condition
302 // and signals the scheduler it needs to re-evaluate).
303 //
304 // We just sit here and spin, wasting CPU cycles until we get to the right
305 // time or are told to leave.
306 //
307  for (;;)
308  {
309  if (GetNormalizedRealtime () >= ns)
310  {
311  return true;
312  }
313  if (m_condition.GetCondition ())
314  {
315  return false;
316  }
317  }
318 // Quiet compiler
319  return true;
320 }
321 
322 bool
324 {
325  NS_LOG_FUNCTION (this << ns);
326 //
327 // Put our process to sleep for some number of nanoseconds. Typically this
328 // will be some time equal to an integral number of jiffies. We will usually
329 // follow a call to SleepWait with a call to SpinWait to get the kind of
330 // accuracy we want.
331 //
332 // We have to have some mechanism to wake up this sleep in case an external
333 // event happens that causes a schedule event in the simulator. This newly
334 // scheduled event might be before the time we are waiting until, so we have
335 // to break out of both the SleepWait and the following SpinWait to go back
336 // and reschedule/resynchronize taking the new event into account. The
337 // SystemCondition we have saved in m_condition takes care of this for us.
338 //
339 // This call will return if the timeout expires OR if the condition is
340 // set true by a call to WallClockSynchronizer::SetCondition (true) followed
341 // by a call to WallClockSynchronizer::Signal(). In either case, we are done
342 // waiting. If the timeout happened, we TimedWait returns true; if a Signal
343 // happened, false.
344 //
345  return m_condition.TimedWait (ns);
346 }
347 
348 uint64_t
349 WallClockSynchronizer::DriftCorrect (uint64_t nsNow, uint64_t nsDelay)
350 {
351  NS_LOG_FUNCTION (this << nsNow << nsDelay);
352  int64_t drift = DoGetDrift (nsNow);
353 //
354 // If we're running late, drift will be positive and we need to correct by
355 // delaying for less time. If we're early for some bizarre reason, we don't
356 // do anything since we'll almost instantly self-correct.
357 //
358  if (drift < 0)
359  {
360  return nsDelay;
361  }
362 //
363 // If we've drifted out of sync by less than the requested delay, then just
364 // subtract the drift from the delay and fix up the drift in one go. If we
365 // have more drift than delay, then we just play catch up as fast as possible
366 // by not delaying at all.
367 //
368  uint64_t correction = (uint64_t)drift;
369  if (correction <= nsDelay)
370  {
371  return nsDelay - correction;
372  }
373  else
374  {
375  return 0;
376  }
377 }
378 
379 uint64_t
381 {
382  NS_LOG_FUNCTION (this);
383  struct timeval tvNow;
384  gettimeofday (&tvNow, NULL);
385  return TimevalToNs (&tvNow);
386 }
387 
388 uint64_t
390 {
391  NS_LOG_FUNCTION (this);
392  return GetRealtime () - m_realtimeOriginNano;
393 }
394 
395 void
396 WallClockSynchronizer::NsToTimeval (int64_t ns, struct timeval *tv)
397 {
398  NS_LOG_FUNCTION (this << ns << tv);
399  NS_ASSERT ((ns % US_PER_NS) == 0);
400  tv->tv_sec = ns / NS_PER_SEC;
401  tv->tv_usec = (ns % NS_PER_SEC) / US_PER_NS;
402 }
403 
404 uint64_t
406 {
407  NS_LOG_FUNCTION (this << tv);
408  uint64_t nsResult = tv->tv_sec * NS_PER_SEC + tv->tv_usec * US_PER_NS;
409  NS_ASSERT ((nsResult % US_PER_NS) == 0);
410  return nsResult;
411 }
412 
413 void
415  struct timeval *tv1,
416  struct timeval *tv2,
417  struct timeval *result)
418 {
419  NS_LOG_FUNCTION (this << tv1 << tv2 << result);
420  result->tv_sec = tv1->tv_sec + tv2->tv_sec;
421  result->tv_usec = tv1->tv_usec + tv2->tv_usec;
422  if (result->tv_usec > (int64_t)US_PER_SEC)
423  {
424  ++result->tv_sec;
425  result->tv_usec %= US_PER_SEC;
426  }
427 }
428 
429 } // namespace ns3
#define NS_LOG_FUNCTION(parameters)
Definition: log.h:345
#define NS_ASSERT(condition)
Definition: assert.h:64
uint64_t TimevalToNs(struct timeval *tv)
#define NS_LOG_INFO(msg)
Definition: log.h:298
static const uint64_t NS_PER_SEC
void Signal(void)
Release one thread if waiting for the condition to be true.
virtual int64_t DoGetDrift(uint64_t ns)
Declaration of method used to retrieve drift between the real time clock used to synchronize the simu...
virtual bool DoRealtime(void)
Return true if this synchronizer is actually synchronizing to a realtime clock.
void SetCondition(bool condition)
Set the value of the underlying condition.
virtual uint64_t DoGetCurrentRealtime(void)
Retrieve the value of the origin of the underlying normalized wall clock time in nanosecond units...
virtual bool DoSynchronize(uint64_t nsCurrent, uint64_t nsDelay)
Wait until the real time is in sync with the specified simulation time.
NS_LOG_COMPONENT_DEFINE("WallClockSynchronizer")
bool GetCondition(void)
Get the value of the underlying condition.
bool TimedWait(uint64_t ns)
Wait a maximum of ns nanoseconds for the condition to be true.
virtual void DoSetOrigin(uint64_t ns)
Establish a correspondence between a simulation time and a wall-clock (real) time.
static const uint64_t US_PER_SEC
uint64_t m_realtimeOriginNano
Definition: synchronizer.h:303
virtual void DoSetCondition(bool cond)
Declaration of the method used to set the condition variable that tells a possible simulator thread w...
void NsToTimeval(int64_t ns, struct timeval *tv)
void TimevalAdd(struct timeval *tv1, struct timeval *tv2, struct timeval *result)
virtual void DoSignal(void)
Declaration of the method used to tell a possible simulator thread waiting in the DoSynchronize metho...
uint64_t DriftCorrect(uint64_t nsNow, uint64_t nsDelay)