[usocket-devel] SBCL/Win32: finalizer problem, etc.

Chun Tian (binghe) binghe.lisp at gmail.com
Tue Mar 22 14:35:47 UTC 2011


Sorry, these code is NOT written by me, it's by Erik Huelsmann.  I just confirmed them when I was adding UDP support to WAIT-FOR-INPUT-INTERNAL.

--binghe


> Hi, Anton
> 
> Thank you very much for your patient explanation, now I completely understand this:)
> 
> The USOCKET SBCL/win32 supporting code is port from LispWorks/win32 supporting code, this is how LispWorks do the same work:
> 
>  (defun free-wait-list (wl)
>    (when (wait-list-p wl)
>      (unless (null (wait-list-%wait wl))
>        (wsa-event-close (wait-list-%wait wl)))))
> 
>  (eval-when (:load-toplevel :execute)
>    (hcl:add-special-free-action 'free-wait-list))
> 
>  (defun %setup-wait-list (wait-list)
>    (hcl:flag-special-free-action wait-list)
>    (setf (wait-list-%wait wait-list) (wsa-event-create)))
> 
> I confirmed above code could really work when I first wrote them. As you can see, LW allow me using HCL:ADD-SPECIAL-FREE-ACTION to register a general handler function for cleaning every needed object. The handler function FREE-WAIT-LIST have to check its argument type first, and then do its cleaning job, because other type of object could come in.  And in %SETUP-WAIT-LIST, I need to call HCL:FLAG-SPECIAL-FREE-ACTION to mark the wait-list object to be "cleanable".  No closure needed here, but seems if I have many different action functions, things could do a bit slower than the SBCL way.
> 
> Any way, just want to share this.  No other issue.
> 
> Regards,
> 
> Chun Tian (binghe)
> 
> 在 2011-3-22,14:38, Anton Kovalenko 写道:
> 
>> "Chun Tian (binghe)" <binghe.lisp at gmail.com> writes:
>> 
>>> I think I cannot understand why the closure must close over those
>>> components of WAIT-LIST but WAIT-LIST itself, but since this is a fact
>>> (as you confirmed), I'd like to adopt your patches and quote all your
>>> explanations and put with the new code together.  I hope, with your
>>> SBCL and USOCKET work, Hunchentoot (and other Lisp-based servers)
>>> could have a beautiful future on Windows platform.
>> 
>> Thank you!
>> 
>> The problem with a closure and a finalizer is not too complicated (i'm
>> now trying to rephrase my explanations to be easy to understand):
>> 
>> A queue of finalizers (sb-impl::**finalizer-store** in SBCL, but other
>> CL implementanions usually have some equivalent) is just a mundane
>> special variable (containing a list in SBCL case). Special variable
>> values (if we forget thread-local bindings and uninterned symbols) are
>> always reachable in Common Lisp: it's easy, for example, to write a loop
>> that iterates over packages and symbols and examines each SYMBOL-VALUE.
>> 
>> GC _doesn't collect reachable objects_. If there is a closure with an
>> object `inside', it's considered reachable too. And if an object isn't
>> collected, it's not the time (from GC's point of view) to run its
>> finalizers, so they are not run.
>> 
>> 
>> Anyone who wants to build a GC with other behavior will be confronted by
>> a very complicated picture: instead of two kinds of objects ("reachable"
>> and "unreachable" (from some roots)) we must consider _which code_ can
>> reach the object and which code cannot. If we introduce a single
>> "magical" exception into the simple dichotomy of live and dead object
>> (like finalizer queue -- if it were a "zombie place" of this kind),
>> complicated GC implementation will be only a half of our problems;
>> questions like "what if one finalizer refers to the object of another
>> one?"  will soon make us confused -- not only about how to implement the
>> GC, but about _what_ we should implement.
>> 
>> SBCL is not alone in taking the simple way; and, when we decide that
>> finalizers (that are notified about dead objects) shouldn't see those
>> objects, a beautiful thing happens: _weak pointers and finalizers_
>> become logically equivalent and mutually interchangeable.
>> 
>> In particular, SBCL's finalizer queue is just an alist of weak pointers
>> to objects in CAR and closures in CDR. After each "low-level" GC
>> invokation, SBCL looks for those weak pointers in **finalizer-store**
>> that became dead, and invokes the closures. 
>> 
>> [Just as a curiosity, it's possible to go in the opposite direction: to
>> implement weak pointers when the finalizers are "given". Never seen it
>> in real life, however: weak pointer support is likely a natural
>> byproduct of low-level GC work, and finalizer support likely isn't].
>> 
>> If not weak pointers, weak hash tables provide the base for finalizer
>> support in the same way. [Having weak hash table support _inside_ is
>> almost a must for any Common Lisp implementation -- or else each
>> interned and uninterned symbol increases memory consumption
>> irreversibly].
>> 
>> Almost any programmer now has some experience with C++ destructors, so
>> it's especially important not to misapply that experience to
>> finalizers. The common trait of finalizers that is described above is
>> _opposed_ to the very definition and purpose of destructors.
>> 
>> Destructors are like some evil creditor, speaking continuously "I hope
>> you won't die in debt". Finalizers are like some relative notifying you
>> of the burial of other relative. Unsure which picture is more sad, but
>> the latter is more natural and less evil.
>> 
>> -- 
>> Regards, Anton Kovalenko
>> +7(916)345-34-02 | Elektrostal' MO, Russia
> 





More information about the usocket-devel mailing list