Execution Supervision

With release 1.1.6 the execution supervision of a set of slave threads by a master thread has been improved. To understand this supervision we explain here the basic underlying concepts of Java threads that execute inside the Java class Interpreter with respect to their inter-ruptibility. We will also explain how Java code for foreign functions should look like.

The execution flow of a Prolog interpreter can be externally influence by two facilities. A Prolog interpreter has an interrupt mask and a signal. This status of a Prolog interpreter can be changed via the methods setMask() and setSignal() of its controller. As long as the Prolog interpreter doesn’t use a Java monitor wait() or some Java foreign code the Prolog interpreter will just execute its search loop.

Each time the Prolog interpreter reaches a call-port it will perform the following check. The check is nothing else than polling the interpreter status. The check is cheap since the signal and the interrupt mask are just some volatile object fields. As a result the status check doesn’t slow down the Prolog interpreter in some recognizable way:

Call-Port:
  if ((getSignal() != null) && getMask())
     throw setSignal(null);    

If the Prolog interpreter is waiting inside a Java monitor wait() there are no means for the Prolog interpreter to perform the above check. We therefore have coupled the status change of the Prolog interpreter with setting the Java interrupt bit. The Java virtual machine will wake-up from an interrupt bit change and throw a Java interrupted exception.

The Prolog interpreter uses the following code for a Java monitor wait(). We simply reuse here the method setSignal() and transform the Java interrupted exception into a signalled Prolog program error. It is thus possible to always interrupt Java code that doesn’t swallow the Java interrupted exception and that uses interruptible primitives:

Interruptible Code:
  try {
    synchronized (monitor) {
      ...
      monitor.wait();
      ...
    }
  } catch (InterruptedException x)
    throw setSignal(null);
  }

Foreign functions don’t need to catch the Java interrupted exception. They can stage the Java interrupted exception in their method signature and the Prolog interpreter will automatically do the necessary catch. As long as the signal is not cleared, the Prolog interpreter will not be responsive to new signals.

Sometimes it can be desirable to temporarily prevent the Prolog interpreter from responding to signals. We can use the method setMask() for this purpose and create atomic regions. This method is used by the Prolog interpreter but can also be used by Java foreign code. We just temporarily reset the interrupt mask:

Atomic Region:
  back = setMask(false);
  ...
  setMask(back);

In practice the situation is a little bit more complex and we have only scratched the surface. Not only can it happen that the same thread tries to submitted different signals to a target thread, it can also happened that different threads try to submit a signal or that the target thread dies. It is therefore recommended to use the module “thread” to submit signals.

The module “thread” currently implements a blocking policy for concurrent status change of a target thread. This is by far the easiest solution but the policy might nevertheless change in the future for obvious reasons. The companion Java class ForeignThread also provides replacements for the methods setSignal(null) and setMask() that automatically find the current Prolog interpreter controller.

Kommentare