From unununium at openverse dot com To msofer at users dot sf dot net Cc khalili at cse dot uta dot edu Cc jeff at hobbs dot org Subject Resource limiting, freezing interps Hi, I was chatting with Jeff Hobbs the other day, and he suggested I come to you with my questions. First off, it would help if I gave you some context. For an OS project (my professor is Dr. Bahram Khalili, to whom I am Cc:'ing this email), I am trying to develop an experimental operating system using Tcl. What do I mean by experimental? I mean it's not for general use (it'll probably be quite slow and have extremely limited hardware support) but can be used to experiment with OS concepts that are too difficult for busy undergrads to hack into Linux. (Want a last-come-first-served scheduler? How about a single-level store? Deadlock prevention or avoidance? Or a new terminal system? Or even unification of fd's, pointers, semaphore handles, pid's, and timers, all securely shareable across processes? Anything...) This would be great for Dr. Khalili's OS class since it would give the students something fun and functional to play with and break without consequence! It'll also let me experiment with my own OS ideas. The project home page is . There's precious little there right now since I've been either too caught up with other school matters or stuck on some rather tricky implementation issues. Said implementation issues were the topic of discussion when Jeff dropped your name. So I'll get to the point. One. I need a means of interrupting running Tcl code in response to a timer event. I believe there's already a TIP for this, the resource limiting framework. (It's in the CVS, at any rate. But it doesn't seem to work correctly.) And we shouldn't mess with the after command and its event loop. When the timeout expires, execution branches into the limit-exceeded handler code. The handler can be a preemptive process scheduler. Jeff said that doing this cross-platform will be very tricky. Unix has SIGALRM which could remove the need for continuous gettimeofday()'s, but using it may undesirably interact with Tk and other loadable libraries. And the SIGALRM handler may need to use longjmp() to switch from the current eval to the new code. Gross gross. But let's say we have this running on a platform where there's a safe mechanism for asynchronously signaling Tcl when timers expire. We should be able to take advantage, and rather than always getting the time, we can just check a flag set by the signal handler. It's alright with me if we don't worry about timeouts expiring within load'ed commands--- simply check the time after the command returns. Parent interps wishing to limit the CPU time of their child interps should not give them direct access to C-implemented commands capable of halting forever. Two. I must be able to freeze (save) the complete state of an interp. This will be used by the timeout-expired code and in the implementation of blocking calls. I also need to thaw (resume) frozen interps, and they should continue executing precisely where they left off. This is nontrivial since the eval state lives on the C stack, not in the Tcl_interp struct. So I guess this adds two subcommands to the interp command (also to the "slave command"): freeze and thaw. Both subcommands may take optional parameters to specify return values, in the style of the return command. When freeze is called on an interp, the interp's complete state is saved. Execution continues immediately after the "interp eval" that started running that interp. "interp eval" returns the code(s) passed as the parameter to freeze, or some suitable default. It's an error to freeze the initial interp, since there's no "interp eval" to return to. When thaw is called on an interp, execution continues immediately after its last "interp freeze". "interp freeze" returns the code(s) passed to thaw. The interp must have been in the frozen state. Hmm, there's a gotcha. Recursive evals often span multiple interps via aliases and "interp eval"s. Somehow all this needs to be saved as well. So maybe associating freeze and thaw with interps is a mistake, but I'll stick with it for the rest of this email. Another problem. What if the eval tree passes through load'ed C code? For instance, I have some C code that takes Tcl script as a parameter and calls it repeatedly in a specially-prepared environment. (Actually it takes a Tcl expression, but that could include [commands].) I guess we could provide a means for the C code to save and restore its state. So how will I use this stuff? The main scheduler loop cycles through the ready queue, thawing one child interp (process) at a time. Before thawing, it sets a time limit* to cause the interp to stop just before the next scheduled event, often quantum expiry. The limit handler freezes the interp, thus returning control to the main scheduler loop. * It adds the previous time limit to the new interval. The next time the interp is thawed, it will continue executing inside the limit handler. Since the time limit has been increased, the resource limiting framework will continue executing the interp code rather than throwing an error. Blocking commands are also implemented as the interp freezing itself. As for the gotcha, often child interps will need to call commands implemented in the parent interp, which presumably has more privileges. Many of these comands are blocking (read file, write file, wait for timer, wait for vertical retrace, whatever), so the parent interp will place the child interp on a wait queue then freeze it. When the child interp is thawed, execution should continue right after the point of freezing, even though the code in question is implemented in the parent interp! This requires more thought (and a better explanation). I certainly no expert on Tcl internals. I've been reading the sources, but I have spent much more time on things like loadable commands, types, and source tree organization than on bytecode compilation and execution. So if I misunderstand the nature of Tcl, please forgive me. You may be wondering why I don't just use threads. Well, threads are scheduled by the host operating system, so there would be no way to script the scheduler. Also I may some day want to run this thing on the bare metal--- embedded Tcl! In that case there would be no host OS, just a version of libc that talks to the hardware directly rather than making syscalls. I think I've heard other languages/runtimes call this microthreading, a fancy term for user threads with no support from the kernel. I'm not exactly sure why or how, but this is sometimes advertised as a feature. They probably say it's ultra-lightweight or something stupid like that. Maybe there's a benefit to it. Maybe our threading library can be modified to only reveal to the kernel as many threads as there are physical execution units (CPU's, hyperthreading thingies, whatever). I dunno... I'm just trying to find more uses for this junk. Coroutines? Generator functions? Thanks for any help, code, or advice you can offer. Dr. Khalili: This email should give you an idea of the sort of research and design work I have been doing for this project. I could have been working on the user-level stuff (file management commands, editors, applications, system administration tools) but that's a solved problem. So I focused on ferreting out the showstoppers, primarily the need to freeze the eval state. -- Andy Goth + unununium@openverse.com + http://ioioio.net/