Welcome! Please see the About page for a little more info on how this works.

0 votes
in core.async by

Running this code eventually throws OOME :

`
(let [c (a/chan)]
(a/go (while (a/<! c)))
(a/go

(when-some [xs (range)]
  (doseq [x xs]
    (a/>! c x)))))

`

This one (with {{let}} instead of {{when-some}}) works fine :

`
(let [c (a/chan)]
(a/go (while (a/<! c)))
(a/go

(let [xs (range)]
  (doseq [x xs]
    (a/>! c x)))))

`

In the former, the range seq is stored on the heap (in go block's state array) and retained for the whole doseq iteration, althought it's not needed anymore after loop initialization.
In the latter, the range seq is stored on the stack so the compiler is able to perform its local clearing magic.

This behavior is likely to happen for every value spanning across multiple ssa blocks.

3 Answers

0 votes
by

Comment made by: alexmiller

Isn't the problem actually that when-some is really a let and you're holding the head of the infinite range in the let?

0 votes
by

Comment made by: leonoel

I updated the description with a less contrived example.
The problem happens because a let binding wraps a control flow expression. This effectively moves the value from the stack to the heap, preventing the compiler to clear the reference.

0 votes
by
Reference: https://clojure.atlassian.net/browse/ASYNC-219 (reported by leonoel)
...