Am Sa., 14. Okt. 2023 um 23:14 Uhr schrieb John Cowan <[email protected]>:

>
>
> On Sat, Oct 14, 2023 at 3:05 PM Marc Feeley (via srfi-246 list) <
> [email protected]> wrote:
>
>
[...]


> For simplicity lets just say a guardian is a port.  Then a thread could be
>> given the responsability of reading the next object from the guardian, in a
>> loop, to process the required finalization of these objects.  Essentially:
>>
>>   (define g (make-guardian))
>>
>>   (thread-start! ;; start a “finalization” thread
>>    (make-thread
>>     (lambda ()
>>       (let loop ()
>>         (let ((obj (read g))) ;; get next not strongly reachable object
>> from guardian (block if there is none)
>>           (finalize! obj)     ;; finalize it
>>           (loop))))))
>>
>> The important point here is that the garbage collector uses the guardian
>> as a mechanism to notify the finalization thread, that their operation is
>> asynchronous, and that polling is no longer needed.
>>
>> To me this is a cleaner API because it corresponds with the reality that
>> the GC and main programs are separate threads (indeed this is consistent
>> with the “collector process” and “mutator process” vocabulary used when
>> talking about garbage collection algorithms).
>>
>
> They aren't always, nor is it always necessary to finalize so eagerly.
> The Abstract Example is a way to use guardians that doesn't depend on the
> GC, provided you can accept relaxed finalization.
>

Roughly spoken, a garbage collector runs when the used memory is above a
certain threshold.  A guardian represents not memory but a different
resource, for example, open ports.  It is unnecessary to close every unused
port immediately, only when too many ports are in use.  This can only
happen when a new port is opened.  At that point, the guardian is tested.


> As pointed out in SRFI 124, there is *never* any guarantee that GC runs at
> all.
>

SRFI 124 is wrong here; by Scheme's proper tail call guarantee, defined in
"Will Clinger's Proper Tail Recursion and Space Efficiency," no longer used
locations must be garbage collected (up to some "O(1)").


>
>
>> A variation on this API would be to add an operation on the guardian that
>> blocks the calling thread until there is at least one available not
>> strongly reachable object for that guardian.  Unfortunately the proposed
>> representation of guardians as procedures does not make it easy or elegant
>> to do this.  For this reason guardians should be their own type such that
>> (make-guardian) returns this type and the operations on a guardian are done
>> with specific procedures:
>>
>> (guardian-register guardian obj [rep])  ;; equivalent to the proposed
>> (guardian obj [rep]) operation
>> (guardian-unregister guardian)          ;; equivalent to the proposed
>> (unregister-guardian guardian)
>> (guardian-next guardian)                ;; equivalent to the proposed
>> (guardian) operation
>> (guardian-wait guardian)                ;; new operation that blocks the
>> calling thread until there is at least one not strongly reachable object
>>
>
> I'm not sure why Chez does not do it this way: it seems intuitively
> obvious.  I just sent Dybvig an email about it.  Note however that subtypes
> of procedure can support an external API.
>
>>
>> This makes it easier to add operations if needed in the future.
>>
>
I agree.

The operation `guardian-wait' would belong to a different SRFI, though.


>
>> This set of operations allows a program to use a polling approach if
>> that’s appropriate (for example low number of guardians), or one based on
>> notification (that blocks the finalization thread when it has nothing to
>> do).
>>
>
> Adding guardian-wait makes sense to me rather than bringing in the whole
> Chez events API.
>

Reply via email to