This is a quick and dirty (and incomplete) example of a lock server for general resources. It doesn't implement the actual locking of the resources but can be used to (begin to) answer the question
How do I manage lock files in a cross platform manner in Tcl.
You lock a resource by sending "L:resource_name\n". You unlock a resource with "U:resource_name\n". Once you acquire the lock, you get back a message: "LOCKED". If you do not immediately acquire the lock you are placed in a queue until your turn (no message is sent until then).
Disconnecting from the server immediately releases any locks and any pending locks you may have.
--
Todd Coram
variable port 6700
# lock(resource) -> chan ..
# First chan int the list has the lock, the rest are queued.
array set lock [list]
# client(chan) -> resource
array set client [list]
# If you own the lock, return 1 else remove chan from queue and return 0
#
proc unlock {chan res} {
global lock client
if {[lindex $lock($res) 0] == $chan} {
set lock($res) [lrange $lock($res) 1 end]
set idx [lsearch -exact $client($chan) $res]
set client($chan) [lreplace $client($chan) $idx $idx]
# Notify the next in line
if {[llength $lock($res)] != 0} {
puts [lindex $lock($res) 0] "LOCKED $res"
}
return 1
}
set idx [lsearch -exact $lock($res) $chan]
if {$idx != -1} {
set lock($res) [lreplace $lock($res) $idx $idx]
}
return 0
}
# You will either aquire the lock (return 1) or be queued (return 0).
#
proc lock {chan res} {
global lock client
lappend lock($res) $chan
lappend client($chan) $res
return [locked? $chan $res]
}
proc locked? {chan res} {
global lock
if {[info exists lock($res)] && [lindex $lock($res) 0 ] == $chan} {
return 1
}
return 0
}
proc accept {chan addr port} {
global client
fconfigure $chan -buffering none
fileevent $chan readable [list handle_req $chan]
set client($chan) [list]
}
proc handle_req chan {
global client lock
if {[eof $chan]} {
# Unlock resources
foreach res $client($chan) {
unlock $chan $res
}
unset client($chan)
close $chan
return
}
set str [gets $chan]
foreach {req res} [split $str :] {
switch -- $req {
L {
puts stderr "($chan) Locking $res"
if {[lock $chan $res]} { puts $chan "LOCKED $res" }
}
U {
if {![info exists lock($res)]} {
puts $chan "NOLOCKS $res"
break
}
if {[unlock $chan $res]} {
puts $chan "UNLOCK $res"
} else {
puts $chan "DEQUEUED $res"
}
}
default {
puts $chan {HUH? Usage: L:resource or U:resource}
}
}
}
}
socket -server [list accept] $port
vwait forever