I'm trying to make a "safe" method of generating request ids for web sockets (just a desktop app not a real server) and want each socket to have its own id generator. All I'm doing is generating ids and recycling them after the request completes, such that the id doesn't grow unlimited throughout a user's session. I used an example concerning closures for a counter in JavaScript from David Flanagan's book and all seems to work well in Tcl but I'd greatly appreciate any advice on how to do this correctly and how I can test that these variables cannot be altered by the main program apart from calling one of the procedures within the namespaces. For example, is it possible to modify the gap
list under the WEBS::$sock
from the global namespace with [meant without] calling one of the procedures? Thank you.
Also, is there any difference between declaring namespace eval WEBS {}
outside proc. ReqIdGenerator
and using namespace eval WEBS::$sock
inside the procedure? I can see that the results are the same for my little tests but wondered if there was any differences otherwise.
As an aside, in JS using the push and pop methods of arrays, it seems easier to recycle ids on a last-in-first-out basis; but using Tcl lists, it seems easier to use a first-in-first-out basis because using lassign
with one variable assigns index 0 to the variable and returns the remaining elements as a new list. The equivalent of array.pop() seems to require more steps. Is that a correct observation? Thank you.
WARNING:
There is an error in this code in that the namespace references $sock
and it works only because it is a global variable. If it were not global, the code would throw and error. The best I could find thus far is in this question.
proc ReqIdGenerator {sock} {
namespace eval WEBS {
namespace eval $sock {
variable max 0
variable gap {}
variable open {}
variable sock $sock
proc getId {} {
variable max
variable gap
variable open
if { [llength $gap] > 0 } {
set gap [lassign $gap id]
lappend open $id
return $id
} else {
lappend open [set id [incr max]]
return $id
}
chan puts stdout "Error in getId"
return -1
}
proc delId {id} {
variable max
variable gap
variable open
if { [set i [lsearch $open $id]] == -1 } {
return 1
} elseif { [llength $open] == 1 } {
reset
} else {
lappend gap [lindex $open $i]
set open [lreplace $open $i $i]
}
return 0
}
proc reset {} {
variable max 0
variable gap {}
variable open {}
}
proc getState {{prop "all"}} {
variable max
variable gap
variable open
variable sock
if { $prop eq "all" } {
return [list $max $gap $open]
} elseif { $prop eq "text" } {
return "State of socket $sock: max: $max; gap: $gap; open: $open"
} else {
return [set $prop]
}
}
}
}
}
set sock 123
ReqIdGenerator $sock
set sock 456
ReqIdGenerator $sock
# Add ids 1 through 10 to both sockets
for {set i 0} {$i<10} {incr i} {
WEBS::123::getId
WEBS::${sock}::getId
}
# Delete even ids from socket 456
for {set i 2 } {$i<11} {incr i 2} {
WEBS::${sock}::delId $i
}
# Delete odd ids from socket 123
for {set i 1 } {$i<10} {incr i 2} {
WEBS::123::delId $i
}
chan puts stdout [WEBS::123::getState text]
# => State of socket 123: max: 10; gap: 1 3 5 7 9; open: 2 4 6 8 10
chan puts stdout [WEBS::456::getState text]
# => State of socket 456: max: 10; gap: 2 4 6 8 10; open: 1 3 5 7 9