Ann: SWI-Prolog 10.1.20

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 flag profile_exact
    (default auto) 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_subgoals and max_scc_subgoals and
    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_api to 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, the system_thread_id is 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, ->tooltip and library(open_document).
  • Epilog: terminal output is much faster (cat of 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 :slight_smile:

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 (default auto) provides the default
    for the exact option of profile/2. With auto, 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 keys clock and exact.

  • 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 statistics table_space_used and
    shared_table_space_used and subject to the table_space and
    shared_table_space limits. 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 local scope
    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 loaded for an
    unknown file The loaded property 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_tabled only skips the declaration if the predicate is already
    tabled, so it is accepted silently, like opaque.

  • 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

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<-open answered the current pane of the window
    it was asked from, typically the navigator, raising “No implementation
    for: <-editor”. @emacs<-show_buffer now 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 a call trie node, the
    variant table that maps calls to their tables, and an IDG node,
    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 of user, 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, with user split
    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
    painted accepts 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. area and outline hit 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: cat of 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.

1 Like