0
votes

I worked on a TCL script that takes a bit of time to execute. In order to to run it faster, I would like to use multi-threading using the Thread package. The problem is that I execute my TCL script in an engineering software called Hypermesh. It supports TCL scripting quite well since everything that can be done in the software has an associated TCL command. For example if the user was to use Hypermesh to create a node at coordinate (x, y, z) then the same result can be obtained using the command *createnode $x $y $z. Back to the multi-thread problem: I can create threads just fine but inside the thread, I cannot use the command *createnode because it is not recognized. I am guessing it is because some packages are not loaded inside the thread. I would like to load those missing packages with "package require" but no matter how I tried, it could not find any package.

I found out that inside a thread, the auto_path variable does not exist. I tried to create it, but it still would not work. It looks like my thread can execute basic TCL buit-in commands and nothing else. On the other hand, when I created a Thread using wish, I did not have this problem at all. All the packages "outside" the thread were also directly loaded "inside".

Here is how my code looks like:

package require Thread
package require Ttrace

set scriptToSend { 
   package require Trf
   return 0
}

set t1 [thread::create]
thread::send -async $t1 $scriptToSend result
puts $result

Trf is just a random package that can be loaded just fine when "package require" is called outside of t1

Is there any way I could setup the thread to have all the commands that exist outside the thread? For example by making the use of package require possible?

EDIT: I spent some more time on this problem and tried to use tpool instead of thread. The threads created in tpool seem "better". Unlike with thread, they do have an auto_path variable and I am able to use it to successfully load packages when using threads. However now I have more of an Hypermesh issue since it does not let me load all the necessary packages to use procs such as *createnode. I will try to go around the problem by making my threads do as much as they can without using any Hypermesh function. It will still speed up the script quite a bit.

1
There are 2 issues with your code unrelated to Hypermesh: The result variable will not be updated until the event loop has a chance to run. So your puts $result will produce an error: can't read "result": no such variable. Also, scriptToSend is executed at the global level in thread t1. Running return 0 at the global level also constitutes an exception. - Schelte Bron

1 Answers

0
votes

Tcl uses a limited version of the auto_path global in threads by default because it doesn't set it up in quite the same way (e.g., it doesn't evaluate your ~/.tclshrc). To use the same packages as in the main thread, you'll need to transfer a copy of that variable over.

thread::send $otherThread [list set ::auto_path $::auto_path]

Do that before sending other scripts to the subthread in order to synch things up.


On my system I'd see this sort of thing too:

bash$ tclsh8.6
% puts $::auto_path
/opt/local/lib/tcl8.6 /opt/local/lib /opt/local/lib/tcllib1.19
% package require Thread
2.8.4
% set otherThread [thread::create]
tid0x7000053ab000
% puts [thread::send $otherThread {set ::auto_path}]
/opt/local/lib/tcl8.6 /opt/local/lib

There's also the module path (controllable via the tcl::tm::path command) but that's the same for me in the subthread as in the main thread.