Learning the VMS Code Base

VMS code structure and behavior is detailed in this paper.

The main elements are the core controller, animation slots, animation master, request handler, and assigner.

Animation slots and Core Controller

The animation slots and core controller make up a level of virtualization. Each animation slot acts like a virtual processor, and the core controller acts like a switch, connecting the physical processor to each slot in turn. The purpose is to amortize the cost of the Animation Master, to divide the cost of switching to the runtime across the slots. Each animation slot can hold a slave. When the core controller switches the physical processor over to animate the slave in a slot, that slave in turn animates the application code.

Here is the code for the core controller. It is an endless loop, but the assembly primitive at the end: "switchToSlv(currVP);" switches the physical processor out, so it no longer animates the code of the loop. Later, the slave will execute another assembly primitive that switches the physical processor back, to resume where it left off.

The core controller marches down the animation slots, checking each to see if it contains a slave ready to do work. If so, then it sets that one as current, leaves the scanning and calls switchToSlv(currVP). This causes the application code to begin being animated by the physical processor. When the application does a runtime-invoking call, the physical processor is switched back where it left off in the core controller loop.

When the core controller runs out of animation slots, it sets currVP to the masterVP for that core, and switches to it.

The Animation Master

The animation master is a function that is animated by the masterVP of a core. Each core has its own masterVP. A masterVP reuses the slaveVP structure, so it has a call stack, and place to save switch-over info. The switch-over info is where the primitives that switch between core controller and master save the instr addr, framepointer and stack pointer needed to resume the core controller where it left off.

The master's main job is to invoke the request handler on slaves that need a request handled, and to fill the slots with slaves that have application work to animate. Here is the code of the master It scans the slots, sending each slave that has a request to the request handler. A pointer to the request handler is registered in the master environment when the language initializes.

After the slave in a slot has been handled, or if a slot was empty, the master calls the assigner, in order to get a slave that has work, and then assign that slave to the slot.

When all the slots have been scanned, the master switches back over to the core controller, with the call "masterSwitchToCoreCtlr( masterVP );" This saves the instr addr, stack pointer and frame pointer, so the next switch to the master resumes where this one left off.

The Request Handler

The request handler is supplied by the language implementation and is called by the master. For example, here is the code of the SSR request handler and assigner. The request handler is the second function down.

The request handler's purpose is to implement the behavior of parallelism constructs. In the case of SSR, it implements send and receive constructs, which cause the calling application code to suspend until both the sender and receiver have rendez-vous'd. Once application code calls either send or receive, it is suspended. The construct calls can be seen here starting about 2/3 of the way down, with the "SSR__send_of_type_to" call. The request handling code it invokes can be seen here.

In effect, a send or receive suspends the calling code, switches to the request handler, and the handler checks if the rendez-vous partner is already waiting. If not, it places a pointer to the suspended slave into a hash table, where it waits. When its partner arrives, the pointer is taken out, and the slave is made ready to resume work by placing it into a queue of slaves ready.

This queue of slaves ready acts as the communication between request handlers and assigner. The choice of how to do this communication is up to the language implementor.

The Assigner

The assigner is supplied by the language implementation and is called by the master. It's purpose is to choose from among slaves with work ready, and give the chosen one to the master, which then places the slave into a slot. The core controller will later switch to the slave, at which point the application code will resume at the point just past the call to the send or receive.

At the moment, SSR only has a first-come first-served assignment policy, and it splits slaves among the cores, keeping a separate queue of slaves on each core.