Overview | Index by: file name |
procedure name |
procedure call |
annotation
lib.tcl
(annotations | original source)
#
# Copyright (C) 2010 Alexandros Stergiakis <alsterg@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#//#
# A library of useful procedures for the Master and Sesssion interpreters.
#//#
################################
# Useful procedures
################################
# Equivalent the the C language's ? construct.
# If bool is true, then $true is returned, otherwise $false is returned.
#
# @param bool An expression that must be evaluated to either true or false.
# @param true Any string or list.
# @param false Any string or list.
# @return Either $true or $false.
proc ? {bool true {false {}}} {
if {[expr {$bool}]} {
return $true
} else {
return $false
}
}
# Remove an item from a list. Item specified by value.
#
# @param listName A list name
# @param item The name/value of the item to remove
proc lremove {listName item} {
upvar $listName lst
if {[set pos [lsearch -exact $lst $item]] == -1} {
return
}
set lst [lreplace $lst $pos $pos]
}
# Test if a string is empty.
#
# @param listValue A list name
# @return true if empty, otherwise false
proc lempty {listValue} {
expr {$listValue eq {}}
}
# Return the input text with extra formating based on ANSI escape codes.
# If ANSI escape codes are disabled for a session, then only the -underline
# parameter is supported (using special marking). The rest are ignored.
#
# @todo Support background color and the other ANSI codes.
# @param sid Session ID.
# @param args -bold <bool> -underline <bool> -crossed <bool> -italic <bool> -blinkslow <bool> -blinkrapid <bool> -franktur <bool> -color <color> -- <text>
# @return The input text formatted.
proc ansi {sid args} {
Session $sid ANSI
if {! $ANSI} {
# We only support -underline, using special marking.
set txt [lindex $args end]
if {"-underline" in [lrange $args 0 end-1]} {
set txt ">>> $txt <<<"
}
return $txt
}
set ESC "\033"
set color {}
set type {}
set suffix {}
for {set i 0} {$i < [llength $args]} {incr i} {
set arg [lindex $args $i]
switch -exact -- $arg {
"-bold" {
set type 1
append suffix "${ESC}\[22m"
}
"-underline" {
set type 4
append suffix "${ESC}\[24m"
}
"-crossed" {
set type 9
append suffix "${ESC}\[29m"
}
"-italic" {
set type 3
append suffix "${ESC}\[23m"
}
"-blinkslow" {
set type 5
append suffix "${ESC}\[25m"
}
"-blinkrapid" {
set type 6
append suffix "${ESC}\[25m"
}
"-franktur" {
set type 20
append suffix "${ESC}\[23m"
}
"-color" {
switch -exact -- [lindex $args [incr i]] {
"black" { set color {;30}}
"red" { set color {;31}}
"green" { set color {;32}}
"yellow" { set color {;33}}
"blue" { set color {;34}}
"magenta" { set color {;35}}
"cyan" { set color {;36}}
"white" { set color {;37}}
default { error "unrecognized color" }
}
append suffix "${ESC}\[;37m"
}
"--" { # skip }
default {
set txt $arg
}
}
}
return "${ESC}\[${type}${color}m$txt${suffix}"
}
################################
# Shared memory access
################################
# Update the value of shared memory variable, when the corresponding local changes.
#
# @param name The name of the local/shared variable.
# @param args Rest of standard variable trace arguments.
proc Global_update_write {name args} {
upvar $name var
tsv::set conf $name $var
}
# This procedure makes it easy to access module shared memory parameters among threads
# by mapping a local variable to a shared memory variable, and optionally keeping them
# synchronized as the local variable changes. However, if the session variable changes
# the local will not be updated. Review the code where this proc is used
# to investigate whether this could happen and whether it poses a problem.
# A read trace would solve this, but adds significant extra overhead.
#
# @param args List of shared memory variable names to bind local variables to.
# @error
proc Global {args} {
set sync 0 ;# default is "ro"
switch -exact -- [lindex $args 0] {
"-rw" { set sync 1; set args [lrange $args 1 end] }
"-ro" { set args [lrange $args 1 end] }
}
foreach var $args {
if {! [tsv::exists conf $var]} {
error "Global configuration variable \"$var\" is not defined"
}
uplevel 1 [list set $var [tsv::get conf $var]]
if {$sync} {
uplevel 1 [list trace add variable $var write ::Global_update_write]
}
}
}
# Update the value of shared memory variable, when the corresponding local changes.
#
# @param name The name of the local/shared variable.
# @param args Rest of standard variable trace arguments.
proc Session_update_write {name args} {
upvar $name var __sid__ sid
tsv::set $sid $name $var
}
# This procedure makes it easy to access shared memory session parameters,
# by mapping a local variable to a shared memory variable, and keeping them
# synchronized. However, if the session variable changes
# the local will not be updated. Review the code where this proc is used
# to investigate whether this could happen and whether it poses a problem.
# A read trace would solve this, but adds significant extra overhead.
#
# @param args [-rw|-ro] <sid> var1 [var2 ...]
proc Session {args} {
set sync 0 ;# default is "ro"
switch -exact -- [lindex $args 0] {
"-rw" { set sync 1; set args [lrange $args 1 end] }
"-ro" { set args [lrange $args 1 end] }
}
set sid [lindex $args 0]
set args [lrange $args 1 end]
foreach var $args {
if {! [tsv::exists $sid $var]} {
error "Session configuration variable \"$var\" is not defined"
}
uplevel 1 [list set $var [tsv::get $sid $var]]
if {$sync} {
uplevel 1 [list set __sid__ $sid]
uplevel 1 [list trace add variable $var write Session_update_write]
}
}
}
# Update the value of shared memory variable, when the corresponding local changes.
#
# @param name The name of the local/shared variable.
# @param args Rest of standard variable trace arguments.
proc Module_update_write {name args} {
upvar $name var __mod__ mod
tsv::set $mod $name $var
}
# This procedure makes it easy to access global shared memory parameters
# by mapping a local variable to a shared memory variable, and keeping them
# synchronized.However, if the session variable changes
# the local will not be updated. Review the code where this proc is used
# to investigate whether this could happen and whether it poses a problem.
# A read trace would solve this, but adds significant extra overhead.
#
# @param args [-rw|-ro] <mod> var1 [var2 ...]
proc Module {args} {
set sync 0 ;# default is "ro"
switch -exact -- [lindex $args 0] {
"-rw" { set sync 1; set args [lrange $args 1 end] }
"-ro" { set args [lrange $args 1 end] }
}
set mod [lindex $args 0]
set args [lrange $args 1 end]
foreach var $args {
if {! [tsv::exists $mod $var]} {
error "Session configuration variable \"$var\" is not defined"
}
uplevel 1 [list set $var [tsv::get $mod $var]]
if {$sync} {
uplevel 1 [list set __mod__ $mod]
uplevel 1 [list trace add variable $var write Module_update_write]
}
}
}
################################
# Non-blocking thread::send
################################
namespace eval Thread {
# When bgsend is invoked, it waits on a variable (result_$counter) to be set. This variable
# is set by the executed script when its evaluation completes. Since it is possible
# that the script being evaluated uses bgsend again in the other direction,
# and following that more bgsends in both directions can be opened (waiting for
# their variable to be set), for this reason we can not use the same variable for
# all parallel bgsend sessions. We use a different namespace variable for each.
# The 'counter' variable is used to generate unique names, for each session-
# specific variable.
variable counter 0
# A procedure executed when a bgsend job timeouts.
#
# @param counter The unique number associated with this bgsend job.
# @param tid Thread Id of thread script is sent to.
proc bgsend_timeout {counter tid} {
if {[interp exists $tid]} {
interp cancel $tid "Timeout waiting"
}
set ::Thread::result_${counter} [list 1 {Timeout waiting} {-code 1 -level 0 -errorcode NONE -errorinfo {Timeout waiting}}]
}
# A version of thread::send with asynchronous execution, that waits until command
# is evaluated (without blocking), propagates errors back, and returns the result of
# the evaluation. In addition it supports the folling option(s):
# -timeout <val> : Wait up to <val> ms for the script to return. If it doesn't return by
# that time, generate an error back to the caller.
# Warning: Although the procedure 'bgsend' returns, the script
# might still be executed when it gets its turn in the event loop
# at some unknown time in the future. Result in this case will
# be lost. Errors will be captured by 'bgerror'.
#
# @param tid Thread Id to evaluate script into.
# @param script Tcl script to evaluate.
# @param args Optional parameters.
# @return The return value of the evaluation.
# @error
proc bgsend {tid script args} {
variable counter
for {set i 0} {$i < [llength $args]} {incr i} {
switch -exact -- [lindex $args $i] {
"-timeout" {
set timeout [lindex $args [incr i]]
}
default {
error "Unrecognized option"
}
}
}
incr counter
set script [format {
set _err [catch { %s } _res _opt]
thread::send -async %s [list set ::Thread::result_%d [list $_err $_res $_opt]]
} $script [thread::id] $counter]
if {[info exists timeout]} {
after $timeout [list ::Thread::bgsend_timeout $counter $tid]
}
set ::Thread::result_${counter} {}
thread::send -async $tid $script
vwait ::Thread::result_${counter}
if {! [info exists ::Thread::result_${counter}]} { return } ;# XXX
lassign [set ::Thread::result_${counter}] err res opt
unset ::Thread::result_${counter}
if {$err} { ;# An error occured.
return -options $opt $res
}
return $res
}
} ;# namespace
Overview | Index by: file name |
procedure name |
procedure call |
annotation
File generated 2010-03-13 at 22:28.