Rivet Internals

This section easily falls out of date, as new code is added, old code is removed, and changes are made. The best place to look is the source code itself. If you are interested in the changes themselves, the Subversion revision control system (svn) can provide you with information about what has been happening with the code.


When Apache is started, (or when child Apache processes are started if a threaded Tcl is used), Rivet_InitTclStuff is called, which creates a new interpreter, or one interpreter per virtual host, depending on the configuration. It also initializes various things, like the RivetChan channel system, creates the Rivet-specific Tcl commands, and executes Rivet's init.tcl. The caching system is also set up, and if there is a GlobalInitScript, it is run.


The RivetChan system was created in order to have an actual Tcl channel that we could redirect standard output to. This lets us use, for instance, the regular puts command in .rvt pages. It works by creating a channel that buffers output, and, at predetermined times, passes it on to Apache's IO system. Tcl's regular standard output is replaced with an instance of this channel type, so that, by default, output will go to the web page.

The global Command

Rivet aims to run standard Tcl code with as few surprises as possible. At times this involves some compromises - in this case regarding the global command. The problem is that the command will create truly global variables. If the user is just cut'n'pasting some Tcl code into Rivet, they most likely just want to be able to share the variable in question with other procs, and don't really care if the variable is actually persistant between pages. The solution we have created is to create a proc ::request::global that takes the place of the global command in Rivet templates. If you really need a true global variable, use either ::global or add the :: namespace qualifier to variables you wish to make global.

Page Parsing, Execution and Caching

When a Rivet page is requested, it is transformed into an ordinary Tcl script by parsing the file for the <? ?> processing instruction tags. Everything outside these tags becomes a large puts statement, and everything inside them remains Tcl code.

Each .rvt file is evaluated in its own ::request namespace, so that it is not necessary to create and tear down interpreters after each page. By running in its own namespace, though, each page will not run afoul of local variables created by other scripts, because they will be deleted automatically when the namespace goes away after Apache finishes handling the request.

One current problem with this system is that while variables are garbage collected, file handles are not, so that it is very important that Rivet script authors make sure to close all the files they open.

After a script has been loaded and parsed into it's "pure Tcl" form, it is also cached, so that it may be used in the future without having to reload it (and re-parse it) from the disk. The number of scripts stored in memory is configurable. This feature can significantly improve performance.

Debugging Rivet and Apache

If you are interested in hacking on Rivet, you're welcome to contribute! Invariably, when working with code, things go wrong, and it's necessary to do some debugging. In a server environment like Apache, it can be a bit more difficult to find the right way to do this. Here are some techniques to try.

The first thing you should know is that Apache can be launched as a single process with the -X argument:

httpd -X

On Linux, one of the first things to try is the system call tracer, strace. You don't even have to recompile Rivet or Apache for this to work.

strace -o /tmp/outputfile -S 1000 httpd -X

This command will run httpd in the system call tracer, which leaves its output (there is potentially a lot of it) in /tmp/outputfile. The -S option tells strace to only record the first 1000 bytes of a syscall. Some calls such as write can potentially be much longer than this, so you may want to increase this number. The results are a list of all the system calls made by the program. You want to look at the end, where the failure presumably occured, to see if you can find anything that looks like an error. If you're not sure what to make of the results, you can always ask on the Rivet development mailing list.

If strace (or its equivalent on your operating system) doesn't answer your question, it may be time to debug Apache and Rivet. To do this, you will need to run the ./configure.tcl script with the -enable-symbols option, and recompile.

Since it's easier to debug a single process, we'll still run Apache in single process mode with -X:

@ashland [~] $ gdb /usr/sbin/apache.dbg
GNU gdb 5.3-debian
Copyright 2002 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "powerpc-linux"...
(gdb) run -X
Starting program: /usr/sbin/apache.dbg -X
[New Thread 16384 (LWP 13598)]

When your apache session is up and running, you can request a web page with the browser, and see where things go wrong (if you are dealing with a crash, for instance).