A Discrete-Event Network Simulator
Home
Tutorials ▼
English
Portuguese
Docs ▼
Wiki
Manual
Models
Develop ▼
API
Bugs
API
Main Page
Related Pages
Modules
Namespaces
Classes
Files
File List
File Members
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
25
#include "
wall-clock-synchronizer.h
"
26
27
NS_LOG_COMPONENT_DEFINE
(
"WallClockSynchronizer"
);
28
29
namespace
ns3 {
30
31
WallClockSynchronizer::WallClockSynchronizer
()
32
{
33
NS_LOG_FUNCTION_NOARGS
();
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
71
WallClockSynchronizer::~WallClockSynchronizer
()
72
{
73
NS_LOG_FUNCTION_NOARGS
();
74
}
75
76
bool
77
WallClockSynchronizer::DoRealtime
(
void
)
78
{
79
NS_LOG_FUNCTION_NOARGS
();
80
return
true
;
81
}
82
83
uint64_t
84
WallClockSynchronizer::DoGetCurrentRealtime
(
void
)
85
{
86
NS_LOG_FUNCTION_NOARGS
();
87
return
GetNormalizedRealtime
();
88
}
89
90
void
91
WallClockSynchronizer::DoSetOrigin
(uint64_t ns)
92
{
93
NS_LOG_FUNCTION_NOARGS
();
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
//
102
m_realtimeOriginNano
=
GetRealtime
();
103
NS_LOG_INFO
(
"origin = "
<<
m_realtimeOriginNano
);
104
}
105
106
int64_t
107
WallClockSynchronizer::DoGetDrift
(uint64_t ns)
108
{
109
NS_LOG_FUNCTION_NOARGS
();
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_NOARGS
();
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
265
WallClockSynchronizer::DoSignal
(
void
)
266
{
267
NS_LOG_FUNCTION_NOARGS
();
268
269
m_condition
.
SetCondition
(
true
);
270
m_condition
.
Signal
();
271
}
272
273
void
274
WallClockSynchronizer::DoSetCondition
(
bool
cond)
275
{
276
NS_LOG_FUNCTION_NOARGS
();
277
m_condition
.
SetCondition
(cond);
278
}
279
280
void
281
WallClockSynchronizer::DoEventStart
(
void
)
282
{
283
NS_LOG_FUNCTION_NOARGS
();
284
m_nsEventStart
=
GetNormalizedRealtime
();
285
}
286
287
uint64_t
288
WallClockSynchronizer::DoEventEnd
(
void
)
289
{
290
NS_LOG_FUNCTION_NOARGS
();
291
return
GetNormalizedRealtime
() -
m_nsEventStart
;
292
}
293
294
bool
295
WallClockSynchronizer::SpinWait
(uint64_t ns)
296
{
297
NS_LOG_FUNCTION_NOARGS
();
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
323
WallClockSynchronizer::SleepWait
(uint64_t ns)
324
{
325
NS_LOG_FUNCTION_NOARGS
();
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
379
WallClockSynchronizer::GetRealtime
(
void
)
380
{
381
struct
timeval tvNow;
382
gettimeofday (&tvNow, NULL);
383
return
TimevalToNs
(&tvNow);
384
}
385
386
uint64_t
387
WallClockSynchronizer::GetNormalizedRealtime
(
void
)
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
401
WallClockSynchronizer::TimevalToNs
(
struct
timeval *tv)
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
409
WallClockSynchronizer::TimevalAdd
(
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
src
core
model
wall-clock-synchronizer.cc
Generated on Tue Oct 9 2012 16:45:35 for ns-3 by
1.8.1.2