If `build-array' gets #f, it'll raise a contract error. I could get
around that by keeping another reference to the procedure that I set to
#f later, but I'm going to try Matthew's suggestion first.
On 12/03/2012 09:47 AM, Robby Findler wrote:
In the test, if you get unlucky and there is a gc between the
definition of bx and unboxing of it then this test will pass in
correctly, I think (unless build-array complains if it gets #f?)
On Sun, Dec 2, 2012 at 11:44 PM, <ntoro...@racket-lang.org> wrote:
ntoronto has updated `master' from 325600b0cf to 8f17913d55.
http://git.racket-lang.org/plt/325600b0cf..8f17913d55
=====[ One Commit ]=====================================================
Directory summary:
74.0% collects/math/private/array/
25.9% collects/math/tests/
~~~~~~~~~~
8f17913 Neil Toronto <ntoro...@racket-lang.org> 2012-12-02 19:02
:
| Fixed memory leak in making arrays strict: doing so wouldn't clear
| the reference to the original procedure, which itself could hold on
| to a lot of memory
:
A collects/math/tests/strictness-memory-leak-test.rkt
M .../math/private/array/typed-array-struct.rkt | 28 ++++++++++++--------
=====[ Overall Diff ]===================================================
collects/math/private/array/typed-array-struct.rkt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- OLD/collects/math/private/array/typed-array-struct.rkt
+++ NEW/collects/math/private/array/typed-array-struct.rkt
@@ -76,17 +76,23 @@
(: unsafe-build-array (All (A) (Indexes (Indexes -> A) -> (Array A))))
(define (unsafe-build-array ds f)
- (define size (check-array-shape-size 'unsafe-build-array ds))
- (define: data : (U #f (Vectorof A)) #f)
- (define (strict!)
- (set! data (inline-build-array-data ds (λ (js j) (f js)) A)))
- (define unsafe-proc
- (λ: ([js : Indexes])
- (let ([data data])
- (if data
- (unsafe-vector-ref data (unsafe-array-index->value-index ds js))
- (f js)))))
- (Array ds size ((inst box Boolean) #f) strict! unsafe-proc))
+ ;; This box's contents get replaced when the array we're constructing is
made strict, so that
+ ;; the array stops referencing f. If we didn't do this, long chains of array
computations would
+ ;; keep hold of references to all the intermediate procs, which is a memory
leak.
+ (let ([f (box f)])
+ (define size (check-array-shape-size 'unsafe-build-array ds))
+ ;; Sharp readers might notice that strict! doesn't check to see whether
the array is already
+ ;; strict; that's okay - array-strict! does it instead, which makes the
"once strict, always
+ ;; strict" invariant easier to ensure in subtypes, which we don't always
have control over
+ (define (strict!)
+ (let* ([old-f (unbox f)]
+ [vs (inline-build-array-data ds (λ (js j) (old-f js)) A)])
+ ;; Make a new f that just indexes into vs
+ (set-box! f (λ: ([js : Indexes])
+ (unsafe-vector-ref vs (unsafe-array-index->value-index
ds js))))))
+ (define unsafe-proc
+ (λ: ([js : Indexes]) ((unbox f) js)))
+ (Array ds size ((inst box Boolean) #f) strict! unsafe-proc)))
(: unsafe-build-strict-array (All (A) (Indexes (Indexes -> A) -> (Array A))))
(define (unsafe-build-strict-array ds f)
collects/math/tests/strictness-memory-leak-test.rkt
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- /dev/null
+++ NEW/collects/math/tests/strictness-memory-leak-test.rkt
@@ -0,0 +1,16 @@
+#lang racket
+
+(require math/array
+ rackunit)
+
+;; Make a procedure that returns a random value to keep the optimizer from
converting it to a
+;; top-level, non-closure; if that happens, the module keeps a reference to
it, which makes this
+;; test always fail
+(define bx (make-weak-box (let ([v (random)]) (λ (js) v))))
+
+(define arr (build-array #() (weak-box-value bx)))
+
+;; Making `arr' strict should release the only remaining reference to the
contents of `bx'
+(array-strict! arr)
+(collect-garbage)
+(check-false (weak-box-value bx))
_________________________
Racket Developers list:
http://lists.racket-lang.org/dev