File: plm.tcl

package info (click to toggle)
ns2 2.35%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 78,120 kB
  • sloc: cpp: 172,923; tcl: 107,127; perl: 6,391; sh: 6,143; ansic: 5,846; makefile: 812; awk: 525; csh: 355
file content (402 lines) | stat: -rw-r--r-- 12,787 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
#This code is a contribution of Arnaud Legout, Institut Eurecom, France.
#As the basis, for writing my scripts, I use the RLM scripts included in 
#ns. Therefore I gratefully thanks Steven McCanne who makes its scripts
#publicly available and the various ns team members who clean and
#maintain the RLM scripts.
#The following copyright is the original copyright included in the RLM scripts.
#
# Copyright (c)1996 Regents of the University of California.
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
# 3. All advertising materials mentioning features or use of this software
#    must display the following acknowledgement:
# 	This product includes software developed by the MASH Research
# 	Group at the University of California Berkeley.
# 4. Neither the name of the University nor of the Research Group may be
#    used to endorse or promote products derived from this software without
#    specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# @(#) $Header: /cvsroot/nsnam/ns-2/tcl/plm/plm.tcl,v 1.1 2000/07/19 21:37:54 haoboy Exp $
#


#The PLM structure: 
#When we create a new receiver (place_receiver) we instantiate the 
#class PLM/ns which inherit from the class PLM. The class PLM creates 
#as many PLMLayer/ns instance (create-layer) as there are layers. The 
#class PLMLayer/ns creates an instance of PLMLossTrace (which is 
#reponsible for monitoring received packets and monitoring losses).

#The PLM class is intended to implement all the PLM protocol without any
#specific interface with ns. The specific ns interface is implemented in PLM/ns.
#There is a similar relation between PLMLayer and PLMLayer/ns. 
#However, we do not guarantee the strict validity of this ns interfacing.



#The PLM class implement the PLM protocol (see 
#http://www.eurecom.fr/~legout/Research/research.html 
#for details about the protocol evaluation)
Class PLM

PLM instproc init {levels chk_estimate n_id} {
    $self next
    $self instvar PP_estimate wait_loss time_loss 
    $self instvar start_loss time_estimate check_estimate node_id
    global rates
    set PP_estimate {} 
    set start_loss -1
    set wait_loss 0
    set time_loss 0
    set time_estimate 0
    set check_estimate $chk_estimate
    set node_id $n_id
    
	$self instvar debug_ env_ maxlevel_

	set debug_ 0
	set env_ [lindex [split [$self info class] /] 1]
	set maxlevel_ $levels

	#XXX
	global plm_debug_flag
	if [info exists plm_debug_flag] {
		set debug_ $plm_debug_flag
	}

	$self instvar subscription_

	#
	# we number the subscription level starting at 1.
	# level 0 means no groups are subscribed to.
	# 
	$self instvar layer_ layers_
	set i 1
	while { $i <= $maxlevel_ } {
		set layer_($i) [$self create-layer [expr $i - 1]]
		lappend layers_ $layer_($i)
		incr i
	}
	
	#
	# set the subscription level to 0 and call add_layer
	# to start out with at least one group
	#
	set subscription_ 0
	$self add-layer
}



#make_estimate makes an estimate PP_estimate_value by taking the minimum PP_value 
#received during a check_estimate period (and at least PP_estimation_length  
#PP_value received). This PP_estimate_value is used to choose a layer.
#For each PP_value, make_estimate makes a stability_drop i.e. PLM drops layer(s) if a 
#PP_value is lower than the current subscription level.
PLM instproc make_estimate {PP_value} {
    $self instvar PP_estimate PP_estimate_value ns_ time_estimate check_estimate debug_
    global PP_estimation_length
    
    #Add PP_value to the list of single PP estimates PP_estimate
    lappend PP_estimate $PP_value
    
    #Drop layer(s) if the PP_value is lower than the current subscription level
    $self stability-drop $PP_value

    #time_estimate is the minimum period of time during which we collect PP_value
    #to make the global estimate PP_estimate_value
    set ns_time [$ns_ now]
    if {$time_estimate==0} {
	set time_estimate [expr $ns_time + $check_estimate]
    }
    if {$debug_>=3} {
	trace_annotate "[$self node]: check: $check_estimate $PP_estimate , nb: [llength $PP_estimate]"
    }

    #if we have collected PP_value for at least time_estimate and we have at least 
    #PP_estimation_length, we calculate the PP_estimate_value
    if {($time_estimate<=$ns_time) && ([llength $PP_estimate] >= $PP_estimation_length)} {
	#we take the minimum
	set PP_estimate_value [lindex [lsort -real $PP_estimate] 0]
	if {$debug_>=3} {
	    trace_annotate "[$self node]: check: $check_estimate PP estim: $PP_estimate, value: $PP_estimate_value"
	}
	#puts stderr [set PP_estimate_value]
	#puts stderr [set PP_estimate]
	if {$debug_>=2} {
	    trace_annotate [expr round($PP_estimate_value)]
	}
	set PP_estimate {}
	#puts stderr "noeud: [$self node] check_estimate: $check_estimate"
	set time_estimate [expr $ns_time + $check_estimate]
	#choode the layer according to the PP_estimate_value
	$self choose_layer $PP_estimate_value
	
    }
}


#stability_drop drops layer(s) if a PP_value is lower than the current subscription level.
PLM instproc stability-drop {PP_value} {
    $self instvar subscription_ start_loss time_estimate PP_estimate
    $self instvar check_estimate ns_
    global rates_cum

    set ns_time [$ns_ now]
    #puts stderr $PP_value
    for {set i 0} {[lindex $rates_cum $i] < [expr round($PP_value)]} {incr i} {
	if {$i > [llength $rates_cum]} {break}
    }
    #puts stderr [lindex $rates_cum $i]
    #puts stderr $PP_estimate_value
    #puts stderr $i
    
    if {$subscription_ > $i} {
	for {set j $subscription_} {$i < $j} {incr j -1} {
	    set start_loss -1
	    $self drop-layer	    
	}
	set PP_estimate {}
	set time_estimate [expr $ns_time + $check_estimate]
    }
}

#calculate the cumulated rates. (usefull for choose_layer)
proc calc_cum {rates} {
    set temp 0
    set rates_cum {}
    for {set i 0} {$i<[llength $rates]} {incr i} {
	set temp [expr $temp + [lindex $rates $i]]
	lappend rates_cum $temp
    }
    return $rates_cum
}

#choose_layer chooses a layer according to the PP_estimate_value 
#and the current subscription level.
PLM instproc choose_layer {PP_estimate_value} {
    $self instvar subscription_ start_loss
    global rates_cum

    #A assume an estimate will better ajust the rate than dropping
    #a layer due to the losses
    set start_loss -1

    #puts stderr $PP_estimate_value
    for {set i 0} {[lindex $rates_cum $i] < [expr round($PP_estimate_value)]} {incr i} {
	if {$i > [llength $rates_cum]} {break}
    }
    #puts stderr [lindex $rates_cum $i]
    #puts stderr $PP_estimate_value
    #puts stderr $i
    
    if {$subscription_ < $i} {
	for {set j $subscription_} {$j < $i} {incr j} {
	    $self add-layer	    
	}	    
    } elseif {$subscription_ > $i} {
	for {set j $subscription_} {$i < $j} {incr j -1} {
	    $self drop-layer	    
	}
    } elseif {$subscription_ == $i} {
	return
    }
}


#In case of loss, log-loss is called. As only one PP_value allows to drop 
#the right number of layers (with stability_drop), log-loss is very conservative 
#i.e. only drop layer in case of high and sustained loss rate (PLM always gives 
#a chance to receive a PP_value before dropping a layer due to loss).
PLM instproc log-loss {} {
    $self instvar subscription_ h_npkts h_nlost start_loss debug_
    $self instvar time_loss ns_ wait_loss
   
    $self debug "LOSS [$self plm_loss]" 

    #puts "pkt_lost" in the output file for each packet (or burst) lost 
    if {$debug_>=2} {
	trace_annotate "$self pkt_lost"
    }
    set ns_time [$ns_ now]
    

    #start a new loss cycle. when start_loss is set to -1 we reinitialize the 
    #counter of the number of packets received h_npkts and the number of packets
    #lost h_nlost (that avoid old packets lost to contribute to the actual loss rate)
    if {$time_loss <= $ns_time} {
	if {$debug_>=2} {
	    trace_annotate "not enough losses during 1s: reinitialize"
	}
	set start_loss -1
    }

    #we reinitialize h_npkts and h_nlost each time start_loss=-1 and
    #each time there is a loss whereas we drop a layer less than 500ms apart.
    if {($start_loss == -1) || ($wait_loss >= $ns_time)} {
	if {$debug_>=2} {
	    trace_annotate "$start_loss [expr $wait_loss >= $ns_time] reinitialize"
	}
    	set h_npkts [$self plm_pkts]
	set h_nlost [$self plm_loss]
	set start_loss 1
	#we calculate the loss rate at most on a 5 second interval.
	set time_loss [expr [$ns_ now] + 5]
	if {$debug_>=2} {
	    trace_annotate "time_loss : $time_loss"
	}
    }

    #drop a layer if the loss exceed a threshold and if there was no layer drop 
    #the 500ms preceding.
    if {([$self exceed_loss_thresh]) && ($wait_loss <= $ns_time)} {
	$self drop-layer
	set start_loss -1
	#we cannot drop another layer before 500ms. 500ms is largely enough to avoid 
	#cascade drop due to spurious inference as PLM does not need the bottleneck queue
	#to drain, but just a PP to pass the bottleneck queue.
	set wait_loss [expr $ns_time + 0.5]
	if {$debug_>=2} {
	    trace_annotate "drop layer wait_loss: $wait_loss"
	}
    }
}

#The loss rate is only calculated for more than 10 packets received. The loss
#threshlod is 10%
PLM instproc exceed_loss_thresh {} {
	$self instvar h_npkts h_nlost debug_
	set npkts [expr [$self plm_pkts] - $h_npkts]
	if { $npkts >= 10 } {
		set nloss [expr [$self plm_loss] - $h_nlost]
		#XXX 0.4
		set loss [expr double($nloss) / ($nloss + $npkts)]
		$self debug "H-THRESH $nloss $npkts $loss"
		if { $loss > 0.10 } {
			return 1
		}
	}
	return 0
}


PLM instproc drop-layer {} {
    $self instvar subscription_ layer_ node_id debug_
    set n $subscription_

    #
    # if we have an active layer, drop it
    #
    if { $n > 0 } {
	$self debug "DRP-LAYER $n"
	$layer_($n) leave-group 
	incr n -1
	set subscription_ $n
	if {$debug_>=2} {
	    trace_annotate " [$self set node_id] : change layer $subscription_ "
	}
    }
    
    #rejoin the session after 30 seconds if drop all the layers
    if { $subscription_ == 0 } {
	set ns [Simulator instance]
	set rejoin_timer 30
	$ns at [expr [$ns now] + $rejoin_timer] "$self add-layer"
	if {$debug_>=2} {
	    trace_annotate " Try to re-join the session after dropping all the layers "
	}
    }
}

PLM instproc add-layer {} {
    $self instvar maxlevel_ subscription_ layer_ node_id debug_
    set n $subscription_
    if { $n < $maxlevel_ } {
	$self debug "ADD-LAYER"
	incr n
	set subscription_ $n
	$layer_($n) join-group
	if {$debug_>=2} {
	    trace_annotate " [$self set node_id] : change layer $subscription_ "
	}
    }
}

#
# return the amount of loss across all the groups of the given plm
#
PLM instproc plm_loss {} {
	$self instvar layers_
	set loss 0
	foreach l $layers_ {
		incr loss [$l nlost]
	}
	return $loss
}

#
# return the number of packets received across all the groups of the given plm
#
PLM instproc plm_pkts {} {
	$self instvar layers_
	set npkts 0
	foreach l $layers_ {
		incr npkts [$l npkts]
	}
	return $npkts
}

PLM instproc debug { msg } {
	$self instvar debug_ subscription_ ns_

	if {$debug_ <1} { return }
	set time [format %.05f [$ns_ now]]
	puts stderr "PLM: $time  layer $subscription_ $msg"
}

Class PLMLayer

PLMLayer instproc init { plm } {
	$self next

	$self instvar plm_ npkts_
	set plm_ $plm
	set npkts_ 0
	# loss trace created in constructor of derived class
}

PLMLayer instproc join-group {} {
	$self instvar npkts_ add_time_ plm_
	set npkts_ [$self npkts]
	set add_time_ [$plm_ now]
	# derived class actually joins group
}

PLMLayer instproc leave-group {} {
	# derived class actually leaves group
}

PLMLayer instproc getting-pkts {} {
	$self instvar npkts_
	return [expr [$self npkts] != $npkts_]
}