views:

261

answers:

3

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"
+2  A: 

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.

RHSeeger
The complete command I've been trying beginning "after 0" is straight from the linked to url http://www2.tcl.tk/8755: "after 0 $httpd eval $cmd"I want to launch the server but then interact with it all in the same script, supposedly what the above referenced URL makes possible, particularly after what they term the "after 0" trick.I'd previously tried your suggestion of issuing my own "vwait forever" command, by extracting it from the bottom of bin/httpd.tcl. I'd rather make as few modifications to the underlying tclhttpd source as I have to however.
George Jempty
Both the "after 0" and "vwait forever" pieces of code you add can (and should) go in your own code, not the tclhttpd code.
RHSeeger
I appreciate the information in your answer but I'm not "accepting" it and am instead offering a bounty as I really still want to know how to use bgerror. I think this would allow me to find out the difference between why I can launch the server without prepending "after 0 ", albeit I cannot then interact with it, and why the server does not launch whatsoever when using the "after 0" trick
George Jempty
@George: Understood. If I have time I'll try to get it up and running myself to get an answer for you. That being said, work is pretty busy right now, so I'm unlikely to have the time :(
RHSeeger
I took some time to try to get the code working but, unfortunately, I couldn't get my tcl env setup correctly to load the tclhttpd code correctly and I just don't have the time at the moment to make it work. Best of luck getting the answer you're looking for.
RHSeeger
No problem thanks for your efforts
George Jempty
+1  A: 

Hi George,

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.

Carlos Tasada
+1  A: 

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?

Bryan Oakley
I don't want to interact with the slave interpreter, I want to interact with the web server that it launches. I understand the difference between a slave interpreter and a thread. I'm sure the original link to http://www2.tcl.tk/8755 that I posted provides all the context necessary. I essentially tried to follow that "recipe", it didn't work, in which case the recipe suggests using bgerror.
George Jempty