tsv(n)
πΈ tsv(n) β thread shared variables, bestie edition πΈ
okay so this is tsv(n), and it's part of the Tcl threading extension β the bit that lets you mess with data shared between threads straight from script level. no C, no vibes lost. π
β¨ SYNOPSIS β¨
package require Tcl 8.4
package require Thread ?2.8?
tsv::names ?pattern?
tsv::object varname element
tsv::set varname element ?value?
tsv::get varname element ?namedvar?
tsv::unset varname ?element?
tsv::exists varname element
tsv::pop varname element
tsv::move varname oldname newname
tsv::incr varname element ?count?
tsv::append varname element value ?value ...?
tsv::lock varname arg ?arg ...?
tsv::handlers
tsv::lappend varname element value ?value ...?
tsv::linsert varname element index value ?value ...?
tsv::lreplace varname element first last ?value ...?
tsv::llength varname element
tsv::lindex varname element ?index?
tsv::lrange varname element from to
tsv::lsearch varname element ?options? pattern
tsv::lset varname element index ?index ...? value
tsv::lpop varname element ?index?
tsv::lpush varname element ?index?
tsv::array set varname list
tsv::array get varname ?pattern?
tsv::array names varname ?pattern?
tsv::array size varname
tsv::array reset varname list
tsv::array bind varname handle
tsv::array unbind varname
tsv::array isbound varname
tsv::keyldel varname keylist key
tsv::keylget varname keylist key ?retvar?
tsv::keylkeys varname keylist ?key?
tsv::keylset varname keylist key value ?key value..?
π DESCRIPTION π
okay so this whole section is about commands that make thread shared variables happen. a thread shared variable is basically a Tcl array, but make it β¨shared memoryβ¨ β meaning it gets created in shared memory and many threads can poke at it at the same time. and here's the iconic part, the thing that makes it actually usable: every single access to the variable is internally protected by a mutex</at>. so you, the script programmer, do NOT have to sit there locking things yourself. it's giving "someone else already handled the scary part". π«Ά
now the thing you do need to know: thread shared variables are not bound to any thread explicitly. which means β and this is important, bestie β when a thread that created a thread shared variable exits, the variable and its associated memory are NOT unset/reclaimed. π it just... sits there. eating memory. you have to explicitly unset the variable to reclaim the memory it consumed. no cap. the garbage collector is not coming. you are the garbage collector.
π ELEMENT COMMANDS π
tsv::names ?pattern?β hands you the names of shared variables that match the optional?pattern?. if you don't pass a pattern, you get all known variables. that's it, that's the whole vibe.tsv::object varname elementβ creates an object accessor command for the element inside shared variable varname. with this lil guy, you can apply most of the other shared variable commands as method functions of the element object command. and β cute detail β the object command deletes itself automatically when the element it's pointing at gets unset. self-cleaning, we love her. π§
% tsv::set foo bar "A shared string"
% set string [tsv::object foo bar]
% $string append " appended"
=> A shared string appended
tsv::set varname element ?value?β sets the value of element in shared variable varname to value, and returns that value back to the caller. the value is optional β leave it off and the command just returns the current value of the element instead. if the element cannot be found, error is triggered. no chill, just error.tsv::get varname element ?namedvar?β retrieves the value of element from shared variable varname. now, if you give it the optional namedvar, the value gets stored in that named variable. and the return value depends on whether you passed namedvar or not, so listen closely:- no namedvar, and the element can't be found in the shared array? the command triggers error.
- namedvar given? then the command returns true (1) if the element is found, or false (0) if it's not. no drama, just a boolean.
tsv::unset varname ?element?β unsets element from shared variable varname. and if you skip the optional element? it deletes the whole variable. π (remember the DESCRIPTION β this is also how you actually reclaim the memory.)tsv::exists varname elementβ checks whether element exists in shared variable varname, and returns true (1) if it does or false (0) if it doesn't. yes, this is a vibe check. no, i will not elaborate.tsv::pop varname elementβ returns the value of element in shared variable varname and unsets the element, all in one atomic operation. grab it and yeet it, atomically. π₯tsv::move varname oldname newnameβ renames oldname to newname inside shared variable varname. technically it's aget/unset/setsequence, but β and this is the whole point β it's all one atomic step. no thread can catch you mid-move.tsv::incr varname element ?count?β same energy as the standard Tclincrcommand, except it increments the value of the element in shared variable varname instead of a Tcl variable.tsv::append varname element value ?value ...?β same energy as the standard Tclappendcommand, except it appends one or more values to the element in shared variable varname instead of a Tcl variable.tsv::lock varname arg ?arg ...?β this one's the bouncer. π«‘ it concatenates the passed arguments and evaluates the resulting script under the internal mutex protection. so during that script evaluation, the entire shared variable is locked. and here's the smart part β for shared variable commands within the script, internal locking is disabled, so no deadlock can occur. you're also allowed to unset the shared variable from inside the script. and if the shared variable didn't exist at the time of the first lock operation, it's automatically created.
% tsv::lock foo {
tsv::lappend foo bar 1
tsv::lappend foo bar 2
puts stderr [tsv::set foo bar]
tsv::unset foo
}
tsv::handlersβ returns the names of all persistent storage handlers enabled at compile time. see ARRAY COMMANDS below for the tea. π
π LIST COMMANDS π
okay so all of these are basically the Tcl command of the same name, but with a glow-up: they operate on elements of shared arrays. that's the whole difference. slay.
tsv::lappend varname element value ?value ...?β like the standard Tcllappend, but it appends one or more values to the element in shared variable varname instead of the Tcl variable.tsv::linsert varname element index value ?value ...?β like the standard Tcllinsert, but it inserts one or more values at the index list position in the element in shared variable varname instead of the Tcl variable.tsv::lreplace varname element first last ?value ...?β like the standard Tcllreplace, but it replaces one or more values between the first and last position in the element of shared variable varname instead of the Tcl variable.tsv::llength varname elementβ like the standard Tclllength, but it returns the length of the element in shared variable varname instead of the Tcl variable.tsv::lindex varname element ?index?β like the standard Tcllindex, but it returns the value at the index list position of the element from shared variable varname instead of the Tcl variable.tsv::lrange varname element from toβ like the standard Tcllrange, but it returns values between the from and to list positions from the element in shared variable varname instead of the Tcl variable.tsv::lsearch varname element ?options? patternβ like the standard Tcllsearch, but it searches the element in shared variable varname instead of the Tcl variable.tsv::lset varname element index ?index ...? valueβ like the standard Tcllset, but it sets the element in shared variable varname instead of the Tcl variable.tsv::lpop varname element ?index?β like the standard Tcllindex, except it also splices the value out of the element from shared variable varname, all in one atomic operation. and heads up β in contrast to the Tcllindexcommand, this one returns no value to the caller. it's giving "took it, didn't say anything". πtsv::lpush varname element ?index?β this does the exact opposite oftsv::lpop. and just like its counterpart, it returns no value to the caller.
π ARRAY COMMANDS π
this command supports most of the options of the standard Tcl array command. but it also does something extra β¨: it lets you bind a shared variable to some persistent storage database. currently the persistent options supported are the famous GNU Gdbm and LMDB. these options have to be selected during the package compilation time β so if you didn't build with them, they're not there, bestie. the implementation also provides hooks for defining other persistency layers, if needed.
tsv::array set varname listβ does the same as the standard Tclarray set.tsv::array get varname ?pattern?β does the same as the standard Tclarray get.tsv::array names varname ?pattern?β does the same as the standard Tclarray names.tsv::array size varnameβ does the same as the standard Tclarray size.tsv::array reset varname listβ does the same as the standard Tclarray set, but it clears varname and sets new values from list atomically. out with the old, in with the new, no gap in between. πtsv::array bind varname handleβ binds varname to the persistent storage handle. the format of the handle is<handler>:<address>, where<handler>is"gdbm"for GNU Gdbm and"lmdb"for LMDB, and<address>is the path to the database file.tsv::array unbind varnameβ unbinds the shared array from its bound persistent storage. it's free now. π¦tsv::array isbound varnameβ returns true (1) if the shared varname is bound to some persistent storage, or zero (0) if not.
π and that's part 1, bestie β there's more coming, so don't go anywhere π
π KEYED LIST COMMANDS π
Okay bestie, so these keyed list commands? They're borrowed from the TclX package, and they give you a whole structured data type built right on top of standard Tcl lists. Think of it like structs in the C programming language β same energy, way cuter syntax. π
A keyed list is a list where each element holds a key and a value pair. Those element pairs get stored as lists themselves β the key is the first element of the list, and the value is the second. And we call those key-value pairs fields. Here's what one looks like:
{{NAME {Frank Zappa}} {JOB {musician and composer}}}
And fields can have subfields! The separator character is '.' β and lowkey, subfields are actually just fields whose value is another keyed list. So this next one has the top level fields ID and NAME, plus the subfields NAME.FIRST and NAME.LAST:
{ID 106} {NAME {{FIRST Frank} {LAST Zappa}}}
There is no limit to the recursive depth of subfields, so you can literally build complex data structures with this. Keyed lists get constructed and accessed through a bunch of commands β and here's the important part, bestie: all keyed list management commands take the name of the variable containing the keyed list as an argument (so it's passed by reference), not the list itself. iykyk. β¨
tsv::keyldel varname keylist keyβ Deletes the field named bykeyfrom the keyed listkeylistsitting in the shared variablevarname. And it takes out both the key and the value, no leftovers. ποΈtsv::keylget varname keylist key ?retvar?β Returns the value tied tokeyfrom the keyed listkeylistin the shared variablevarname. If you don't pass the optionalretvar, the value comes back as the result of the command β and heads up, in that case, ifkeyisn't found in the list, you get an error. π
Now if you do pass retvar and key is in the list, the value lands in the variable retvar and the command returns 1 (key was present, slay). If key isn't in the list, the command returns 0 and retvar is left exactly as it was. And if you pass {} for retvar, the value is not returned at all β which lets a Tcl programmer check whether a key exists in a keyed list without setting a variable as a side-effect. Main character move, honestly. π
tsv::keylkeys varname keylist ?key?β Returns a list of the keys in the keyed listkeylistin the shared variablevarname. If you passkey, then it's the name of a key field whose subfield keys you want retrieved. ποΈtsv::keylset varname keylist key value ?key value..?β Sets the value associated withkeyin the keyed listkeylisttovalue. Ifkeylistdoesn't exist yet, it gets created. Ifkeyisn't currently in the list, it gets added. If it already exists,valuereplaces the existing value. And you can pass multiplekey valuepairs if you want, bestie β go off. π₯
π DISCUSSION π
So the current implementation of thread shared variables makes accessing data shared between different threads easy and convenient, fr. Internally, the data lives in Tcl objects, and every package command operates on the internal data representation β which minimizes shimmering and boosts performance. β¨ Special care has been taken to make sure all object data is properly locked and deep-copied when it moves between threads. Safe and secure, we love to see it. π«Ά
Now for the not-so-cute part: due to the internal design of the Tcl core, there is no provision of full integration of shared variables within the Tcl syntax, unfortunately. π All access to shared data has to go through the supplied package commands. Also, variable traces are not supported. But even so β the benefits of easy, simple and safe shared data manipulation outweights the imposed limitations. Periodt. π
π CREDITS π
Thread shared variables are inspired by the nsv interface found in AOLserver, a highly scalable Web server from America Online. Iconic lineage, honestly. π
π¦ SEE ALSO π¦
threadtpoolttrace
π KEYWORDS π
- locking
- synchronization
- thread shared data
- threads
And that's a wrap on tsv(n), bestie! π This one's from Tcl Threading, version 2.8, dated tsv(n) β go forth and share your variables safely, you absolute legend. π
β¨π«Ά
π¦ where this page comes from
- alpine 3.22: tcl-doc 8.6.16-r0 Β· TCL
- alpine 3.23: tcl-doc 8.6.17-r0 Β· TCL
- alpine 3.24: tcl-doc 8.6.17-r1 Β· TCL
- arch rolling: tcl 8.6.16-1 Β· TCL
- epel 9: tcl-thread 2.8.8-1.el9 Β· TCL
- fedora 43: tcl-thread 2.8.8-8.fc43 Β· TCL
- fedora 43: tcl-thread 2.8.8-8.fc43 Β· TCL
- fedora 44: tcl-thread 2.8.8-9.fc44 Β· TCL
- fedora 44: tcl-thread 2.8.8-9.fc44 Β· TCL
- opensuse-leap 16.0: tcl 8.6.15-160000.2.2 Β· TCL
- opensuse-leap 16.0: tcl 8.6.15-160000.2.2 Β· TCL
- opensuse-leap 16.0: tcl 8.6.15-160000.2.2 Β· TCL
- opensuse-leap 16.0: tcl 8.6.15-160000.2.2 Β· TCL
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms