MC logo

The Tswitch Thread Support Library

^  Dr. Bennet

Tswitch

Main Page
F06 Assignment
Download Library
CCSC '07 Paper

Clients for Tswitch

Save And Restore
Restack
Multiple Stacks

Clients for the F06 Assignment

Simple
Thread Demolition Derby
Graph Search
A threading library creates threads in an ordinary (non-threaded) process by multiplexing the single thread provided by the base OS. Writing a simple thread library makes a good assignment for an Operating Systems course because it provides practice with scheduling and dispatch, without having to deal with a full-blown OS scheduler. The tswitch library provides low-level primitives sufficient to implement such a library using C or C++.

Tswitch is written in x86 assembler using the NASM assembler. The present version of tswitch runs on the standard Intel architecture under Linux. (I'm hoping to make a Windows port as time allows.)

The following interface is presented to the client:

int regsave(regbuf_t buffer);

int regrest(regbuf_t buffer, int retval);

These save and restore the CPU registers into or from a buffer area provided by the user. Tswitch defines the type name regbuf_t which the client uses to declare a variable. The regsave and regrest calls then load the CPU registers into or out of this variable.

Since regrest restores the PC value along with the other registers, a regrest call transfers control to the regsave which saved the value. As such, regrest does not return, or rather, a call to regrest returns from another call to regsave.

So regsave returns once for the original call, then zero or more times from regrest calls. Since the program must often behave differently in these cases, the library arranges for the return values to be different. On the original return, regsave returns zero. When returning from regrest, it returns the value given as the second parameter to regrest.

Readers familiar with setjmp and longjmp from the standard C library will recognize that regsave and regrest are essentially the same operations.

int restack(void *newstack, int size, int nframe, int nparm);

This function switches the current stack to a new area specified by the caller. The argument space points to the memory area for the new stack, which is size bytes long. The method copies a portion of the existing stack to the new area, then changes the stack pointer to point to the copy. It copies nframe stack frames, starting with the frame from which restack is called, plus nparm additional 32-bit words. The later accounts for parameters to the top frame copied; the function cannot tell how many there are. This number should be at least one, to allow for the return address, plus the number of parameters you need to preserve in the copy of the stack.

If all goes well, Restack returns the amount of remaining free space on the stack after the copy, else it returns a negative value. In the former case, the function returns with the new stack in use.

cleanvec_t setcleanup(cleanvec_t cleanfunc);

Restack copies nframe stack frames. Of course, the client cannot return from the top frame when running on the copied stack, since the stack pointer will be invalid after the return. Restack changes the last return address of this top frame to point to a cleanup function, so attempts to return too far will transfer there. The default error function prints a message and terminates the process. Setcleanup specifies an alternative function, perhaps one that calls regrest to switch to a stack has not just been hosed.

Working with multiple call stacks is a tricky business. The first thing to keep in mind is that all parameters and local variables are stored on the stack, so when a stack changes, these may all change. Static data are not kept on a stack. This includes variables declared outside any function, as well as those declared inside functions with the keyword static.

Restack copies the current stack frame into the specified space, then changes the stack pointer to reference it. If you have pointers to variables on the old stack, the copies of the pointers on the new stack will still point to the variables on the original stack.

It's also worth remembering that the new stacks are of fixed size. These are fairly easy to overflow.