[racket-dev] Testing whether a procedure gets collected

From: Robby Findler (robby at eecs.northwestern.edu)
Date: Sat Dec 1 21:23:41 EST 2012

I think the high-level answer is that you have to understand something
about details that aren't currently specified but nevertheless are how
things currently work and then make a test that will work when you
make those additional assumptions (and then keep it running in drdr so
you can tell when the assumptions get broken).

In this case, I think that a weak box is going to work well to test
the code you described at the beginning of this thread. That is,
create the array; use some internal thing to extract the procedure
from it. Put it into a weak-box; change the state; collect garbage,
check the box. I think that won't fail unless you let that procedure
be reachable from a finalizer or you somehow get it promoted to the
master area (between places) .. or there is a bug. :)

Robby

On Sat, Dec 1, 2012 at 8:05 PM, Neil Toronto <neil.toronto at gmail.com> wrote:
> Ah. It prints #f for me when I have debugging info turned on in DrRacket;
> otherwise I get #<procedure>. Must be inlining keeping it around or
> something.
>
> The problem with either finalizers or weak boxes is that neither provides
> enough guarantees. Finalizers are never guaranteed to be run. A weak box may
> not be the only reference to a procedure value, depending on what
> optimizations are done. I'm trying to test something that's normally not
> supposed to be observable.
>
> For now, I've got a finalizer on an array procedure that's created at the
> beginning of the 1000-line test module, and I test that the finalizer was
> run at the end. I've also got a (collect-garbage) and a (sleep 0) in there.
> It works... for now, on my machine.
>
> Maybe a weak box containing a random closure would work. Hmm...
>
>
> On 12/01/2012 06:46 PM, Robby Findler wrote:
>>
>> This prints #f for me.
>>
>> #lang racket
>>
>> (define (make-box-thing v)
>>    (make-weak-box (λ (_) v)))
>>
>> (define bx (make-box-thing 4))
>> (collect-garbage)
>> (weak-box-value bx)
>>
>> And I guess that non-closure procedures are held onto by the modules
>> they are inside. This program prints #f for me, and it seems to
>> confirm that hypothesis.
>>
>> #lang racket
>>
>> (define bx
>>    (parameterize ([current-namespace (make-base-namespace)])
>>      (eval '(module m racket
>>               (define bx (make-weak-box (λ (_) 1)))
>>               (provide bx)))
>>      (eval '(require 'm))
>>      (eval 'bx)))
>>
>> (collect-garbage)
>> (weak-box-value bx)
>>
>> Robby
>>
>> On Sat, Dec 1, 2012 at 7:30 PM, Neil Toronto <neil.toronto at gmail.com>
>> wrote:
>>>
>>> Honestly, because I was too rushed to try them before I had to leave this
>>> morning. :D However, now that I have the chance, I've found that Typed
>>> Racket doesn't support them. I can't add support using `required/typed',
>>> because `Weak-Box' would have to be a polymorphic type.
>>>
>>> Also, they don't seem to let go of procedure values. This one's value
>>> doesn't ever turn to #f no matter how many times I collect garbage:
>>>
>>>    (define bx (make-weak-box (λ (_) 0)))
>>>
>>> Thinking it might be because that lambda doesn't create a closure, I
>>> tried
>>> this:
>>>
>>>    (define (make-box-thing v)
>>>      (make-weak-box (λ (_) v)))
>>>
>>>    (define bx (make-box-thing 4))
>>>
>>> But this `bx' doesn't let go of its value, either. I can't help but think
>>> I'm missing something really stupid, though.
>>>
>>> Neil ⊥
>>>
>>>
>>> On 12/01/2012 10:58 AM, Robby Findler wrote:
>>>>
>>>>
>>>> How about using a weak box instead?
>>>>
>>>> Robby
>>>>
>>>> On Sat, Dec 1, 2012 at 11:45 AM, Neil Toronto <neil.toronto at gmail.com>
>>>> wrote:
>>>>>
>>>>>
>>>>> I'm getting ready to push a change to math/array that fixes a memory
>>>>> leak.
>>>>> I've devised a test that I think will determine whether an array's
>>>>> procedure
>>>>> gets collected after the array is made strict, but I don't know whether
>>>>> it
>>>>> works only by accident. Here it is:
>>>>>
>>>>>
>>>>> (define: collected? : (Boxof Boolean)  (box #f))
>>>>>
>>>>> (define arr
>>>>>     (let ([proc  (λ: ([js : Indexes]) 0)])  ; constant array
>>>>>       (register-finalizer proc (λ (proc) (set-box! collected? #t)))
>>>>>       (build-array #() proc)))
>>>>>
>>>>> (array-strict! arr)
>>>>> (collect-garbage)
>>>>> (sleep 0)  ; give finalizers a chance to run?
>>>>> (check-true (unbox collected?))
>>>>>
>>>>>
>>>>> This test passes for me now, but will fail if anyone else tries it.
>>>>> What
>>>>> worries me is that (sleep 0) is apparently required, meaning that
>>>>> finalizers
>>>>> aren't run immediately when garbage is collected.
>>>>>
>>>>> How can I ensure that the finalizer for `proc' gets run before I test
>>>>> the
>>>>> value of `collected?'?
>>>>>
>>>>> Neil ⊥
>>>>> _________________________
>>>>>    Racket Developers list:
>>>>>    http://lists.racket-lang.org/dev
>>>
>>>
>>>
>


Posted on the dev mailing list.