Skip to content

Instantly share code, notes, and snippets.

@jburse

jburse/pharao.pl

Last active Jul 7, 2020
Embed
What would you like to do?
setup :-
retractall(stock(_)),
retractall(pyramid(_)),
between(1, 5000, N),
assertz(stock(N)),
fail.
setup.
worker :-
retract(stock(N)), !,
assertz(pyramid(N)),
worker.
worker.
process(N) :-
setup,
findall(ID, (between(1,10,_), thread_create(worker, ID)), L),
forall(member(ID, L), thread_join(ID)),
aggregate_all(count, pyramid(_), N).
setup :-
retractall(stock(_)),
retractall(pyramid(_)),
between(1, 5000, N),
assertz(stock(N)),
fail.
setup.
worker :-
retract(stock(N)),
assertz(pyramid(N)),
fail.
worker.
process(N) :-
setup,
findall(ID, (between(1,10,_), thread_create(worker, ID)), L),
forall(member(ID, L), thread_join(ID)),
aggregate_all(count, pyramid(_), N).
/**
* Queue example using new module "cond".
*
* Warranty & Liability
* To the extent permitted by applicable law and unless explicitly
* otherwise agreed upon, XLOG Technologies GmbH makes no warranties
* regarding the provided information. XLOG Technologies GmbH assumes
* no liability that any problems might be solved with the information
* provided by XLOG Technologies GmbH.
*
* Rights & License
* All industrial property rights regarding the information - copyright
* and patent rights in particular - are the sole property of XLOG
* Technologies GmbH. If the company was not the originator of some
* excerpts, XLOG Technologies GmbH has at least obtained the right to
* reproduce, change and translate the information.
*
* Reproduction is restricted to the whole unaltered document. Reproduction
* of the information is only allowed for non-commercial uses. Selling,
* giving away or letting of the execution of the library is prohibited.
* The library can be distributed as part of your applications and libraries
* for execution provided this comment remains unchanged.
*
* Restrictions
* Only to be distributed with programs that add significant and primary
* functionality to the library. Not to be distributed with additional
* software intended to replace any components of the library.
*
* Trademarks
* Jekejeke is a registered trademark of XLOG Technologies GmbH.
*/
:- use_module(library(misc/lock)).
:- use_module(library(misc/cond)).
:- monitor_new(O), retractall(monitor(_)), assertz(monitor(O)).
% dequeue(-Term)
dequeue(M) :-
retract(queue(M)),
!
; monitor(O),
with_lock(O, (repeat,
( retract(queue(M)),
!
; cond_wait(O),
fail))).
% enqueue(+Term)
enqueue(M) :-
monitor(O),
with_lock(O, (assertz(queue(M)),
cond_notify(O))).
/**
* Queue with timeout example using new module "cond".
*
* Warranty & Liability
* To the extent permitted by applicable law and unless explicitly
* otherwise agreed upon, XLOG Technologies GmbH makes no warranties
* regarding the provided information. XLOG Technologies GmbH assumes
* no liability that any problems might be solved with the information
* provided by XLOG Technologies GmbH.
*
* Rights & License
* All industrial property rights regarding the information - copyright
* and patent rights in particular - are the sole property of XLOG
* Technologies GmbH. If the company was not the originator of some
* excerpts, XLOG Technologies GmbH has at least obtained the right to
* reproduce, change and translate the information.
*
* Reproduction is restricted to the whole unaltered document. Reproduction
* of the information is only allowed for non-commercial uses. Selling,
* giving away or letting of the execution of the library is prohibited.
* The library can be distributed as part of your applications and libraries
* for execution provided this comment remains unchanged.
*
* Restrictions
* Only to be distributed with programs that add significant and primary
* functionality to the library. Not to be distributed with additional
* software intended to replace any components of the library.
*
* Trademarks
* Jekejeke is a registered trademark of XLOG Technologies GmbH.
*/
:- use_module(library(misc/lock)).
:- use_module(library(misc/cond)).
:- monitor_new(O), retractall(monitor(_)), assertz(monitor(O)).
% dequeue(-Term)
dequeue(M) :-
retract(queue(M)),
!
; monitor(O),
with_lock(O, (repeat,
( retract(queue(M)),
!
; cond_wait(O),
fail))).
% dequeue(-Term, +Integer)
dequeue_timeout(M, T) :-
D is current_time+T, /* compute deadline */
( retract(queue(M)),
!
; monitor(O),
with_lock(O, (repeat,
( retract(queue(M)),
!
; W is D-current_time, /* compute distance */
( W =< 0,
!,
fail /* timeout outcome */
; cond_wait_timeout(O, W),
fail))))).
% enqueue(+Term)
enqueue(M) :-
monitor(O),
with_lock(O, (assertz(queue(M)),
cond_notify(O))).
/**
* Store using module "lock".
*
* Warranty & Liability
* To the extent permitted by applicable law and unless explicitly
* otherwise agreed upon, XLOG Technologies GmbH makes no warranties
* regarding the provided information. XLOG Technologies GmbH assumes
* no liability that any problems might be solved with the information
* provided by XLOG Technologies GmbH.
*
* Rights & License
* All industrial property rights regarding the information - copyright
* and patent rights in particular - are the sole property of XLOG
* Technologies GmbH. If the company was not the originator of some
* excerpts, XLOG Technologies GmbH has at least obtained the right to
* reproduce, change and translate the information.
*
* Reproduction is restricted to the whole unaltered document. Reproduction
* of the information is only allowed for non-commercial uses. Selling,
* giving away or letting of the execution of the library is prohibited.
* The library can be distributed as part of your applications and libraries
* for execution provided this comment remains unchanged.
*
* Restrictions
* Only to be distributed with programs that add significant and primary
* functionality to the library. Not to be distributed with additional
* software intended to replace any components of the library.
*
* Trademarks
* Jekejeke is a registered trademark of XLOG Technologies GmbH.
*/
:- use_module(library(misc/lock)).
:- use_module(library(basic/random)).
:- use_module(library(standard/arith)).
:- use_module(library(system/thread)).
:- use_module(library(basic/lists)).
:- mutex_new(O), retractall(mutex(_)), assertz(mutex(O)).
/**
* put_unsafe(K, V):
* The predicate replaced the value of the K
* by the new value V without locking.
*/
put_unsafe(K, V) :-
retractall(store(K,_)),
assertz(store(K, V)).
/**
* put(K, V):
* The predicate replaced the value of the K
* by the new value V with locking.
*/
put(K, V) :-
mutex(O),
with_lock(O,
(retractall(store(K, _)),
assertz(store(K, V)))).
get(K, V) :-
mutex(O),
with_lock(O,
store(K, V)).
/*******************************************************************/
/* Some Testing */
/*******************************************************************/
/**
* fill_unsafe:
* The predicate randomly fills the store in parallel
* using the predicate put_unsafe/1.
*/
fill_unsafe :-
retractall(store(_, _)),
findall(T, (between(1,4,_),
thread_new(fill_unsafe_thread, T),
thread_start(T)), L),
forall(member(T, L), thread_join(T)).
fill_unsafe_thread :-
between(1, 1000, _),
random(100, K),
random(100, V),
put_unsafe(K, V),
fail.
fill_unsafe_thread.
/**
* fill:
* The predicate randomly fills the store in parallel
* using the predicate put/1.
*/
fill :-
retractall(store(_, _)),
findall(T, (between(1,4,_),
thread_new(fill_thread, T),
thread_start(T)), L),
forall(member(T, L), thread_join(T)).
fill_thread :-
between(1, 1000, _),
random(100, K),
random(100, V),
put(K, V),
fail.
fill_thread.
/**
* check(K):
* The predicate succeeds in K with a key which has
* multiple values.
*/
check(K) :-
bagof(V, get(K, V), L),
L = [_,_|_].
% ?- repeat, write('.'), flush_output, fill_unsafe, check(K).
% ..K = 3 ;
% .K = 1 ;
% ........K = 0 ;
% K = 5 ;
% ....K = 2 ;
% ?- repeat, write('.'), flush_output, fill, check(K).
% ..........................................................
% ..............................................................
/**
* Store using module "lock".
* SWI-Prolog version.
*
* Warranty & Liability
* To the extent permitted by applicable law and unless explicitly
* otherwise agreed upon, XLOG Technologies GmbH makes no warranties
* regarding the provided information. XLOG Technologies GmbH assumes
* no liability that any problems might be solved with the information
* provided by XLOG Technologies GmbH.
*
* Rights & License
* All industrial property rights regarding the information - copyright
* and patent rights in particular - are the sole property of XLOG
* Technologies GmbH. If the company was not the originator of some
* excerpts, XLOG Technologies GmbH has at least obtained the right to
* reproduce, change and translate the information.
*
* Reproduction is restricted to the whole unaltered document. Reproduction
* of the information is only allowed for non-commercial uses. Selling,
* giving away or letting of the execution of the library is prohibited.
* The library can be distributed as part of your applications and libraries
* for execution provided this comment remains unchanged.
*
* Restrictions
* Only to be distributed with programs that add significant and primary
* functionality to the library. Not to be distributed with additional
* software intended to replace any components of the library.
*
* Trademarks
* Jekejeke is a registered trademark of XLOG Technologies GmbH.
*/
/**
* put_unsafe(K, V):
* The predicate replaced the value of the K
* by the new value V without locking.
*/
put_unsafe(K, V) :-
retractall(store(K,_)),
assertz(store(K, V)).
/**
* put_unsafe_transaction(K, V):
* The predicate replaced the value of the K
* by the new value V without locking.
*/
put_unsafe_transaction(K, V) :-
transaction((retractall(store(K,_)),
assertz(store(K, V)))).
/**
* put(K, V):
* The predicate replaced the value of the K
* by the new value V with locking.
*/
put(K, V) :-
with_lock(lock,
(retractall(store(K, _)),
assertz(store(K, V)))).
get(K, V) :-
with_lock(lock,
store(K, V)).
/* with_mutex/2 doesn't work for non-det get/2, so we need with_Lock/2 */
:- meta_predicate with_lock(?,0).
with_lock(L, G) :-
setup_call_cleanup(mutex_lock(L),
G,
mutex_unlock(L)).
/*******************************************************************/
/* Some Testing */
/*******************************************************************/
/**
* fill_unsafe:
* The predicate randomly fills the store in parallel
* using the predicate put_unsafe/1.
*/
fill_unsafe :-
retractall(store(_, _)),
findall(T, (between(1,4,_),
thread_create(fill_unsafe_thread, T)), L),
forall(member(T, L), thread_join(T)).
fill_unsafe_thread :-
between(1, 1000, _),
random_between(0, 99, K),
random_between(0, 99, V),
put_unsafe(K, V),
fail.
fill_unsafe_thread.
/**
* fill_unsafe_transaction:
* The predicate randomly fills the store in parallel
* using the predicate put_unsafe_transaction/1.
*/
fill_unsafe_transaction :-
retractall(store(_, _)),
findall(T, (between(1,4,_),
thread_create(fill_unsafe_transaction_thread, T)), L),
forall(member(T, L), thread_join(T)).
fill_unsafe_transaction_thread :-
between(1, 1000, _),
random_between(0, 99, K),
random_between(0, 99, V),
put_unsafe_transaction(K, V),
fail.
fill_unsafe_transaction_thread.
/**
* fill:
* The predicate randomly fills the store in parallel
* using the predicate put/1.
*/
fill :-
retractall(store(_, _)),
findall(T, (between(1,4,_),
thread_create(fill_thread, T)), L),
forall(member(T, L), thread_join(T)).
fill_thread :-
between(1, 1000, _),
random_between(0, 99, K),
random_between(0, 99, V),
put(K, V),
fail.
fill_thread.
/**
* check(K):
* The predicate succeeds in K with a key which has
* multiple values.
*/
check(K) :-
bagof(V, get(K, V), L),
L = [_,_|_].
% ?- repeat, write('.'), flush_output, fill_unsafe, check(K).
% ....
% K = 30 ;
% ?- repeat, write('.'), flush_output, fill_unsafe_transaction, check(K).
% ..
% K = 4 ;
@jburse

This comment has been minimized.

Copy link
Owner Author

@jburse jburse commented Jun 22, 2020

Preview: New tutorial Web Tomcat Prolog example I. (Jekejeke)

In the course of the next release 1.4.5 of Jekejeke Prolog runtime library
we are reworking our web deployment scenarios. There is also a certain
demand to have improved documentation there.

The old web deployment documentation did only show a Java
server page and no true servlet. So we started a new document
and added a true servlet example. A servlet is just a Java class

that implements the HttpServlet class. The servlet is listed in
the web.xml of the corresponding web context. The web.xml features
the following entry now:

<servlet>
    <servlet-name>plain_servlet</servlet-name>
    <servlet-class>example01.Plain</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>plain_servlet</servlet-name>
    <url-pattern>/servlet/example01.Plain</url-pattern>
</servlet-mapping>

The servlet Plain works it way by creating a knowledge base, loading
a table into it and returning the results of a query over this table.
Likewise Java server pages and servlet can be also directly

accessed through a telnet utility. We demonstrate how this can
be done. The servlet Plain returns text/plain without any HTML
mark up, the result is seen here:

image014

With the advent of the Windows Subsystem for Linux (WSL) it is
easiest to install this application from the Windows store, and then
use telnet from within the WSL bash.

@jburse

This comment has been minimized.

Copy link
Owner Author

@jburse jburse commented Jun 22, 2020

Preview: New tutorial Web Tomcat Prolog example II. (Jekejeke)

For release 1.4.5 we did change the way how knowledge bases will
be created. In the past we had a first experiment to provide a class
loader to a knowledge base. There was a constructor taking a class

and the class loader of the class was used. In the new tutorial we
already use the new API, where the parameter is now a classs loader.
This is important since in the new tutorial we deploy the Jekejeke

Prolog runtime library in the Tomcat common location <server>/lib folder:

lib
 +---    interpreter.jar

Subsequently we intialize by specifying the web context class loader:

        know = new Knowledgebase(ToolkitLibrary.DEFAULT,
                   Data.class.getClassLoader());

The result is seen here:

image016

This was basically the example of our old web deployment tutorial.
The main changes to this example are to the data holder. We not only
changed the knowledge base construction, we also refined the locking.

@jburse

This comment has been minimized.

Copy link
Owner Author

@jburse jburse commented Jun 22, 2020

Preview: New tutorial Web Tomcat Prolog example III. (Jekejeke)

The highlight of the new web tutorial is the example where we demonstrate
sharing a Prolog interpreter by different knowledge bases on a web server.
This is done via our knowledge base hierarchy feature.

A common knowledge base will only setup the Prolog interpreter. This Prolog
interpreter is then shared by two knowledge bases Child and Child2. The
sharing is done via a parent link similarly as class loaders are shared.

image018

To allow this sharing there was already an old knowledge base constructur
that takes a parent knowledge base. For the upcoming release 1.4.5 we also
refined the API and now provide a constructore that also takes a class loader:

        Knowledgebase parent = Parent.getKnowledgebase();
        know = new Knowledgebase(parent,
                   Child.class.getClassLoader());

More details are found in the tutorial and on GitHub. The example was tested
with a Tomcat web container and we also show configuration files and start/stop scripts.
For another web container different configuration and start/stop might apply.

New Tutorial: Deployment Methods Web
http://www.jekejeke.ch/idatab/doclet/prod/en/docs/05_run/15_stdy/10_web/package.html

GitHub Sources: Deployment Methods Web
https://github.com/jburse/jekejeke-samples/tree/master/jekrun/deployweb

@jburse

This comment has been minimized.

Copy link
Owner Author

@jburse jburse commented Jun 28, 2020

Preview: New Prolog module "cond" for condition variables I. (Jekejeke)

We just completed a little multi-theading Odyssey. Our first goal was to
make available the intrinsic locks and condition variables of Java objects. But
this didn't work out as expected. We managed to provide a primitive:

:- use_module(library(experiment/monitor)).
my_pred :- 
    ...
    synchronized(Object, Goal)
    ...

But it had the disadvantage that it would release the lock at a
non-deterministic exit port and then acquire again at the redo port. This
locking does not match our eager shared tabling semantics.

There is no way out of this dilemma since JDK13 decided to discontinue
Unsafe.monitorEnter() and Unsafe.monitorExit(). So our hands are tied
to provide a solution based on intrinsic locks. So we decided to come

back to our old module "lock" for the locking aspect. To ease the life
of the programmer we introduced a new meta predicate with_lock/2.
We explicitly chosen another name than SWI-Prologs with_mutex/2,

because the meta predicate is able to deal with any object that implements
the Java interface Lock. And we find for example a read lock which is not
binary like a mutex. The new take is:

:- use_module(library(misc/lock)).
my_pred :- 
    ...
    with_lock(Lock, Goal)
    ...

For some demonstration there is the Prolog text store.p. We provide
put_unsafe/2 which doesn't use a lock and put/2 which does use a lock. In even
this simple example we can observe undesired interleaving. Two threads might

have completed the retractall/1 in put_unsafe/2 leading to a duplicate entry.
The time it takes that such a situation happens can differ. The testing predicates
fill_unsafe/0, fill/0 and check/1 allow playing around with the two predicates:

unsafee_example

But locks alone are only half of the story. Among other multi-threading primitives
we find condition variables which can be derived from locks. In the following
comments we will demonstrate their usage from within Prolog.

@jburse

This comment has been minimized.

Copy link
Owner Author

@jburse jburse commented Jun 28, 2020

Preview: New Prolog module "cond" for condition variables II. (Jekejeke)

We moved all condition variables operations from the module "lock" to the
new module "cond" and streamlined their names a little bit. What is avaible
among condition variables are the following predicates now:

  • cond_new(L, C):
    The predicate succeeds in C with a new condition for the lock L.
  • cond_wait(C):
    The predicate succeeds when the condition C was notified.
  • cond_wait_timeout(C, T):
    The predicate succeeds when the condition C was notified
    in the time-out T. Otherwise the predicate fails.
  • cond_notify(C):
    The predicate succeeds in notifying one thread waiting on C.
  • cond_notify_all(C):
    The predicate succeeds in notifying all threads waiting on C.

A lock might have zero, one or many condition variables. Nevertheless a
constellation that might appear in practice is the use of a lock with exactly
one condition variable. This mimics the intrinsic lock and condition variables

of a Java object. As a convenience for this constellation there is a now the
new class Monitor. This class implements at the same time the interface
Lock and the interface Condition, so that it can be used with the module

"lock" and the module "cond" at the same time:

image001_small

There is a convenience predicate in the module "cond" that creates
a monitor from a new mutex and a new condition variable derived from
the same mutex. The new predicate is:

  • monitor_new(M):
    The predicate succeeds in M with a new monitor.

But a monitor and a condition variable might come from other places, such
as a predicate property or a module property. The current example simply
creates a monitor while consulting like we did for the previous example in the

case of a lock. The Prolog text queue.p shows how realize an unbounded
queue consisting of a enqueue operation and a dequeue operation. The enqueue
will use cond_notify/1 whereas the dequeue operation does the following

double checking and cond_wait/1 in case no element was yet found:

% dequeue(-Term)
dequeue(M) :-
   retract(queue(M)),
   !
;  monitor(O),
   with_lock(O, (repeat,
      (  retract(queue(M)),
         !
      ;  cond_wait(O),
         fail))).

The queue can be directly tested in the Jekejeke Prolog Runtime Library top-level
by opening additional thread tabs. In the "Init" tab we were loading the Prolog text.
In the "Thread #1" tab we placed a dequeue/1 which went into blocking. In the "Init"

tab we palced an equeue/1 and which could see in the "Thread #" tab:

queue_example

The code of the predicate dequeue/1 uses a repeat loop. Prolog repeat loops can
be exited by a cut (!). We exit successfully when an element arrived. In the
next example we show how we can have success and failure results.

@jburse

This comment has been minimized.

Copy link
Owner Author

@jburse jburse commented Jun 28, 2020

Preview: New Prolog module "cond" for condition variables III. (Jekejeke)

We now turn to the Prolog text queue2.p. We provide a predicate dequeue2.p
which might timeout. To deal which such programming requirements the
module "cond" provides this evaluable function as well:

  • current_time:
    Returns the current time in milliseconds.

The unit of the evaluable function current_time/0 matches the unit of
the predicate cond_wait_timeout/2. We can use it to program a predicate
dequeue_timeout/2 as follows:

dequeue_timeout(M, T) :-
   D is current_time+T, /* compute deadline */
   (  retract(queue(M)),
      !
   ;  monitor(O),
      with_lock(O, (repeat,
         (  retract(queue(M)),
            !
         ;  W is D-current_time, /* compute distance */
            (  W =< 0,
               !,
               fail             /* timeout outcome */
            ;  cond_wait_timeout(O, W),
               fail))))).

The code shares with dequeue/1 the same repeat loop. This repeat loop
is already necessary since cond_wait/1 and cond_wait_timeout/2 might
spuriously exit, so that the desired condition has to be anyway rechecked.

For cond_wait_timeout/2 we use the repeat loop to further check whether the
deadline D was reached, which is the case if the distance W to deadline is
zero or negative. We then use the same distance to issue a further

cond_wait_timeout/2. We can use again the Jekejeke Prolog Runtime Library
top-level for testing. If we wait long enough for enqueue_timeout/2 and
don't provide an element it will fail:

queue_example2

It should be noted here that we didn't use locks and condition variables
for safety here, since no unfavorable interleaving is possible by a single
operation retract/1. We could also have used a busy loop. But a buys loop

would waste CPU resources so that a condition variable is better choice.
A condition variable wait can be also combined with safety considerations
at the same time. So the condition and action in the critical section can be

more complex than what we showed. The source code of the new module
"cond" is already on GitHub and the user manual is also already written.
We will go public with it in 1-2 months with the upcoming release 1.4.5.

Open Source: Module "cond"
http://github.com/jburse/jekejeke-devel/blob/master/jekrun/headless/jekpro/frequent/misc/cond.p

User Manual: Module "cond"
http://www.jekejeke.ch/idatab/doclet/prod/en/docs/05_run/10_docu/05_frequent/07_theories/21_misc/07_cond.html

@jburse

This comment has been minimized.

Copy link
Owner Author

@jburse jburse commented Jul 4, 2020

?- aggregate_all(count,concurrent_and(between(1,10000,X),between(1,10000,Y)),N).

strange_memory

In the above the threads stop working after the first third of time, and aggregation
goes on, because it depleats the answer queue. So in the second third and
last third only aggregate_all/3 is working.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.