core.async

CLJS: mult becomes blocked after putting to a closed channel

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Completed
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • Patch:
    Code

Description

A mult can become blocked after a putting to a closed tap channel due to an off-by-one error in triggering the end condition.

It appears the issue was recently fixed in the Clojure version of mult, but not the ClojureScript version.
https://github.com/clojure/core.async/commit/8864214ed9fa9aa8dc12297c6cf99ad0be65a6ae

The issue occurs because the counter that indicates the mult has finished a putting to all taps, dctr is decremented out of band and the zero? triggering condition in done function is missed.
https://github.com/clojure/core.async/blob/65b2d6c81350ba8b4bb3a8b82ad45ba984c0037c/src/main/clojure/cljs/core/async.cljs#L457

The done function passed to put! appears to be called regardless of whether the channel is closed. Because of this, we don't need to (swap! dctr dec) or necessarily call (done nil) (the CLJ fix) to fix this issue.

Demonstration: https://gist.github.com/loganlinn/e840139a9ab5503970fd

Activity

Hide
Gijs Stuurman added a comment -

The title of this issue says it's CLJS only, but the double done/counter-decrement applies to the CLJ version as well. As is briefly mentioned at the end of this issue.

For closed channels the done function is called twice in mult, because put! returns false for closed channels and calls the callback:
https://github.com/clojure/core.async/blob/65b2d6c81350ba8b4bb3a8b82ad45ba984c0037c/src/main/clojure/clojure/core/async.clj#L684

In the CLJ version this will not lead to blocking but it violates the behavior in the docstring of "each tap must accept before the next item is distributed" when a tap is closed.

Show
Gijs Stuurman added a comment - The title of this issue says it's CLJS only, but the double done/counter-decrement applies to the CLJ version as well. As is briefly mentioned at the end of this issue. For closed channels the done function is called twice in mult, because put! returns false for closed channels and calls the callback: https://github.com/clojure/core.async/blob/65b2d6c81350ba8b4bb3a8b82ad45ba984c0037c/src/main/clojure/clojure/core/async.clj#L684 In the CLJ version this will not lead to blocking but it violates the behavior in the docstring of "each tap must accept before the next item is distributed" when a tap is closed.
Hide
David Nolen added a comment -

As far as I can tell the original issue has been fixed in master?

Show
David Nolen added a comment - As far as I can tell the original issue has been fixed in master?
Hide
Logan Linn added a comment -
Show
Logan Linn added a comment - Yeah. It appears https://github.com/clojure/core.async/commit/40ab7edacac747af1108c80223039e8a0b1c2ca9 resolved the issue. Thanks!

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: