Custom Queue Object

The example makes use of the auto loader from the beginning but highlights the use of a Prolog Java proxy objects. We use them to show how a call-back cannot only be implemented in Java but also scripted by Prolog. The example makes use of Java object for a concurrent queue. The Java object has the following interface definition, consisting of a constructor and two methods:

public class Queue {
    public Queue();
    public Runnable take() throws InterruptedException;
    public void post(Runnable r, long w)
}

In our example this queue is used to schedule and run call-backs that display a line “Ha” followed by a line “Tschi”. Since only one call-back is run at a time by the executor thread we don’t need a lock and nevertheless observe no interleaving. We begin with defining the call-back in Java. The following Java code does the job:

    public void run()  {
        try {
            long time = System.currentTimeMillis();
            StringBuilder buf = new StringBuilder();
            buf.append("Process ");
            buf.append(name);
            buf.append(": ");
            buf.append(count);
            buf.append('\n');
            writer.write(buf.toString());
            writer.flush();
            count++;
            if (count < 10)
                queue.post(this, time + 1000);
        } catch (IOException x) {
            throw new RuntimeWrap(x);
        }
    }

The call-back will increment a counter, display the counter and re-schedule itself with a delay of 1000ms. It will do so for 10 times. We can already script the executor code in Prolog itself. This code will first create two counters and the in an infinite loop execute the runnable of the queue. The code uses the auto loading of Java classes and interfaces:

main :-
   current_output(W),
   example08/'Queue':new(Q),
   example08/'Counter':new('P1', Q, W, C1),
   example08/'Counter':new('P2', Q, W, C2),
   C1::run,
   C2::run,
   repeat,
      Q::take(R),
      R::run,
   fail.

The two lines with the invocation example08/’Counter’:new create the two counters. The invocation will call the constructor of the counter. This constructor takes various parameters, such as the name of the counter, the queue for self-re-scheduling and the stream for output. The call-backs of the counter are invoked via 'Runnable'. When the main loop is invoked the result will be as follows:


Picture 3: Screenshot of Queue Example Run

Prolog Java proxy classes now allow scripting the call-backs in Prolog. The replacement for a Java class is a Prolog module. This Prolog module has to use reexport/1 directives to import the Java interfaces that the Prolog Java proxy class is going to implement. In the present case we reexport/1 the Java interface ’Runnable’:

:- package(library(example08)).
:- module(counter2, []).
:- reexport(foreign('Runnable')).
:- reexport(foreign('InterfaceSlots')).
:- use_module(library(basic/proxy)).

We also re-export the Java interface ‘InterfaceSlots’. When this interface is present in the Prolog Java proxy class it will also be possible to access a state of an instance via some predi-cates and evaluable functions at/3, set_at/3 and length/2 that are automatically handled. To create an instance we use the predicate sys_new_instance/3:

:- public new/4.
new(N, Q, W, R) :-
sys_new_instance(example08/counter2, 4, R),
R::set_at(0, N),
R::set_at(1, Q),
R::set_at(2, W),
R::set_at(3, 0).

The call-back can now be scripted via Prolog. The Prolog code is a literal translation of the Java counter code from above. Our proxy handler will use an interactor to execute the call-back predicate. Currently the handler will execute the predicate once and then close the interactor, so that no trail and no choice points are left. We might support further executing policies in the future:

:- public run/1.
:- override run/1.
run(R) :-
T is 'System':currentTimeMillis,
R::at(0, N),
R::at(1, Q),
R::at(2, W),
R::at(3, C),
atom_concat('Process ', N, A1),
atom_concat(A1, ': ', A2),
number_codes(C, L),
atom_codes(A, L),
atom_concat(A2, A, A3),
atom_concat(A3, '\n', A4),
write(W, A4),
flush_output(W),
D is C+1,
R::set_at(3, D),
(D < 10 ->
S is T+1000,
Q::post(R, S); true).

The override/1 directive is a module system specific feature. It suppresses a warning that the ’Runnable’ interface is overridden. We might change this style check in the future in that we might not require this directive for abstract members. Currently our module system has not yet a distinction between abstract and non-abstract members.

The foreign function interface exception RuntimeWrap is not seen, since the proxy handler uses it automatically for us. Using the above Prolog scripted counter in an executor gives the same result as the Java coded counter. The variant of an executor with a Prolog scripted counter is found in the Prolog text “queueobj2.p” in the appendix.

Comments