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 <time.h>
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 {
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 {
74 }
75 
76 bool
78 {
80  return true;
81 }
82 
83 uint64_t
85 {
87  return GetNormalizedRealtime ();
88 }
89 
90 void
92 {
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 {
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 {
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 // XXX BUGBUG 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 {
268 
269  m_condition.SetCondition (true);
270  m_condition.Signal ();
271 }
272 
273 void
275 {
277  m_condition.SetCondition (cond);
278 }
279 
280 void
282 {
285 }
286 
287 uint64_t
289 {
292 }
293 
294 bool
296 {
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 {
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  int64_t drift = DoGetDrift (nsNow);
352 //
353 // If we're running late, drift will be positive and we need to correct by
354 // delaying for less time. If we're early for some bizarre reason, we don't
355 // do anything since we'll almost instantly self-correct.
356 //
357  if (drift < 0)
358  {
359  return nsDelay;
360  }
361 //
362 // If we've drifted out of sync by less than the requested delay, then just
363 // subtract the drift from the delay and fix up the drift in one go. If we
364 // have more drift than delay, then we just play catch up as fast as possible
365 // by not delaying at all.
366 //
367  uint64_t correction = (uint64_t)drift;
368  if (correction <= nsDelay)
369  {
370  return nsDelay - correction;
371  }
372  else
373  {
374  return 0;
375  }
376 }
377 
378 uint64_t
380 {
381  struct timeval tvNow;
382  gettimeofday (&tvNow, NULL);
383  return TimevalToNs (&tvNow);
384 }
385 
386 uint64_t
388 {
389  return GetRealtime () - m_realtimeOriginNano;
390 }
391 
392 void
393 WallClockSynchronizer::NsToTimeval (int64_t ns, struct timeval *tv)
394 {
395  NS_ASSERT ((ns % US_PER_NS) == 0);
396  tv->tv_sec = ns / NS_PER_SEC;
397  tv->tv_usec = (ns % NS_PER_SEC) / US_PER_NS;
398 }
399 
400 uint64_t
402 {
403  uint64_t nsResult = tv->tv_sec * NS_PER_SEC + tv->tv_usec * US_PER_NS;
404  NS_ASSERT ((nsResult % US_PER_NS) == 0);
405  return nsResult;
406 }
407 
408 void
410  struct timeval *tv1,
411  struct timeval *tv2,
412  struct timeval *result)
413 {
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