Re: A fix for SBCL error "thread local storage exhausted" when creating too many rules

David Scherfgen via Maxima-discuss <[email protected]> Sun, 7 Jun 2026 06:02:09 +0200
Newsgroups gmane.comp.mathematics.maxima.general
Message-ID <CAMTHLKjgRrG=OWwgt7ZUC4N9MT+MWQftQ28Cvqj2EVFU_PGafQ@mail.gmail.com>
Thanks, Richard!

The TWA garbage collection actually still exists under that name in some
modern Lisps, for example CCL (
https://ccl.clozure.com/manual/chapter17.4.html).

The problem with SBCL is not that those symbols themselves don't get
garbage-collected. Each time a dynamic variable gets dynamically bound for
the first time, SBCL assigns to it a fresh slot in the limited thread-local
storage (TLS). It does so using an internal counter that points to the next
free TLS index, rather than a freelist, and that counter only increases.
When it hits the limit, "the party is over" (as the error message says).
There's no mechanism to reclaim TLS slots.

The variables in the generated matcher programs were probably all declared
special when Maxima was ported from Maclisp, which had dynamic variables by
default.

I take your "harmless" as a green light - I'll go ahead and commit the
patch, so that the party never ends.

Best regards
David

Richard Fateman <[email protected]> schrieb am Sa., 6. Juni 2026, 23:58:

> Maclisp garbage collection used to collect TWAs  ... truly worthless
> atoms. These are symbols with
> no binding -- just a print name.  Maybe that would cure the problem? Maybe
> other lisps can do this?
> Anyway your fix seems harmless.
> RJF
>
> On Fri, Jun 5, 2026 at 6:58 AM David Scherfgen via Maxima-discuss <
> [email protected]> wrote:
>
>> Hi everyone,
>>
>> I've been looking into the annoying bug #4517
>> <https://sourceforge.net/p/maxima/bugs/4517/>, where Maxima crashes on
>> SBCL due to Thread-Local Storage (TLS) exhaustion after repeatedly
>> generating and destroying rules (via defrule, tellsimp, etc.). It
>> famously can be triggered by running the rtest_rules test a few times.
>>
>> The root cause is that the pattern-matching compiler dynamically binds
>> new symbols generated during rule creation. On SBCL, every newly
>> dynamically bound special variable permanently consumes a TLS slot. Even if
>> the rule is subsequently removed, SBCL cannot free the underlying TLS
>> index, eventually causing a hard crash once the limit is reached.
>>
>> To resolve this, I've implemented a reusable symbol pool (
>> *rule-symbol-pool*). The patch modifies matcom.lisp to track all symbols
>> generated for a specific rule via its property list. When a rule is
>> overwritten, or when it is destroyed via kill1-atom in suprv1.lisp,
>> those symbols are completely scrubbed (makunbound, fmakunbound, and
>> property list cleared) and pushed back into the pool for the next rule to
>> recycle. Now, the TLS consumption strictly depends on the rules that exist
>> at the same time, rather than all rules ever created over the lifetime of
>> the session.
>>
>> I'd love to get some feedback on this approach, and I have a specific
>> question for the veterans regarding the historical design: In my patch, I
>> replaced the tr-gensym call in genref with the new pool allocator (
>> get-rule-symbol). Does anyone recall the original rationale for using
>> tr-gensym (which interns the symbol) in genref instead of a standard
>> uninterned gensym, as it is the case in other places in the relevant
>> code? I want to ensure I'm not breaking a subtle dependency with the
>> translator.
>>
>> Note: A visible side effect of this change is that these match variables
>> will now be named RULE-SYMBOL-... rather than TR-GENSYM~... in
>> macro-expanded code.
>>
>> Best regards
>> David Scherfgen
>> _______________________________________________
>> Maxima-discuss mailing list
>> [email protected]
>> https://lists.sourceforge.net/lists/listinfo/maxima-discuss
>>
>

_______________________________________________
Maxima-discuss mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/maxima-discuss