/**
DLP multi-threaded (bounded) buffer + producer + consumer example featuring (1) active, i.e. multi-threaded objects, (2) communication by rendez-vous, and (3) non-logical variables.**/ :-object pxbuff. main :- text_area(BrowserStream), set_output(BrowserStream), AB := new(active_buffer(10)), _C := new(term_consumer(5,AB)), _P := new(term_producer(5,AB)). :-end_object pxbuff. /**When an active instance of a term_producer object is created, execution starts at the term_producer object constructor. The constructor prints a message and invokes method loop/2. The loop method executes N times and sends a Prolog term (in this example an integer) to the active_buffer thread by means of the goal "B<-put_term(I)" :
**/
:-object term_producer.
term_producer(N,B) :-
format('P ~w term_producer main/2 running~n', [this]),
loop(N,B).
loop(0,_) :-
format('P ~w end of loop ...~n', [this]).
loop(I,B) :-
format('P ~w sending ~w~n', [this, I]),
B <- put_term(I),
N is I-1,
loop(N,B).
:-end_object term_producer.
/**
The creation of an active term_consumer object in main/0
of object pxbuff results in the execution of the term_consumer/2
constructor. The constructor outputs a message and starts loop/2.
The loop/2 method executes N times and retrieves its data from
the active buffer thread by means of the "B<-get_term(T)" goal :
**/
:-object term_consumer.
term_consumer(N,B) :-
format('C ~w term_consumer main/2 running~n', [this]),
loop(N,B).
loop(0,_) :-
format('C ~w end of loop ...~n', [this]).
loop(I,B) :-
B <- get_term(T),
format('C ~w receiving ~w~n', [this, T]),
N is I-1,
loop(N,B).
:-end_object term_consumer.
/**
Object active_buffer contains four non-logical variables:
head, tail, size, and count. As opposed
to logical (Prolog like) variables, non-logical variables can be
updated destructively.
Execution starts at the active_buffer/1 object constructor.
The constructor outputs a message and invokes loop/1.
Method loop/1 accepts either a put_term/1 or get_term/1
method invocation request of an independently running term_producer
or term_consumer object, depending on the internal state of this
active_buffer object as specified by the guards: in case the
current number of data items in the buffer is less than size
or greater than zero, the first matching method request in the
accept queue will be accepted for execution by active_buffer.
If only one guard holds, the corresponding method entry will
be accepted. In case there is no matching method request the
thread will be blocked until such a method message arrives.
A term_producer or term_consumer thread will block until
active_buffer accepts a particular method invocation request and
has returned its answer.
When either a get_term/1 or put_term/1 method is accepted, the
corresponding method in active_buffer will be executed :
Method get_term/1 retrieves the first entry in the linked
list and returns the corresponding term after the non-logical
variable head of the linked list has been updated.
Method put_term/1 will store the term in the linked list and
updates the non-logical variable tail or both the head
and the tail of the list.
After a get_term/1 or put_term/1 method "rendez-vous",
loop/1 in active_buffer prints the current state (see
out_list/1) and starts the next loop iteration.
**/
:-object active_buffer.
var head=null, tail=null, size=3, count=0.
active_buffer(N) :-
format('B ~w active_buffer main/1 running~n', [this]),
loop(N).
loop(0) :-
format('B ~w end of loop ...~n', [this]).
loop(I) :-
accept(
put_term(_) <== [count < size],
get_term(_) <== [count > 0]
),
out_list(head),
N is I-1,
loop(N).
get_term(Term) :-
-- count,
head <- get_node_term(Term),
head <- get_node_next(Next),
head := Next,
format('B ~w get term ~w~n', [this, Term]).
put_term(Term) :-
Node := new(buffer_node),
Node <- set_node_term(Term),
add_node(count, Node),
format('B ~w put term ~w~n', [this, Term]),
++ count.
add_node(0, Node) :-
!,
head := Node,
tail := Node.
add_node(_, Node) :-
tail <- set_node_next(Node),
tail := Node.
out_list(Node) :-
format('B~tcurrent nodes:~n'),
out_list(0, Node).
out_list(Curr, _) :-
Curr = count, !,
format('B~tend node list.~n').
out_list(I, Node) :-
N is I + 1,
Node <- get_node_term(Term),
Node <- get_node_next(Link),
format('B~tnode no. ~w, term = ~w~n', [N, Term]),
out_list(N, Link).
:-end_object active_buffer.
/**
Object buffer_node is a passive object (no constructor
involved). It has two non-logical variables: term and
next. These variables are destructively updated by the
set_node_term/1 and set_node_next/1 methods, respectively.
Object buffer_node is used by active_buffer
to construct a linked list of buffered terms :
**/ :-object buffer_node. var term, next=null. set_node_term(Term) :- term := Term. set_node_next(Next) :- next := Next. get_node_term(Term) :- Term := term. get_node_next(Next) :- Next := next. :-end_object buffer_node. /**/