Dear SWI-Prolog user,
SWI-Prolog 10.1.20 is ready for download. This release fixes some build issues
and slowdown on MacOS. It improves the profiler, which can now time exactly, makes tabling with many tables use much less memory and adds IDE tools to see where memory and time go and how tables depend on each other. Highlights:
Core
- Profiler: profile/2 option exact(Bool) measures exact wall or CPU
time rather than sampling. The new Prolog flagprofile_exact
(defaultauto) uses exact timing if reading the clock is cheap.
Time is recorded in nanoseconds. Fixed several issues, notably
profiling in one thread stopping the profiler of another and a
crash when wall-time profiling interrupted foreign code. - Tabling uses much less memory for programs with many tables: compact
edge sets and smaller nodes for the incremental dependency graph
(IDG), smaller tries, private tables only get an atom when needed
and abolishing private tables is much faster. Fixed several bugs
in incremental tabling, WFS simplification and races on tables. - New tripwires
max_incomplete_subgoalsandmax_scc_subgoalsand
introspection of the subgoal dependency graph and the IDG of another
thread, used by the new IDE tools. - Foreign interface: PL_get_list_slice() and PL_get_arg_values() for
fast processing of lists and compound terms, and the Prolog flag
validate_apito disable the argument checks (false with -O). - Fixed retract/1 on SSU (=>) clauses, clause/2, bagof/3 with a
qualified goal, current_op/3, write_term/2 with cyclic attributes,
syntax error locations and several crashes. - Clang builds no longer slow down non-compiled arithmetic (up to 4
times on macOS). MODIFIED: on macOS, thesystem_thread_idis now
the system-wide id shown by lldb and Instruments. - Packs: check that the pack name matches the repository name.
HTTP
- Fixed #176: http_stop_server/2 could hang.
JPL
- Fixed conversion of atoms with characters above U+00FF to Java
Strings on all systems except Windows.
XPCE (GUI)
- IDE: new memory viewer (Tools/View memory) showing how Prolog uses
its memory as rings or a treemap, a dependency viewer for the IDG
of a thread, an SDG viewer showing the incomplete tables of a
thread and a Debug menu in every IDE window. The thread monitor
shows graphs of the tables and incomplete tables of a thread. - Profiler: a Call tree tab showing where the time below a predicate
goes, the text report lists XPCE methods and call graph edges have
tooltips. library(plot/hierarchy_chart)shows hierarchical distributions as
rings or a treemap, with a new Disk usage demo. Also a new demo
Ripples to illustrate GPU rendering,plotter->hover_values,
path<->event_area,->tooltipandlibrary(open_document).- Epilog: terminal output is much faster (
catof a large file from
25 to 0.4 seconds) and URLs and File:Line locations become links
(terminal_image<->auto_links, hook epilog:auto_link/2). - Windows: fixed 32-bit integer arithmetic and class date, and xpce
compiles with clang (Windows on ARM64). Compiles with SDL 3.2.
Work In Progress (not in this release)
AI allows to explore and implement improvements much faster. The workflows I just started mainly address performance. The first step is to create blobs as thread-local. This avoids various synchronization steps and allows for precise in-thread precise AGC. This should notably have impact on tabling as tabling uses a lot of blobs, mostly from the same thread. However, the local blobs can safely be copied to other threads, which makes them global and passes their lifetime over to global AGC. In a next step I hope to finally modernize how data tagging is done and exploit the 64 bits we have ![]()
Enjoy --- Jan
SWI-Prolog Changelog since version 10.1.19
-
ENHANCED: the text report of profile/2 lists XPCE methods and keeps its
columns aligned The report lists all nodes, as profile_data/1 does.
The new multifile hook prolog:profile_node_label/2 names a node that
is not a predicate. A label too wide for its column is elided in the
middle, so the module or class and the name remain visible. The columns
of the counts are as wide as their widest count among the rows shown,
taking the extra width from the labels, so the report keeps its width. -
FIXED: profiler: a call still running gave 2^64-1 redos A call
that has not exited or failed when the profile is collected, such as
profiler/2 itself, made exits+fails-calls negative. With ports enabled,
profile_data/1 and the text report showed this as a huge redo count. -
FIXED: profile_data/1 omitted the nodes of XPCE methods So the profiler
GUI could show a method only as a callee: neither its own callees nor
its node. $prof_procedure_data/8 raised a type error on such a node
rather than leaving it to the XPCE profile type. -
CLEANUP: Access cell tags only through pl-data.h Code outside
pl-data.h no longer uses LMASK_BITS, the STG_* storage bits or
storage(). pl-data.h provides named tagex() values (TAGEX_*),
including the private markers of the compiler, read/1, tries and
variant/2, constructors consCompound(), consAttVar(), consIndirect(),
consTagged()/valTagged() for index cells, consAddr()/valAddr()/
addrDelta() for address cells and storage tests isGlobalPtr(),
isIndirectHdr(), isLocalPtr(), isLocalRef() and isInlineInt().
No change to the encoding. This is step 2 of doc/tagging/top-tags.md. -
FIXED: concurrent trie_symbol() could give a trie two atoms Each atom
destroys the trie when reclaimed, so the second destroyed it again,
typically at halt. The thread that loses the race to set the symbol
detaches its atom from the trie. -
FIXED: discarding the nodes of a compiled answer trie left T_DELAY
pointing to a freed answer Simplification can make a conditional
answer unconditional without recompiling the trie, so the trie
becomes discardable while its clause still refers to the answer node.
trie_discard_nodes() now keeps the nodes if the clause has a T_DELAY
that refers to one. -
FIXED: abolishing an imported predicate created a definition with share
count 0 If another module imported the new definition, halting freed it
while it was still referenced. lookupProcedure() and abolishProcedure()
now use newDefinition(), which also clears the argument info. -
FIXED: race destroying a variant table that another thread enumerates
If a thread enumerated the variant table of another thread (e.g.,
using trie_gen/3 in an engine, as the IDG viewer does) while the owner
abolished its tables, both threads could see the reference count drop
to zero and empty the table, causing a crash. clear_variant_table()
now holds a reference while deciding, so exactly one thread empties
the table. -
TEST: Skip thread and profiler tests in single-threaded builds
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
ENHANCED: Check that pack name and repository name match. Currently,
the demanded match is case-insensitive. -
ENHANCED: profile/2 uses exact timing by default if this is cheap The
new Prolog flag profile_exact (defaultauto) provides the default
for the exact option of profile/2. Withauto, exact timing is
used if reading the clock is cheap and, for CPU time, the thread CPU
clock is precise. In virtual machines that trap reading the TSC,
exact profiling can be more than 20 times slower than sampling. -
ADDED: profile/2 option exact(Bool) for exact wall and CPU time
Rather than sampling, the profiler reads a clock each time execution
moves to another node of the call tree. Time spent updating the call
tree is measured and reported as accounting time. For CPU time, the
thread’s CPU time is read once per sample period and after each long
interval, where the thread may have been off the CPU. The summary
of profile_data/1 has the new keysclockandexact. -
FIXED: profile/2 in one thread stopped the profiler of another thread
reset_profiler/0 and profile/2 stopped the profiler of whatever thread
was being profiled. Starting the profiler also re-armed the process’
interval timer before checking that no other thread is being profiled.
profile/2 now raises a permission error and leaves the other thread
alone. -
FIXED: show_profile/1 raised zero_divisor if no sample was taken A
sampled profile of a goal that runs shorter than the sample period has
no ticks. The textual report now shows 0% rather than raising an error. -
FIXED: stop the profiler timer when the profiled thread exits If a
thread exited while being profiled, the profiler was deactivated,
but the interval timer kept firing and the signal handler stayed
installed until the next profiler start. -
FIXED: wall-time profiling crashed if SIGALRM arrived in a foreign
thread SIGPROF and SIGALRM are process signals that may be delivered
to any thread. The profiler signal handler now forwards them to
the profiled thread. Before, a sample taken in a non-Prolog thread
crashed in WallTime() and a sample taken in another Prolog thread
updated the call tree of the profiled thread concurrently. -
ENHANCED: profiler records time in nanoseconds rather than truncated
ms Profile ticks are now nanoseconds of CPU or wall time. Each sample
was truncated to whole milliseconds, losing on average 0.5ms per 5ms
sample, and samples below 1ms were lost completely. The time of the
last tick is kept as nanoseconds since activation, so the ticks add
up exactly to the profiled time. -
BUILD: silence maybe-uninitialized warning in set_stream/2 newline
handling newline_domain_error() returned the result of PL_error(),
so GCC could not see that atom_to_newline_mode() never succeeds
without setting the mode. -
FIXED: write_term/2 with attributes(write) crashed on cyclic attributes
E.g., put_attr(X, a, f(X)) caused unbounded recursion. An attributed
variable met while writing its own attributes is now written as just
its name. Also close the foreign frame if writing the attributes fails.
Reported by Shivaji Gadde. -
FIXED: location of syntax errors for unterminated quoted text and
block comments For string input the context held only the first byte
of the input, cutting multibyte characters in half. It now holds the
text read with the offset of the opening quote or comment. For other
streams the error now points at the opening quote or comment rather than
(one character after) the start of the term. Reported by Shivaji Gadde. -
FIXED: retract/1 could not remove SSU (=>) clauses retract((Head
=> Body)) rebuilt the clause using :-/2 and thus never matched.
Reported by Shivaji Gadde. -
FIXED: current_op/3 with given priority found operators hidden by a
local definition E.g., after op(250, yfx, rem), current_op(400, T,
rem) returned the system definition. Reported by Shivaji Gadde. -
FIXED: clause/2 left the body unbound if all moved unifications became
head unifications E.g., for p(X,Y) :- Y = f(X), clause(p(A,B), Body)
succeeded with Body unbound. Reported by Shivaji Gadde. -
FIXED: bagof/3 missed free variables of a qualified goal bound through
a variable E.g., G = member(X-Y,[1-a,2-b]), bagof(X, user:G, L) called
as a term did not see Y as free variable. Reported by Shivaji Gadde. -
FIXED: powm/3 continued evaluation after raising a type error Reported
by Shivaji Gadde. -
FIXED: crash on an invalid newline(Mode) stream option open/4
and set_stream/2 passed the atom rather than a term reference as
culprit of the domain error, causing a crash or instantiation error.
Reported by Shivaji Gadde. -
ENHANCED: reduce atom-table contention when many threads create blobs
Tabling creates a trie blob per table, so a multi-threaded tabling
workload creates millions of atoms concurrently. -
FIXED: crash when a thread writes to an inherited with_output_to/2
capture Threads inherit the standard streams of their creator. If the
creator is inside with_output_to/2,3, these are the mutex-less memory
stream used for the capture. Concurrent access corrupted the lock
count and the buffer, causing a Sunlock() assertion failure or invalid
code points. This notably affected plunit tests that create threads.
copyStandardStreams() now adds a mutex to such streams. -
FIXED: prune answers deleted by incremental reevaluation Answers that
do not reappear when re-evaluating an incremental table were deleted
without pruning their branch, leaving dead nodes in the answer trie
and its compiled clause. reeval_complete() now prunes the trie after
the answer walk if the answer set changed and nobody is enumerating
the trie. -
ENHANCED: discard the answer nodes of compiled incremental tables
Incremental tables kept both the nodes of their answer trie and the
compiled clause, as reevaluation works on the nodes. Now they discard
the nodes like other tables and prepare_reeval() restores them from
the clause. For a 10M answer incremental table this reduces the
memory after completion from 1,064 MB to 409 MB (after trim_heap/0). -
FIXED: prepare_reeval() clears TRIE_DISCARDABLE, so a table that
becomes conditional on reevaluation does not lose its delay lists.
Tables with an IDG are only discardable if they have no conditional
answers and are never dropped as intermediate tables. -
ENHANCED: compile a trie directly into its clause compile_trie()
used to compile into a growing temporary buffer that was copied into
the clause, so compiling a large answer trie needed about twice the
memory of the clause. This is significant for incremental tables,
which are always compiled on completion. Now a first pass computes the
exact code size, after which the second pass compiles into a clause
of that size. For a 10M answer incremental table this reduces the
peak memory from 1,651 MB to 1,270 MB without slowing down. -
DOC: cyc150: smaller trie, trie node and IDG node structs
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
ENHANCED: shrink struct trie from 168 to 152 bytes The release
hook pointer is replaced by the flags TRIE_ISTABLE and the new
TRIE_ISVARIANT, which also avoids a call per released answer node
without delay info. The lookup and gen_call statistics are 32-bit. -
ENHANCED: shrink trie nodes from 48 to 40 bytes The delay info of
conditional answers moves from every trie node to a table, flagged
by TN_DELAYINFO. Few answers are conditional, while every node of
every trie paid for the pointer. On ErgoAI’s cyc150 the table space
shrinks by 400 MB. -
ENHANCED: shrink the IDG node from 120 to 80 bytes The fields for
monotonic dependencies move to struct idg_mono, which only nodes
with monotonic dependencies allocate, and the counters are 32-bit.
On ErgoAI’s cyc150 the 9.4M nodes use 376 MB less. -
ENHANCED: private tables get an atom only when Prolog needs a handle
The variant table of a thread now refers to its answer tries directly
rather than through their atom. A trie gets its atom when Prolog
asks for a handle and is destroyed directly if it never got one.
The pseudo tables that record calls to incremental dynamic predicates
never need an atom. On ErgoAI’s cyc150 this avoids 3M of 9.5M atoms
(450 MB) and about 4% of the solve time. -
ENHANCED: the atom of a trie no longer copies a reference to the
trie The trie blob is now PL_BLOB_NOCOPY with the trie as its data.
Creating a trie symbol no longer allocates and symbol_trie() follows
one pointer less. A duplicate shared table is now destroyed by
atom-GC rather than explicitly, as releasing the blob can no longer
happen twice. -
DOC: cyc150: ergoAIp now declares its \opposes stores abstract(0)
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
DOC: cyc150: measure abstract(0) dependencies for ergoAIp’s rule
metadata Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
FIXED: calling a complete table of an SSU predicate tabled after its
rules If table/1 was applied to a => predicate that was already defined,
the S_SSU_DET supervisor runs before S_TABLED. If the fast path of
S_TABLED failed from the complete table, this raised “No rule matches”. -
ENHANCED: abolish all private tables in bulk Clearing a thread’s
variant table (abolish_all_tables/0, thread exit) now resets the
tables in creation order, frees the IDG edge sets of private nodes
without updating the other end and releases the IDG registry at once.
On ErgoAI’s cyc150 this reduces abolishing 13M tables from 29s to
4.4s and frees the registry slots that remained. -
ENHANCED: do not compile incremental tables with a single answer
Incremental tables were always compiled on completion because the
answer trie may change while it is enumerated. Generating a single
answer is deterministic, so S_TABLED and S_TRIE_GEN now unify such an
answer directly from the trie. On ErgoAI’s cyc150 this avoids 2.3M
of 2.4M compiled clauses and their clause-reference atoms (-520 MB RSS). -
DOC: doc/tabling: where ErgoAI’s cyc150 programs spend memory and
time Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
ENHANCED: allocate the IDG from the table space The nodes of the
incremental dependency graph, their edge sets and the registry of
a thread are now allocated from the table pool of their tables.
They are thus included in the statisticstable_space_usedand
shared_table_space_usedand subject to thetable_spaceand
shared_table_spacelimits. Exceeding the limit while adding an
IDG node or edge raises a resource error rather than halting. -
ADDED: dark theme: colours of the thread monitor graphs of incomplete
tables Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01Kakt381LQVidSMrMYqTh8W -
DOC: the SDG viewer and the thread monitor graphs of incomplete tables
Document prolog_ide(sdg_viewer), the graphs of incomplete tables of the
thread monitor and, in doc/tabling, the SDG viewer and the SDG counters. -
FIXED: reevaluation leaked the IDG nodes of incremental dynamic calls
Reevaluating an incremental table drops all its IDG edges and adds only
those of the calls it makes this time. Dynamic call variants it no
longer makes kept their pseudo tables, so a table whose calls change
with the data grows the variant table without bound. The same applies
to tables destroyed on completion and dropped intermediate tables.
Such nodes are now reclaimed by a sweep over the thread’s variant table
after completing a root component, amortized over the removed edges. -
FIXED: exception with two attributed catchers whose wakeup fails
aborted findCatcher() rewound the wakeup-state foreign frame after a
failed catcher, freeing the term reference for the wakeup exception
that was allocated only once, as well as the saved wakeup list.
Each catcher is now tried in a foreign frame of its own that is
discarded on failure. Reported by Shivaji Gadde. -
FIXED: ‘$attv_unify’(X,X) on an attributed variable created a
self-reference Binding the attvar cell to a reference to itself
made the next dereference loop forever. Succeed without binding.
Also compute the new value before fetching the attvar address as
linkValG() may shift the stacks. Reported by Shivaji Gadde. -
FIXED: ‘$current_prolog_flag’/5 crashed enumerating
localscope
without local flags If the thread has no thread-local flag table,
fail rather than create an enumerator over a NULL table. Reported by
Shivaji Gadde. -
FIXED: ‘$source_file_property’/3 crashed asking
loadedfor an
unknown file Theloadedproperty dereferenced the source file without
checking that lookupSourceFile() found one. Reported by Shivaji Gadde. -
ADDED: tripwires max_incomplete_subgoals and max_scc_subgoals The
Prolog flags max_incomplete_subgoals and max_scc_subgoals, with their
_action flags, limit the number of incomplete tables of a thread and
the number of tables in an SCC. ‘$tbl_incomplete’/3 reads the number
of incomplete tables and active SCCs of any thread without stopping it. -
ADDED: ‘$tbl_sdg’/3 describes the subgoal dependency graph of a thread
The incomplete tables of the calling thread with their components
and the positive and negative suspensions between them, the base
for a viewer of “why does this not complete”. Another thread reads
it with call_in_thread/2. save_tabling_status() now links the saved
state into the thread, so the components hidden by break/0 (a tripwire
suspend) and answer completion are found too. -
FIXED: WFS simplification could loop forever on a removed delay
set Removing a delay set from an answer (propagate_to_answer())
leaves its elements in the answer’s delay buffer. answer_delays_on()
counted them, so the answer stayed registered in the delays of a table
it no longer depends on. If that table ended up without answers,
simplify_component() propagated to the answer in every pass without
changing anything and never terminated. answer_delays_on() now only
considers the elements of live delay sets. -
MODIFIED: macOS: system_thread_id is the id of pthread_threadid_np()
It used to be the pthread_t, which macOS reuses for a new thread,
so it did not identify a thread over time. The new id is unique
across the system and is what tools such as lldb and Instruments show. -
TEST: idg_reader: skip the tests that need threads in a single-threaded
build Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01Kakt381LQVidSMrMYqTh8W -
FIXED: single-threaded build: statistics key shared_table_space_used
There are no shared tables without threads, so the key is 0. -
FIXED: clang: avoid setjmp() for each arithmetic evaluation Clang builds
guarded valueExpression() using setjmp(). On macOS this implies two
system calls, slowing down arithmetic that is not compiled (no -O)
by up to 4 times. The guard was added because clang crashed on a
longjmp() into PL_next_solution(), which no longer applies since
setjmp() was moved out of the VM main function. -
FIXED: abolishing tables now reclaims the IDG nodes of incremental
dynamic calls A call from a table to an incremental dynamic
predicate adds a dynamic pseudo table to the variant trie that
records the depending tables. abolish_table() ignored these, so
abolish_module_tables/1 and abolish_table_subgoals/1 never reclaimed
them; only abolish_all_tables/0 did. A process that repeatedly loads
and abolishes KBs kept growing (6.5M nodes, about 5 GB, after one
ErgoAI benchmark). -
FIXED: XSB dialect: add message for ignored table options;
accept if_not_tabled library(dialect/xsb/source) printed
xsb(table_option_ignored(Option)) without a message definition.
if_not_tabledonly skips the declaration if the predicate is already
tabled, so it is accepted silently, likeopaque. -
DOC: prolog_ide(idg_viewer) Co-Authored-By: Claude
Opus 5.5 noreply@anthropic.com Claude-Session:
https://claude.ai/code/session_01Kakt381LQVidSMrMYqTh8W -
DOC: doc/tabling: tools for debugging tabled programs in XSB and
ErgoAI Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
ADDED: ‘$idg_predicate_edges’/3 summarizes the IDG per predicate
Counts the IDG nodes per predicate (and how many are invalid) and
the edges between predicates of any thread, as an external reader.
On Pharos this takes 0.064 seconds for 7.3M edges, where the same in
Prolog took 9.3 seconds. This is the data for an IDG viewer. -
ADDED: reading the incremental dependency graph from another thread
A thread may now inspect the IDG of another thread. Readers bracket
their access with idg_reader_enter() and idg_reader_exit(); while there
are readers, freed IDG nodes, edge-set blocks and answer trie structs
are kept until the last reader leaves. The IDG nodes of a thread
are kept in a registry that a reader finds through acquire_ldata().
‘$idg_thread_summary’/3 counts the nodes and edges of a thread this way. -
ENHANCED: compact edge sets for the incremental dependency graph The
edges of an IDG node used generic lock-free hash tables. Most nodes
have no or one edge, so this was mostly overhead: on Pharos the IDG
used 654 MB. The new idg_set (src/pl-idgset.c) is a single word for
none or one edge and an append-only block, hashed above 8 entries,
otherwise. The sets hold keys only; monotonic dependency chains
moved to idg_node->mono_deps. This reduces the IDG to 403 MB and
the RSS from 1.46 GB to 1.09 GB at the same speed. -
FIXED: table_statistics/2,3 counted IDG nodes of dynamic predicates as
tables The variant table also holds, for incremental dynamic predicates,
the variants incremental tables depend on. These have no answers and
were counted as answer tables, e.g., 387,426 tables with 0 answers.
They are now reported as dependency_nodes and dependency_space. -
FIXED: #1553 GCC musttail check passed on ppc64le and s390x The
configure probe was too simple: GCC resolved its call through the
static table to a direct call. The probe now mirrors the VM: five
arguments, the last modified, and an indirect call through the code.
ppc64le cannot do indirect sibling calls and s390x passes the fifth
argument in the callee-saved r6, so both now use the default VM. -
FIXED: trie_property/2 size: only check counts of our own private
tries The size property compared the counted nodes and values with
the administration and printed an OOPS message if they differ.
Another thread may add to the trie while we walk it, e.g., when
inspecting the tables of another thread, so the check is now limited
to the private tries of the calling thread. -
ENHANCED: dark theme: colour for the thread monitor’s tables graph
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
DOC: prolog_ide(memory_viewer) Co-Authored-By: Claude Opus 5.5
noreply@anthropic.com -
ADDED: ‘$thread_local_predicate_size’/3 for the copy in another
thread The memory used by the copy of a thread-local predicate in
some thread, the counterpart of ‘$thread_local_clause_count’/3.
Used by the memory viewer of the IDE. -
ADDED: erased clauses not yet reclaimed: predicate properties and
statistics predicate_property/2 properties erased_clauses(Count) and
erased_clause_space(Bytes), and statistics/2 keys of the same name,
tell how many erased clauses clause garbage collection did not reclaim
yet and how much memory they use. -
ADDED: statistics/2 key shared_table_space_used The bytes in use by
shared answer tables, the counterpart of table_space_used for the
private tables of a thread. -
ADDED: Prolog flag validate_api to disable the foreign interface
checks The checks on term_t, atom_t and functor_t arguments cost
a few nanoseconds per call. The flag is a bit in the flag mask,
tested inline before calling the check. It is true by default and
false if Prolog is started with -O. -
ADDED: PL_get_list_slice() to call a C function for a slice of a list
The callback receives a reused term reference for each element, its
index and a closure. The list is walked once, detecting cycles using
Brent’s algorithm after 1,000 elements. The callback may cause GC.
With CVT_EXCEPTION, partial, improper and cyclic lists raise an error. -
ADDED: PL_get_arg_values() to read a range of arguments of a compound
Fills an array of pl_value_t, each holding the type and value of an
argument. This avoids a term_t and API call per argument for C code
that reads many small terms. Text is converted following the CVT_,
REP_ and BUF_* flags of PL_get_nchars(). PL_AS_FLOAT (an alias for
CVT_FLOAT) stores all numbers as double.
Package bdb
- CLEANUP: remove unused atoms from bdb4pl.c The server atoms are only
needed for the RPC client of Berkeley DB before 5.1. Reported by
clang’s -Wunused-but-set-global.
Package clib
- CLEANUP: remove unused atoms, functors and variables from time.c,
process.c and socket.c Some are only needed outside Windows or with
AF_UNIX. Reported by clang’s -Wunused-but-set-global.
Package cpp
-
TEST: PL_get_list_slice() Co-Authored-By: Claude Opus 5.5
noreply@anthropic.com -
TEST: PL_get_arg_values() Co-Authored-By: Claude Opus 5.5
noreply@anthropic.com
Package http
- FIXED: #176 http_stop_server/2 could hang if the stop signal arrives in
http:schedule_workers/1 The catch around the hook caught any exception,
including the http_stop() signal used to stop the accept thread.
Only catch error(,_) exceptions. Fixes #176.
Package jpl
- FIXED: JPL turned atoms with characters above U+00FF into garbage
Strings Converting such an atom to a Java String repeated its first
character (“ab€d” became “aaaa”). This affected all systems
where wchar_t is 32 bits, i.e., all but Windows. Converting a Java
String ending in a lone UTF-16 lead surrogate read beyond the string.
Also removes unused atoms and functors and compiles the UTF-16 helpers
only when needed.
Package ltx2htm
- CLEANUP: remove unused ATOM_nbsp from tex.c Reported by clang’s
-Wunused-but-set-global.
Package odbc
- CLEANUP: remove unused atoms and functors from odbc.c Reported by
clang’s -Wunused-but-set-global.
Package pldoc
- FIXED: LaTeX backend: Text links to a section whose id holds
an _ section_label//1 removes underscores from the \label, but the
\secref for a link to it kept them, so the reference was unresolved.
Package semweb
- CLEANUP: remove unused atoms and functor from rdf_db.c and error.c
Reported by clang’s -Wunused-but-set-global.
Package ssl
- CLEANUP: remove unused atoms and functors from ssl4pl.c and
crypto4pl.c ATOM_warning is only used outside Windows and ATOM_sslv23
in crypto4pl.c only with OpenSSL before 3.0. Reported by clang’s
-Wunused-but-set-global.
Package sweep
- PORT: declare emacs_module_init() exported before emacs-module.h
Adding dllexport on a redeclaration makes clang warn for Windows
targets (-Wdll-attribute-on-redeclaration).
Package xpce
-
ENHANCED: the text report of profile/2 names XPCE methods Defines
prolog:profile_node_label/2, so the report shows a method as, e.g.,
frame <-keep_alive. -
FIXED: profiler: Edit of a method; its popup opened the manpce window
The popup of a method in the details opened, or raised, the window of
manpce/0 to find out whether the manual documents it. It now uses
the HTML manual directly, as Documentation does. Edit uses edit/1,
which also finds the C source of a built-in method, and edit/1 no
longer offers the source of class send_method or get_method beside it. -
FIXED: profiler: a method in the flat profile broke sorting by name
profile_data/1 now returns the nodes of XPCE methods, so the profiler
lists them and the details of a method show what it calls. -
FIXED: swipl-win: Control-X 1 in an editor could halt Prolog Control-X 1
silently closed an idle Prolog console beside the editor. In swipl-win
the editor left does not keep the application alive, so Prolog halted.
If closing the other panes ends the application, the panes that keep
it alive are now moved to a new tab instead. -
TEST: Skip pce_date tests that Windows cannot run rather than failing
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
TEST: tool_panes axis labelling test independent of the font height
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
TEST: Skip video tests on Wine’s Media Foundation encoder stub
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
TEST: Skip tests that need threads in single-threaded builds
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
FIXED: xdot: run graphviz without threads in single-threaded builds
Without threads, the graph source and dot’s standard error go through
temporary files instead of a thread that feeds dot’s input and one
that reads its errors. -
ENHANCED: profiler call graph edges have a tooltip with calls and
time The label of an edge only shows the number of calls. The tooltip
also tells the caller, the callee and the time, which sets the width
of the edge. -
ENHANCED: xdot: edge labels on top of a translucent background
Graphviz places an edge label beside the centre line of the edge,
regardless of the width of the line and its arrows, which could then
hide part of the label. Labels are now drawn after the arrows, on
a rounded, translucent box of the background of the graph or window.
The class variable xdot.edge_label_alpha (default 160) sets its opacity;
0 draws no background. -
ENHANCED: profiler status line tells exact profiles from sampled
ones For exact(true) profiles, the status line reports the time in
the predicates and the profiler overhead, which is excluded from the
times, rather than samples and distortion. Sampled profiles show
whether CPU or wall time was sampled. -
FIXED: PceEmacs: edit/1 from a window holding only a tool failed When
the arrangements put the new editor elsewhere, e.g., in a window of
its own,emacs_buffer<-openanswered the current pane of the window
it was asked from, typically the navigator, raising “No implementation
for: <-editor”.@emacs<-show_buffernow answers the view it shows
the buffer in. -
TEST: memory viewer: the largest tables may share their size
top_variants assumed that only the tables for 2400 and 2500 answers
use the same space. With smaller trie nodes, more tables share the
largest size. -
FIXED: IDG viewer, profiler, class variable editor: no lambda
expressions The lambdas of the IDG viewer used variables of the clause
without declaring them as {Free}. When compiled, these became fresh
variables, so the patterns of predicates outside the focus graph and
the groups expanded for the previous predicate were not filtered.
Uncompiled lambdas are slow, so all are replaced by helper predicates. -
FIXED: IDG viewer: showing a huge table or predicate exceeded the
stack The worker collected all variants of the predicate or call
pattern and all answers of the selected variant before showing the
first page. Engines now produce the pages of matching variants and
answers using findnsols/4 with count(N), so the “Next 10/100/1000”
buttons get the next N. -
ENHANCED: IDG viewer: show tabled dynamic predicates as tables in
italics A predicate that is both tabled and dynamic, as used by ErgoAI,
was shown as a dynamic predicate (a box). It is now an ellipse, as
other tables, with its name in italics to show it is also dynamic.
The browser keeps showing it in italics and the tooltip says
“incremental table, dynamic”. -
ENHANCED: memory viewer: show the call trie and IDG; report slow
refreshes The tables of a pool now have acall trienode, the
variant table that maps calls to their tables, and anIDGnode,
the incremental dependency graph. While computing the chart takes
more than a quarter of a second, the chart shows the progress cursor
and the status line tells what is being described. -
FIXED: memory viewer: a thread with millions of tables vanished from the
chart Describing the private tables of a thread collected every table
with its variant before grouping them by predicate. With 12.9M tables
this exceeded the stack of the thread computing the chart, and the
error made the whole thread disappear from the threads and tables views. -
ADDED: thread monitor: graphs of the incomplete tables and SCCs per
table The diagram of a thread shows the number of its incomplete tables
and its active SCCs per incomplete table, read with ‘$tbl_incomplete’/3
without stopping the thread. Few SCCs per table hint at badly
structured rules, many and growing at recursion over growing terms. -
ADDED: library(swi/pce_sdg_viewer): show the incomplete tables
of a thread The class prolog_sdg_viewer is a pane of the IDE that
shows the subgoal dependency graph of a thread from ‘$tbl_sdg’/3:
the incomplete tables in their components (SCCs), nested as the
components are, and which tables wait for which. Negative (tnot/1)
edges are red and dashed, tables grow with their answers, leaders have
a double outline. Large graphs are shown per predicate; clicking a
predicate shows its tables. -
TEST: memory viewer: thread_tables_cached removes the cache entry
it fakes Thread ids and, on macOS, system thread ids are reused,
so the next test’s thread found the faked description. -
FIXED: rings chart: child labels raised a type error with a fractional
font height Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
FIXED: @display<-animation_save_formats crashed on a format it cannot
save IMG_CreateAnimationEncoder_IO() with closeio true closes the
stream when it fails, after which we closed it again. -
FIXED: dependency viewer: the count column fits the largest count
The column of node counts in the browser was as wide as 000,000,
so counts of a million nodes or more did not fit. -
ENHANCED: dependency viewer: resizing it resizes the graph, not the
browser The column of the predicate browser neither stretches nor
shrinks, so the graph takes a resize of the viewer. It relied on
the freeze that setting the width of a tile causes, which does not
hold before the predicates arrive. -
FIXED: the outline of a drop on a tool with nested windows showed
on its tabs A drop on a tool whose window holds windows itself,
such as the views of the dependency viewer, only outlined the label
row of those views, which looked like a drop that adds a tab there.
The outline now goes on the windows all the way down. -
FIXED: xdot_window ->fit of an empty graph divided by zero
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com Claude-Session:
https://claude.ai/code/session_01Kakt381LQVidSMrMYqTh8W -
ENHANCED: IDE: breakpoints, exceptions and debug messages moved to
a Debug menu Every window of the IDE has a Debug menu holding Edit
breakpoints, Edit exceptions, Debug messages and a Tabling submenu with
View incremental dependencies (IDG), which was View dependencies on the
Tools menu. In a terminal they follow the terminal’s own Debug items. -
ENHANCED: xdot_window refits the graph when resized Unless the user
moved or zoomed it, a resized xdot_window fits the graph again,
up to its natural_zoom, as the call graph of the profiler did.
The dependency viewer now gets this as well. -
ADDED: IDE dependency viewer: the incremental dependency graph of
a thread Tools/View dependencies shows the IDG of a thread using
library(xdot): which incremental dynamic predicates and tables affect
which tables. As the profiler, it has a browser of the predicates
involved with a filter and shows the graph around the current predicate,
marked with a star: what affects it on the left and what it affects
on the right. Relatives that are not shown are summarised by “… N
more” nodes, whose menu shows them all. Clicking a predicate shows its
call patterns as a cluster. An overview shows the IDG by module and,
for the non-modular code ofuser, by file, where expanded groups are
graphviz clusters; it follows the current predicate. A pane browses
the variants of a predicate or call pattern and their answers, paged
and filtered. -
FIXED: memory viewer: IDG nodes of dynamic predicates are not tables
The variants of an incremental dynamic predicate in the variant table
are nodes of the dependency graph. They are now described as such
rather than as tables with 0 answers. -
ADDED: ->tooltip and <-tooltip as shorthand for help_message:
tag Defined on visual and dict_item by library(help_message).
Tooltips may be markup, which the balloon renders. -
ENHANCED: thread monitor: tables graph, opens on the busiest thread A
graph shows the private tables of a thread once it has them. A new
monitor shows the first console thread and switches to the running
thread using most CPU. A low window labels fewer decades and the
graph window is a plain window, so it has no scrollbars. -
FIXED: plot_axis: grid lines follow a change of the other axis’s
range The dotted support lines of an axis run across the pixel range
of the other axis, but were only redrawn when their own axis changed.
Making the Y axis of the thread monitor shorter left the X grid lines
at their old height, so the diagram was centred too low in a low window. -
ADDED: IDE memory viewer: how Prolog uses its memory as rings or a
treemap Tools/View memory opens a pane with views on the process,
the application (static program and dynamic data, withusersplit
by file), the program per module and predicate (indexes, clauses and
erased clauses), the dynamic data, the threads (stacks, thread-local
data and private tables), the tables per thread and predicate, and the
objects of xpce per class. The tree is computed in a thread of its
own, optionally refreshed every so many seconds; the popup edits what
is below the pointer. memory_tree/2,3 provide the data without the GUI. -
ENHANCED: disk usage demo: popup to open, scan a folder or its parent
The desktop’s folder dialog selects the folder you came from when
you go up, so it cannot easily be used to scan the parent. -
ADDED: profiler: Call tree tab shows where the time below a predicate
goes The call tree of the current predicate is shown as rings or a
treemap using library(plot/hierarchy_chart). It is built from the call
graph by expanding the callees, sharing the time below a callee over its
own callees in proportion; recursion and small calls are not expanded.
Clicking makes a predicate the current one; the popup edits it. -
ADDED: demo Disk usage: rings and treemap of the disk space of a
folder Scans a folder in a thread of its own and shows the result
using library(plot/hierarchy_chart), in the style of GNOME’s baobab.
It starts in the working directory and skips symbolic links, also
those that loop. -
ADDED: library(open_document): open a file in the editor or the desktop
Epilog’s opening of file links, moved to a library of its own so that
other tools can use it: folders and binary files are opened using
desktop_open/1, text files using edit/1. -
ADDED: library(plot/hierarchy_chart) shows a hierarchical distribution
Shows how a quantity such as disk space, time or memory is distributed
over a tree of hierarchy_node objects, as rings around the focus or
as a squarified treemap, in the style of GNOME’s baobab. Hovering a
node highlights it and shows its value; clicking zooms in or out.
Class hierarchy_window shows a chart filling a picture and switches
views. -
FIXED: help balloon swallowed pointer moves over its owner While a
balloon showed, moving the pointer over its owner updated the balloon
but the move never reached the owner, so a graphical that highlights
what is below the pointer stopped doing so. The move is now processed
as usual after updating the balloon. -
ADDED: path<->event_area selects how a path is hit by the mouse
paintedaccepts events exactly where the path is painted, so filled
shapes that touch, such as the segments of a chart, no longer accept
events just outside their edge.areaandoutlinehit only the
interior or only the line. The default,auto, now also hits the
interior of a filled path that is not closed. -
ADDED: plotter ->hover_values shows the values of the graphs below the
pointer When enabled, hovering a plotter shows a vertical line at the
pointer and a box in the colour of each graph holding its value there.
The thread monitor uses this to show the exact stack sizes and CPU
usage of a sample. -
ADDED: demo Ripples: stir the water of a ripple tank A wave equation
in Prolog moves a grid of heights. An sdl_mesh shows the floor of
the pool refracted by the slope of the water, and a second mesh adds
glints using additive blending. Drag the mouse to stir, click to
drop a stone that stays on the floor, toggle rain, run at maximum
speed and clear the floor. Using a pen, the pressure sets the depth
of the waves. Resizing the window enlarges the pool; the number of
triangles grows with its area. -
ENHANCED: pce_sdl_mesh_set/3 accepts v(X,Y,Colour) for untextured
vertices Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
FIXED: converting a cyclic or very long Prolog list to an xpce
object crashed The conversion collected the elements in an alloca()
array that grew without limit, overflowing the C stack. It now uses
PL_get_list_slice(), which detects cycles, and moves to malloc()ed
memory for long lists. Invalid lists now raise type_error(list, L)
or an instantiation error rather than type_error(pce(proper_list), L). -
ENHANCED: pce_sdl_mesh_set/3 is 3 times faster and accepts integer
colours The vertex colour may be an integer 0xRRGGBBAA as well as
rgba(R,G,B,A). Vertices are read using PL_get_arg_values() and both
lists are walked once using PL_get_list_slice(). A partial list now
raises an instantiation error. The particles demo uses integer colours.
pce_sdl_mesh_set/3 and pce_open_terminal_image/4 are now documented
for help/1. -
FIXED: video and camera frames in BT.601 could not be converted on
SDL < 3.4 SDL before 3.4 only converts YUV to RGB if both use the
same primaries, so frames in BT.601 could not become sRGB images.
Keep the primaries of the frame for the conversion. -
TEST: test_theme_editor: class_variable_link needs the manual
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
TEST: test_sdl_input: pinch events require SDL 3.4 Co-Authored-By:
Claude Opus 5.5 noreply@anthropic.com -
FIXED: sdl_mesh without texture ignored <-blend on SDL 3.2 SDL 3.2
draws two triangles that form a rectangle as a filled rectangle with
alpha blending if the texture coordinates have no size, as for the
white pixel we use if the layer has no texture. -
TEST: test_image_save: SDL_image before 3.4 cannot write GIF, TGA
and ICO Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com -
ADDED: @display<-animation_save_formats Formats animation->save
can write. It is empty with SDL_image before 3.4, which cannot
write animations. test_animation uses it to skip tests that save
animations or load APNG on such systems. -
FIXED: date->set (and new(date(…))) was off by an hour across DST
setDate() started from the localtime() of the old date and kept its
tm_isdst, so mktime() applied the DST state of the old date (typically
now) to the new one. Let mktime() determine DST. Reported by Mike
Elston (test_pce_date failed in New Zealand). -
ENHANCED: terminal_image ->save_lines resizes the scroll-back buffer
Previously the buffer size was fixed when the terminal was created,
so changing the preference only affected new terminals. Growing keeps
all text, shrinking drops the oldest lines. The caret, selection,
view, incremental search and command blocks follow their text. -
FIXED: terminal output was slow:
catof a large file took 25 sec,
now 0.4 Each pty event processed at most 4K of output, after which the
terminal was redrawn. Now process all buffered output, for at most
20ms. The per-character consistency check walked the whole scroll-back
(release builds keep assert()); it now only does so in debug builds. -
ADDED: terminal_image <->auto_links: turn text matching a regex
into links terminal_image->auto_link: Regex, [Target] makes text that
matches Regex a hyperlink to Target, where \N is replaced by what group
N matched. Epilog uses this to link http(s) URLs and File:Line[:Column]
locations; add patterns using the multifile hook epilog:auto_link/2. -
TEST: rename xpce tests with “fail” in their name Such names get in the
way when searching test output for failed tests. xpce:failed_initialise
becomes xpce:initialise_free. -
TEST: sdl_layer tests: allow rounding differences between renderer
and Cairo Metal draws a blue YUV frame at 0.5 alpha as 127 where
Cairo draws 128, failing sdl_layer:alpha_no_alpha_channel on macOS. -
TEST: run the video test units in parallel The video tests wait for
playback in real time, 40 seconds in one process. Each unit now
runs as a ctest of its own, the stalling HTTP handler is released
when the server stops rather than sleeping 5 seconds, and the input
format tests play shorter clips: 7.5 seconds. With INSTALL_TESTS,
test_video/0 is still recorded for the installed tree; its ctest is
disabled as the units run separately. -
ENHANCED: epilog rounds the terminal width to whole pixels The
scroll_bar width need not be a whole number of pixels, so the terminal,
scroll_bar and report bar are placed using round(TW-SBW). -
ADDED: xpce expressions round(X), floor(X) and X//Y xpce numbers need
not be integers, so X/Y is 3.5 for 7/2. These make an integer where
one is needed, with the semantics of Prolog’s round/1, floor/1 and //.
Integer division by zero raises division_by_zero. -
FIXED: scroll_bar width of 4mm made the terminal one column too
narrow The default width of a scroll_bar is 4mm, which is not a whole
number of pixels. Now that expressions yield non-integral numbers,
epilog gave the terminal a fractional width just short of 80 columns.
The width is now rounded to whole pixels. -
FIXED: class date on Windows: 32-bit times, crashes before 1970, no zone
offset Dates, date->convert, date->advance and date<-difference used
C long, which is 32 bits on Windows, so times past 2038 and intervals
beyond 68 years were wrong. date->advance tested for overflow after
it happened. Getters crashed on dates the C library cannot represent,
on Windows any date before 1970; they now fail. Windows has no
tm_gmtoff, so date<-time_zone gave no offset and date<-rfc_string had
no zone; the offset is now computed with timegm(), which is _mkgmtime()
on Windows. The fallback timegm() used local time if TZ was not set,
so date<-convert of XML dates was off by the local offset on Windows. -
FIXED: xpce integer add and subtract detected overflow after the
fact The checks inspected the result of a signed overflow, which is
undefined behaviour that compilers may optimise away. They now test
the operands before computing. -
FIXED: xpce integer arithmetic was 32-bit on Windows and crashed
on x/0 Expressions and number objects used C long, which is 32 bits
on Windows, truncating integers above 2^31. They now use intptr_t.
Multiplication falls back to floats when the product does not fit an
xpce integer rather than checking the operands with labs(), and an
integer division by zero no longer raises SIGFPE but yields inf. -
FIXED: compile xpce on Windows with clang (llvm-mingw, Windows on
ARM) Clang accepts the visibility attribute for MinGW targets, so
SO_LOCAL made _rewindAnswerStack() hidden while interface.h declares
it dllexport, which clang rejects. SO_LOCAL is now empty on Windows,
where DLLs only export what is declared dllexport. -
FIXED: compile with SDL 3.2 and SDL_image 3.2 (Debian trixie) With
SDL_image before 3.4, animations load through IMG_LoadAnimation_IO()
without metadata, saving animations raises an error and image->save
writes PNG, JPEG, AVIF and BMP only. With SDL before 3.4, an animated
cursor shows its first frame.