Custom Mutex Object

This example serves two purposes. First of all it contrasts the foreign function interface with Java class auto loading. Second it gives some idea how the programming interface can be used to deal with Java threads. In the console manual we find a tour that shows how the Jekejeke Prolog development environment can be used to run multiple threads. We will con-tinue working on the example process given there. The process there was defined as follows:

process(X) :-
    repeat,
    log(X, 'Ha'),
    log(X, 'Tschi'),
    fail.

When running two processes in parallel we observed interleaving. For example in the below screenshot we see that between a ‘Ha’ of the process P1 and a ‘Tschi’ of the process P1, the process P2 is able to issue a ‘Ha’:


Picture 1: Interleaving of two Processes

Sometimes interleaving is not desired for a critical section. In the following we will first introduce a custom Java object that allows creating boundaries for critical sections. And then we will show how to call this Java object from Prolog. The Java object has the following interface definition, consisting of a constructor and two methods:

public class Mutex {
public Mutex();
    public void acquire() throws InterruptedException;
    public void release();
}

The code of the Java object is found in the appendix. To protect a critical section from inter-leaving we simply have to surround it with an invocation of the methods acquire() and release(). Since the Java object is subject to garbage collection, the programming pattern is especially simple. We do not have to explicitly destroy the lock object.

The first approach to call this Java object from Prolog is to define a couple of foreign predi-cates. We can do this as follows, whereby the Java object will be mapped to the Prolog reference data type:

:- foreign_constructor(new/1, example06/'Mutex', new).
:- foreign(acquire/1, example06/'Mutex', acquire).
:- foreign(release/1, example06/'Mutex', release).

We can readily start testing our simple locks. We use a modified version of the thread tour example that is found in the Prolog text “mutexobj.p”. Our new Prolog text starts first with reg-istering the foreign predicates and then gives a modified version of the predicate init/0. The new initialization not only asserts a fact for the console but also for the lock, so as to share it later between multiple processes:

init :-
current_output(X),
assertz(console(X)),
new(Y),
assertz(lock(Y)).

Although our simple locks are not re-entrant, they are nevertheless cancellable via a thread interrupt. We can issue a thread interrupt by the menu item File | Abort. The result will be that the predicate acquire/1 will abort with a system error and the lock will remain in status locked. Let’s now turn to a practical application of guarding a critical section.

The subsequent code of our Prolog text contains the same logging predicate as in the console manual thread tour. We will therefore not repeat it here. But the process/1 predicate looks different now. The critical section is the logging of the ‘Ha’ and the ‘Tschi’. To allow a graceful interruption via the menu item File | Abort we have wrapped the critical section by the setup_call_cleanup/3 construct:

process(X) :-
    repeat,
    lock(Y),
setup_call_cleanup(
acquire(Y),
(log(X, 'Ha'),
log(X, 'Tschi')),
release(Y)),
    fail.

The setup_call_cleanup/3 hooks into the Fail and into the Exit port. It will use the Exit port when the goal deterministically succeeds. Thus the unlocking will immediately be done after the logging. When running two processes ‘P1’ and ‘P2’, we will not observe some interleaving for the critical section anymore:


Picture 2: No Interleaving for the Critical Section

An alternative method to invoke the Java object is to use the auto loader. Using the auto loader obviates the tedious listing of foreign/3 directives. The auto loader derives the predicate and evaluable function names from the names of the class members. For more details see the programming interface documentation.

The Prolog code that uses the auto loader is found in the Prolog text ‘mutexobj2.p’. This Prolog text does not contain a foreign/3 directive section anymore. The alternative init2/1 predicate definition has the following reading, using the auto generated constructor. The auto loader will only act the first time a foreign module is seen.

init2 :-
    current_output(X),
    assertz(console(X)),
    example06/'Mutex':new(Y),
    assertz(lock(Y)).

The Prolog text also contains an alternative process2/1 predicate definition, using again the auto generated methods. Auto generated predicates perform similarly to foreign/3 directive define predicates, since the auto loader uses the same directives under the hood except for some additional dispatching of overloaded methods. 

process2(X) :-
    repeat,
    lock(Y),
    setup_call_cleanup(
             Y::acquire,
            (log2(X, 'Ha'),
             log2(X, 'Tschi')),
             Y::release),
    fail.

Kommentare