after」タグアーカイブ

In which case vwait never returns

If your Tcl script uses many afters and vwaits, sometimes it happens that vwait never returns even if its target variable is set. I didn't understand why my application get into such a situation.

Here is a sample script that causes first call of vwait never to return.
Run this script in wish.

console show
 
rename vwait original_vwait
proc vwait {var} {
	global last_vwait_var
	puts "enter vwait $var"
	set last_vwait_var $var
	original_vwait $var
	puts "leave vwait $var"
}
set last_vwait_var {}
 
proc set_var1 {val} {
	global var1
	puts "set var1 $val"
	set var1 $val
}
 
proc set_var2 {val} {
	global var2
	puts "set var2 $val"
	set var2 $val
}
 
after 1000 {
	after 1000 {
		set_var1 go
	}
	vwait ::var2
}
 
vwait ::var1
set_var2 go

I expect the following output;

enter vwait ::var1
enter vwait ::var2
set var1 go
leave vwait ::var1
set var2 go
leave vwait ::var2

Contrary to my expection, the actual console output is;

enter vwait ::var1
enter vwait ::var2
set var1 go

When I call "set_var2 go" interactively, the output follows,

% set var2 go
go
leave vwait ::var2
leave vwait ::var1
set var2 go

This means that if you call nested vwaits, the first call of a vwait blocks until all of the nested vwait returns.

Even if you understand this characteristics of vwait, you can run into trouble in network programs. I guess this is caused by the following characteristics of a network program.

  • It doesn't know when a response comes.
  • It uses recursive call of vwait when resending a command on a response timeout.
  • A polling loop interrupts normal sequence order.

Sorry to suck at explaining.

I made an application that communicates with multiple I/O devices which have UDP servers.
I designed it in an object-oriented way and encapsulated UDP client sockets in each of the device objects. As the UDP server device doesn't support push-notification, I had to ask their status at a certain intervals: a polling loop for each device. The polling loops are recursive call of afters. They interrupt into the normal command sequence. I guess this causes unexpected vwait call.

My design was successful in the other platforms like Ruby and C# which have built-in preemptive threads. But in Tcl, I had to place a single manager object for highly concurrent parts of the program.

Tcl has many OOP extensions. We can call Tcl as multi paradigm programming language. But as to this kind of thing, Tcl is not truely object-oriented; it forces tight dependencies between objects.

Tcl 8.6 will gain coroutines. I hope it will introduce a true OOP into Tcl.

Tcl/Tk 8.5 Programming Cookbook