[ Team LiB ] Previous Section Next Section

Sending Messages to Threads

The thread::send command sends a script to another thread to execute. The target thread's main interpreter receives the script as a special type of event added to the end of its event queue. A thread evaluates its messages in the order received along with all other types of events. Obviously, a thread must be in its event loop for it to detect and respond to messages. As discussed on page 324, a thread enters its event loop if you don't provide a script argument to thread::create, or if you include the thread::wait command in the thread's initialization script.

Synchronous Message Sending

By default, thread::send blocks until the target thread finishes executing the script. The return value of thread::send is the return value of the last command executed in the script. If an error occurs while evaluating the script, the error condition is "reflected" into the sending thread; thread::send generates the same error code, and the target thread's stack trace is included in the value of the errorInfo variable of the sending thread:

Example 21-5 Examples of synchronous message sending
set t [thread::create]  ;# Create a thread
=> 1572
set myX 42  ;# Create a variable in the main thread
=> 42
# Copy the value to a variable in the worker thread
thread::send $t [list set yourX $myX]
=> 42
# Perform a calculation in the worker thread
thread::send $t {expr { $yourX / 2 } }
=> 21
thread::send $t {expr { $yourX / 0 } }
=> divide by zero
catch {thread::send $t {expr { $yourX / 0 } } } ret
=> 1
puts $ret
=> divide by zero
puts $errorInfo
=> divide by zero
       while executing
   "expr { $yourX / 0 } "
       invoked from within
   "thread::send $t {expr { $yourX / 0 } } "

If you also provide the name of a variable to a synchronous thread::send, then it behaves analogously to a catch command; thread::send returns the return code of the script, and the return value of the last command executed in the script — or the error message — is stored in the variable. Tcl stores the target thread's stack trace in the sending thread's errorInfo variable.

Example 21-6 Using a return variable with synchronous message sending
thread::send $t {incr yourX 2} myY
=> 0
puts $myY
=> 44
thread::send $t {expr { acos($yourX) } } ret
=> 1
puts $ret
=> domain error: argument not in valid range
puts $errorInfo
=> domain error: argument not in valid range
       while executing
   "expr { acos($yourX) } "

While the sending thread is waiting for a synchronous thread::send to return, it can't perform any other operations, including servicing its event loop. Therefore, synchronous sending is appropriate only in cases where:

  • you want a simple way of getting a value back from another thread;

  • you don't mind blocking your thread if the other thread takes a while to respond; or

  • you need a response from the other thread before proceeding.

graphics/common_icon.gif

Watch out for deadlock conditions with synchronous message sending.


If Thread A performs a synchronous thread::send to Thread B, and while evaluating the script Thread B performs a synchronous thread::send to Thread A, then your application is deadlocked. Because Thread A is blocked in its thread::send, it is not servicing its event loop, and so can't detect Thread B's message.

This situation arises most often when the script you send calls procedures in the target thread, and those procedures contain thread::send commands. Under these circumstances, it might not be obvious that the script sent will trigger a deadlock condition. For this reason, you should be cautious about using synchronous thread::send commands for complex actions. Sending in asynchronous mode, described in the next section, avoids potential deadlock situations like this.

Asynchronous Message Sending

With the -async option, thread::send sends the script to the target thread in asynchronous mode. In this case, thread::send returns immediately.

By default, an asynchronous thread::send discards any return value of the script. However, if you provide the name of a variable as an additional argument to thread::send, the return value of the last command executed in the script is stored as the value of the variable. You can then either vwait on the variable or create a write trace on the variable to detect when the target thread responds. For example:

thread::send -async $t [list ProcessValues $vals] result
vwait result

In this example, the thread::send command returns immediately; the sending thread could then continue with any other operations it needed to perform. In this case, it executes a vwait on the return variable to wait until the target thread finishes executing the script. However, while waiting for the response, it can detect and process incoming events. In contrast, the following synchronous thread::send blocks, preventing the sending thread from processing events until it receives a response from the target thread:

thread::send $t [list ProcessValues $vals] result
    [ Team LiB ] Previous Section Next Section