Function Call Object

This is a generalization of the MethodObject pattern.

A FunctorObject represents the potential to call a function or method (I'll just call them both functions from now on) . It allows this potential to be generated in one place and modified or exercised in other places. A FunctionCallObject is different. It represents a function that has already been called but has not yet returned. You might also think of this as a StackFrame?Object because it represents everything that a stack frame does: the memory allocated to the local variables, their state, and the position of the instruction pointer. In some ways this is a ProtoPattern in that it's not in use in it's pure form. However, ContinuationsAndCoroutines as well as MethodObjects could all be described as FunctionCallObjects.

Usually when a function is called it just does its assigned task, often calculates some useful result, returns that and leaves. The memory for its stack frame is allocated and deallocated by the CPUs (or VMs) stack management features and the flow of control is passed from function to function in a hierarchical manner that resembles a depth-first tree traversal. Sometimes, though, it would be nice to break out of that model. We might want local variables to be visible to more than one stack frame for instance. Or we might want to suspend the operation of one function temporarily so that we can use its intermediate results in some other function. Or we might want to implement some sort of cooperative multitasking scheme in which several functions are started at once and they yield time to each other in some structured way. MethodObjects, ContinuationsAndCoroutines all have some or more of these properties.

Implementation:

We need someplace to store our local variables that will persist longer than a CPU stack frame and we need something that will serve as a function pointer. The local variables part is easy -- we store them as member variables in the FunctionCallObject just like MethodObject does. The hard part is keeping track of the instruction pointer. This must be done by keeping track of the state of the function whenever it is suspended and then using control structures to restore it when the function resumes. See FunctionCallObjectExample? for a demonstration of how to transform a function into a FunctionCallObject.

[More to come...] -- PhilGoodwin

See also: LazyObject



You seem to be describing the MethodContext? and BlockContext? classes in Smalltalk. The Smalltalk solution to the "instruction pointer" question is to have the context contain a reference to its corresponding CompiledMethod? (a reification of the method being executed, containing the bytecodes of the method). The context can then contain a index into its CompiledMethod?.

Another trick that the Smalltalk community learned the hard way is that you don't need to actually execute out of these context objects (although early versions of Smalltalk actually did so). Since they exactly reflect the execution context, the VM can create them "just in time" whenever some object in the environment attempts to look at one. Typically, this happens during an exception or an interrupt -- relatively infrequent occurrences. So a modern Smalltalk VM typically executes as fast as possible in the VM itself, and then lazily creates the context stack whenever required -- discarding it (for the garbage collector) when the stack query is finished.

In Smalltalk then, as you observed, a Process is a chain of Context's, and interesting process switches (such as handling or resuming from an Exception) are accomplished by simple manipulations of the Context instances.

-- TomStambaugh

So what's the difference between a MethodContext? and a BlockContext?? It doesn't sound like there is any way to emulate Smalltalk's solution to the instruction pointer problem in other languages. Is that right? -- PhilGoodwin

I think you've asked two different, but related, questions. First, the easy one: what's the difference between a MethodContext? and a BlockContext?. A method context is a stack frame within which a particular compiled method (function) runs. The MethodContext? contains a pointer to the CompiledMethod? it is running; the CompiledMethod?, in turn, contains pointers to the executable code (byte codes), source code, and literals of the method. Think of the compiledMethod as the thing that can go in a DLL or SO.

A Block, in Smalltalk, is something like a Lambda in Lisp or closure in PERL. It is a fragment of code (like a block in Java or compound statement in C) whose execution is deferred until later. A Block is a class (everything in Smalltalk is a class) that responds to a #value method by evaluating its contents and returning the answer. This behavior is readily emulated in Java by using an anonymous inner class.

A BlockContext? is the context object that serves as the StackFrame? for the Block, analogous to the MethodContext?.

The "instruction pointer" problem you refer to is present in any interpreter, and is no harder nor easier in Smalltalk than any other language. The virtual machine has an instruction pointer, corresponding to the Instruction Pointer or Program Counter of any physical machine. Think of it as a register. The stack frame (or MethodContext?) caches the state needed to accomplish a process switch into that frame. This amounts to suspending the interpreter at an instruction boundary (like grabbing the CPU at an instruction boundary), loading its registers from the StackFrame?, and continuing.

None of it is very hard (although it is often tedious). The tricky part is making it appear to go fast.

-- TomStambaugh


CategoryObjectFunctionalPatterns


EditText of this page (last edited April 27, 2000) or FindPage with title or text search