As you can see, this primitive takes two arguments. Here the language-side method which using it:

primitiveTransferToProcess: newProcess action: anAction
“Primitive.
Sets an activeProcess to new process,
sets an interruptedProcess to the process which was active
set a scheduler’s action ivar to anAction object
”
<primitive: ‘primitiveTransferToProcess’ module: ”>
self primitiveFailed

The primitive will fail, if image (by some occasion) calls this primitive while having an old scheduler installed.

A new scheduler should have additional instance variables:

interruptProcess

interruptedProcess

action

You may ask, why i made the action as additional argument while in order to switch the active process we need only single parameter – the process which should be activated. The answer is, that sometimes we need to pass an argument to newly switched process, and second is to ensure atomicity when passing this argument. This functionality is used by scheduler to ensure atomicity of different operations.

Otherwise, we are risking, when passing an action argument to scheduler, because the process which setting it, can be preempted before entering interrupt process, by other process, and action will be overridden and therefore lost:

Processor setAction: [ do something here ].

“we can be preempted here”

Processor switchToInterruptProcess.

Usually, to evaluate the piece of code inside an interrupt process, most of the code is using an #interruptWith: method which implemented as following:

An interrupt process is a special process, held by sheduler and runs an infinite loop, which

– performs an action (if any), passed to interrupt process

– handles external signals

– performs scheduling

at the end of each cycle, it switching back to interruptedProcess (using the above primitive). So, if there in no-one changed the interruptedProcess , it will continue running the interrupted process.

A second primitive is used to fetch pending signals from VM’s semaphoresToSignalA/semaphoresToSignalB buffers directly to language side, so then it could handle signals by itself without the need from VM to even know about existance of such objects as semaphores.

primitiveFetchPendingSignals
“primitive, fill an array (first argument)with pending signals, stored insemaphoresToSignalA/semaphoresToSignalB buffers.
Returns a number of signals being filled or negative number indicating that array is not big enough to fetch all signals at once.
Primitive fails if first argument is not array.
“

This primitive is used by interruptProcess to fetch all pending signals from VM and to handle them.

This is mainly all what we need from VM in order to be able to implement own scheduling semantics at language side, without being dependent from VM too much.

Signals unification

The interaction between language side and VM with new scheduler don’t requires from VM to have any knowledge about semaphores. VM operates with only integer values (signals), and scheduler is free to interpret them as it likes to.

The language side passing integer objects to different VM primitives, and VM emits signals using these values. A scheduler then is using a new primitive (primitiveFetchPendingSignals) to fetch the list of pending signals in its interrupt process.

Now, since we lifted the responsibility of interpreting signals from VM side to language side, we could use not only Semaphore to handle the signal, but also any other Object. Scheduler is taking a signal integer value, as an index in its signalHandlers array, then simply sends #handleExternalSignal to an object stored in this array at given index. By default, Object>>handleExternalSignal does nothing, while Semaphore>handleExternalSignal is either awaking the process which is waiting on semaphore or increments an excessSignals value, if there is no processes waiting for it.

This effectively allows us to move scheduling semantics from VM to language side, and free to change it in the future without the need of any changes in VM.

The current Squeak VM provides an API function for plugins (signalSemaphoreWithIndex: ) which could be used by plugins to signal a semaphore stored in external objects table (Smalltalk externalObjects == Smalltalk specialObjectsArray at: 39). This means, that all plugins already operating with integer values (signals) and they can’t really interact with semaphore objects, so we don’t need any changes here, except that instead of directly signaling a semaphore object in external semaphores table by VM, we let a new scheduler to interpret this signal at language side.

In Interpreter, however, there are some places which operating with semaphores directly. So, all what we need now is to review all such places in order to unify this.

The primitiveLowSpaceSemaphore is now obsolete, since VM don’t needs to signal semaphores directly – it just passing this signal in primitiveFetchPendingSignals to let scheduler handle it.

An addPendingSignal: is a service method, which stores a new signal to semaphoresToSignalA/semaphoresToSignalB buffers, in same manner as signalSemaphoreWithIndex: does, except that its not sends forceInterruptCheck. As you may guess, i changed signalSemaphoreWithIndex: method to a two-liner:

signalSemaphoreWithIndex: index
“Record the given semaphore index in the double buffer semaphores array to be signaled at the next convenient moment. Force a real interrupt check as soon as possible.”

to register an object, who will take care for handling this event. It can be semaphore or something else – we don’t really care at this moment. A #performDuringInterrupt: used to ensure that given enclosed code will run atomically and can’t be preempted.

Next, a current primitiveSignalAtMilliseconds takes a two arguments: the millisecond clock value, when timer event should be signaled and a semaphore to store at TheTimerSemaphore index in special objects array.

Since, now we passing a signal value (TimerSignal constant) to language side to handle it, the need in having an object to be signaled is redundant, that’s why i made a new primitive for this:

A new Delay implementation will make use this new primitive instead of old one.

Again, we add a convenience method setTimerSignalHandler: anObject to new scheduler. In contrast to previous implementation, we don’t need to change the handler object during run time. We set it only once at image startup phase – it will be a Delay class, which on receiving this signal will perform a regular procedure for signaling expired delays, if any. But i don’t want go in detail about this now, since this post dediceted to describe the VM-side changes.

fortunately, there is no special primitive for setting the finalization semaphore, so this is all what we need to change in VM.

Backward compatibility

The changes is made in such way, that VM will stay compatible with old image, which don’t use new scheduler, but if it does, then its using different code. You can browse all senders of hasNewScheduler message to see where code takes different route in a presence of new scheduler.

This method simply checks , that a Processor special object having additional slots:

I don’t like to create many identities on different sites (because the more you have, the harder to remember all the details, like credentials, emails etc, and even a site name), but its just happens, that in order to improve communications with Squeak community, Squeak Board decided to create a blog, and i, as a board member, have to register here to participate.

I found that wordpress blogging engine is much more modern & convenient than hotspot, so i decided to move my personal blog here.