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. >
