Subject: bug#78917: feature/igc [PATCH] Avoid chaining finalizers together

From: Pip Cet <[email-masked]>
Date: Sat, 28 Jun 2025 20:52:58 +0000

"Helmut Eller" <[email-masked]> writes:

> On Sat, Jun 28 2025, Pip Cet wrote:
>
>> "Helmut Eller" <[email-masked]> writes:
>>
>>> Lisp_Finalizers are currently chained together in a doubly linked list.
>>> This prevents them from being collected.  I propose that we simply don't
>>> use this list with MPS.
>>>
>>> The first patch is minimalistic: it only skips finalizer_insert when
>>> creating finalizers.
>>>
>>> The second patch is more thorough: it #ifdefs away the prev/next fields
>>> entirely.
>>
>> Looks good, except for the addition of igc--process-messages;
>> igc--collect should process *all* messages before returning,
>
> Maybe.  igc-collect (the one from igc.el) could easily call
> igc--process-messages, though.

That would work as well, if igc--process-messages processed all
messages (or returned to the caller some indication that it failed to do
so).

>> exposing a function that processes only some of the messages doesn't
>> seem useful to me.
>
> It's useful to test finalizes.  It would be more useful to have an API
> to test finalizers that works for both MPS and non-MPS configurations.

As you can't reliably test finalizers, it's not a huge problem if trying
to do so anyway requires some more code.

> but I guess that a plain
>
>   (garbage-collect)
>
> is what most people would prefer.

Hmm.  I think (garbage-collect) should become a nop in MPS builds.

>> The test probably should be rewritten to something like this:
>>
>> ;; Ensure finalizers are collected and bug#78917 doesn't reappear.
>> (ert-deftest test-two-finalizers ()
>>   ;; this test will usually succeed, but might fail due to unpredictable
>>   ;; references to the finalizer on the C stack or elsewhere.
>>   :tags '(:igc :unstable)
>>   (let* ((garbage-collection-messages t)
>>          (finalizer-1-called nil)
>>          (finalizer-2-called nil)
>>          (finalizer-1 (make-finalizer (lambda (setq finalizer-1-called t))))
>>          (finalizer-2 (make-finalizer (lambda (setq finalizer-2-called t)))))
>>     (dotimes (_ 3)
>>       (igc--collect)
>>       (sleep-for 0.1))
>>     (should (not finalizer-1-called))
>>     (should (not finalizer-2-called))
>>     (setq finalizer-1 nil)
>>     (dotimes (_ 1)
>>       (igc--collect)
>>       (accept-process-output nil 1 0 t))
>>     (should finalizer-1-called)
>>     (should (not finalizer-2-called))))
>
> Have you tried it?  This doesn't work for me.

I modified igc--collect as described above.

Pip