Question

I'm trying to run tclhttpd in a slave interpreter but slightly modified so as to run within a tclkit. The code below "runs" (I can hit http://localhost:8015) but never reaches the puts line at the bottom because "the server does not return, it enters [vwait forever]". But when I try "the after 0 trick", e.g. prepending "after 0 " to the line "$httpd eval $cmd", the server does not run at all, so I presume "errors have to be handled by bgerror"

However I cannot find good examples of how to use bgerror, plus my research shows that now the convention is to use "interp bgerror". Please see the first couple of examples returned by http://www2.tcl.tk/_/gsearch?S=bgerror; the first link contains the verbiage "fill in useful tricks and examples for using bgerror" but then there are no samples I can discern how to apply, and the second link concludes "I am interested in examples how this is supposed to be used."

package require starkit
starkit::startup

set httpd_args [list]
set httpd [interp create]
$httpd eval "set argc [llength $httpd_args]"
set cmdargv "set argv [list $httpd_args ]"
$httpd eval "set topdir $starkit::topdir"
$httpd eval $cmdargv

set cmd [list source [file join $starkit::topdir bin/httpd.tcl]]
$httpd eval $cmd

puts "if seeing this controlled has returned"
Was it helpful?

Solution

I don't quite understand the question you are asking. It sounds like your goal is to start up an http server in one interpreter but somehow interact with the main interpreter. Is that right? If so, what does that have to do with bgerror?

Are you aware that even though you are running the server in a separate interpreter, it is not running in a separate thread? That is, you can't (*) interact with the main interpreter while either interpreter is blocked by a vwait.

(*) you can, if your interaction takes the form of Tk widgets that also take advantage of the event loop

As for how to use bgerror, There are a couple of ways that it works. The default mechanism calls the function 'bgerror" which you may define to do whatever you want. It takes a single string (the text of an error message) and does something with it. That something could be to print the error to stdout, show it in a dialog, write it to a file, etc.

As an example, consider this interactive session:

% proc bgerror {s} {puts "hey! I caught an error: $s"}
% # after 30 seconds, throw an error
% after 30000 {error "this is an error"}
after#0
% # after 40 seconds, terminate the event loop
% after 40000 {set ::done 1}
after#1
% # start the event loop
% vwait ::done
hey! I caught an error: this is an error
% # this prompt appears after 40 seconds or so

You can also register your own error handler, as described in the documentation for "interp bgerror". This came along in tcl 8.5, though it had a bug that wasn't fixed until 8.5.3.

For example:

% set foo [interp create]
interp0
% $foo eval {proc myErrorHandler {args} {puts "myErrorHandler: $args"}}
% $foo bgerror myErrorHandler
myErrorHandler
% # after 30 seconds, throw an error
% $foo eval {after 30000 {error "this is an error"}}
after#0
% # after 40 seconds, terminate the loop
% $foo eval {after 40000 {set ::done 1}}
after#1
% $foo eval {vwait ::done}
myErrorHandler: {this is an error} {-code 1 -level 0 -errorcode NONE -errorinfo {this is an error
    while executing
"error "this is an error""
    ("after" script)} -errorline 1}
% # this prompt appears after 40 seconds or so

Does this help answer your question?

OTHER TIPS

Completely edited based on the OP's comments...

The after 0 trick is the following line:

after 0 $httpd eval $cmd

What this does is tell the interp to add the command in question ($http eval $cmd) to the event queue, which means it will run once the event loop is started (or returned to if it's already started). You can see the reliance on the event loop in the following comment from that page (by Jacob Levy):

I should note that this depends on the event loop being active.

My guess is that you're running a plain Tclsh, which means you never enter the event loop (the Wish shell enters the event loop at the end of the script, the Tcl shell does not). The standard way to enter the event loop is to run the following command once you get to the end of your Tcl code:

# Enter the event loop and stay in it until someone 
# sets the "forever" variable to something
vwait forever

That being said, anything you have after the vwait will not run until after the event loop is exited. If you want the httpd to run in parallel to your code, you need to either:

  • Use multiple threads, or write your ... which really isn't that hard
  • code to be event based ... which requires you understand even based programming well enough to prevent pieces of code from being starved of execution time.

Hope that helps.

If I've understood correctly what you want to do, your code should look similar to that:

set httpd_id [thread::create -preserved]
thread::send $http_id "source [file join $starkit::topdir bin/httpd.tcl]"

In this way you'll have TclHttpd running in a thread, without worrying for the vwait problem

If you also want to be informed about any error during the httpd execution, TclHttp sends all the errors to a log file. You can configure the path of the Log doing:

Log_SetFile "/logs/httpd_log"

You need to have the httpd::log package.

I hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top