Ann: SWI-Prolog 8.3.3

Dear SWI-Prolog user,

I’m happy to announce SWI-Prolog 8.3.3. Input from Adrian Wong and David
Tonhofer have improved documentation. Input from David Tonhofer and
Sebastian Sardina has further cleaned JPL documentation and error
handling. Discusion on this forum have helped shaping new concurrency
support. Highlights:

  • Added thread_wait/2 and thread_update/2 as new built-ins that support
    concurrent blackboard style programming.
  • Added concurrent_forall/2,3 and concurrent_and/2 as new high level
    predicates for concurrent computation.
  • Make thread_get_message/1,2 listen immediately to thread_signal/2.
  • Make library(persistency) thread-safe.
  • Enhanced and documented saving settings from library(settings)
  • Build Windows binaries using the new Docker image. This has some consequences:
    • Using PGO optimized code should make this version faster
    • JPL is only available for the 64-bit version because OpenJDK
      is only available for 64 bits and Oracle JDK doesn’t install in Wine
      and cannot easily be included in a Docker image due to license
      restrictions.

The 8.3.2 version failed for Macports. Hopefully the upgrade works this time.

Enjoy --- Jan

SWI-Prolog Changelog since version 8.3.2

  • ADDED: concurrent_and/2,3 based on Jan Burse’s balance/1.

  • DOC: library(persistency).

  • FIXED: Provide elementary thread-safety. Jan Burse.

  • TEST: Added SKIP_SSL_TESTS option to CMake config.

  • ADDED: concurrent_forall/2,3: as forall, running tests in parallel.

  • ENHANCED: thread_signal/2: avoid a max 0.25 sec wait if the signalled
    thread is blocked on a message queue.

  • PORT: Allow pthread_cond_timedwait() to return EINTR.

  • ENHANCED: copy_term/3 to sort the attvars prior to generating
    the goals. This produces constraints at the toplevel in more
    predictable order. Rick Workman.

  • CLEANUP: Term names set to those of ISO Standard

  • DOC: Updated “exception term” page with ISO info

  • DOC: Fix typos in manual

  • DOC: Remove outdated status in manual Shared table space limit was
    added in commit 6a01fbf70447b419447281826e770ec0cd21ab3d

  • ENHANCED: Issue#625: library(settings): only save when modified and
    document persistency issues.

  • FIXED: Consider files loaded from a state already loaded.

  • FIXED: Allow for saved states of more than 4Gb.

  • DOC: add missing documentation for native zip handling predicates.

  • MODIFIED: Renamed thread_wait_on_goal/2 into thread_wait/2 and added
    thread_update/2.

  • ENHANCED: save_settings/1 to use setup_call_cleanup/3 to guarantee
    reclaiming resources.

  • FIXED: Recursive autoloading when a saved state loads an additional
    file that sets the Prolog flag autoload to false. Peter Ludemann.

  • MODIFIED: thread_wait_on_goal/2: make it per-module rather than global
    over the database.

  • ADDED: thread_wait_on_goal/2 to wait for database changes.
    This predicate is similar to its Qu-Prolog cousin.

  • MODIFIED: Option processing now raises a type_error(option, Option)
    if an option is malformed (not Name(Value) neither Name = Value).

  • DOC: Fix typos in manual

  • PPA: Removed support for Ubuntu disco (19.10).

Package chr

  • BUILD: Lacking dependency may cause build failures.

Package http

  • DOC: Document the library(zlib) and library(http/http_stream) plugins
    for library(http_open).

Package jpl

  • CLEANUP: Exptn-generating code & msgs streamlined

  • TEST: Guess /usr/share/java/junit.jar is junit4 if we can’t find a
    clear version 4. Not sure how safe this this.

  • PORT: We do not need javah

  • DOC: Added link to JPL Github repo in main page

  • ADDED: Unit testing for checking Prolog and JPL Exceptions

Package ssl

  • TEST: Allow disabling the SSL tests. Creating the certificates seems
    really hard inside a docker container using wine as it refuses to
    seed the openssl.exe random generator. Why remains unclear to me.
2 Likes

Thanks to all who contributed.

I was very surprised to see concurrent_and/2 (original) which I hope to use soon, so a noted thanks to Jan Burse.

thread_signal/2. Note that the new stuff is as usual not set in stone. If people have good reasons to make changes things can change.

1 Like

The SWI-Prolog concurrent_and/2 uses an unbounded answer queue,
whereas my balance/1 uses a bounded answer queue. So they are not the
same. Especially you can generate strange memory consumption

patterns for concurrent_and/2, which don’t happen for my balance/1.

Like here:

?- 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.

My intention is that balance/1 does not work like that. Rather that it has a
synchronous aspect and that it synchronized with the demand in the continuation.
You see the difference in this query, which only wants one element:

 SWI-Prolog (threaded, 64 bits, version 8.3.3)

?- concurrent_and(between(1,1000,X),(between(1,1000,Y),
   (X==1000,Y==1000->write(last),nl;true))), read(Z).
|: last
|: abc.

X = Y, Y = 1,
Z = abc .

On the other hand my system doesn’t display a “last”,
since it uses a bounded answer queue:

Jekejeke Prolog 4, Runtime Library 1.4.4

?- balance((between(1,1000,X),(between(1,1000,Y),
    (X==1000,Y==1000->write(last),nl;true)))), read(Z).
abc.
X = 2,
Y = 1,
Z = abc 

This synchronization with the continuation of balance/1 is wanted.
It makes use it constant memory. The memory used by balance/1 is
the same like the sequential run multiplied by the number of used

threads. On the other hand the SWI-Prolog concurrent_and/2 might
write enormous amounts of results into the answer queue. My
synchronization with the continuation is of advantage for certain

search algorithms, like branch and bound. Maybe SWI-Prolog
could provide an option to make the answer queue bounded.
If concurrent_and/2 implements something else than balance/1,

I don’t take resonsibility at all(*). Please don’t blame me for memory
crashes, thread explosion or CPU burning. Especially memory
crashes shouldn’t happen with balance/1.

(*)
‘I don’t take responsibility at all’
https://www.politico.com/news/2020/03/13/trump-coronavirus-testing-128971

Added, I don’t see much reason to make it an option. I thought about the generator being too fast, but didn’t really consider this to be an issue with the consumer.

1 Like

With some sliding window technique, for example used in TCP/IP
paket assembly, could also program a balance_sorted/1, which
would not reorder the results. Dunno yet how hard this is.

Or an unbounded buffer at answer outlet would also do. But
something with a bounded window size would be more in spirit
of balance/1. It possibly requires a selective queue to get from.

I think SWI-Prolog has matching for get queue and for
web prolog there was something new with freeze/2.

Thanks for concurrent_forall/2, it speed up a time-consuming predicate which loads a lot of files by a factor of 4.5 by just changing forall/2 to concurrent_forall/2.

Appreciate it!

Thanks or reporting! Interested in some more data points …

As in compiling Prolog files or something else?

How many cores/threads?

These are 73 files with data that gets parsed and then loaded into the database with dynamic predicates.

4 cores, 8 hyperthreads. So cpu_count returns 8, and that is what I am using.

Again thanks for the work!

1 Like

Now there is concurrent_and/2, which corresponds to my
balance/1. Did nobody ask for setup_balance/1 yet?

Ever tried to do a concurrent CLP(FD) queens problem
with concurrent_and/2? The setup_balance/1 is specified here:

Module "distributed"
http://www.jekejeke.ch/idatab/doclet/prod/docs/05_run/10_docu/02_reference/07_theories/02_runtime/04_distributed.html

Edit 23.07.2020:
You can “overclock” the generator. It need not
just generate n different values, when there
are n threads. Usually you would generate more

values, that are then balanced over the threads.
This is possible since setup is only done once
per thread. If each thread would redo the

model building and the overclocking is m, i.e.
the generator would generate m*n values, then
you would have m*n setup calls.

This way you have only n setup calls.