core.async

Race condition when closing mults

Details

  • Type: Defect Defect
  • Status: Open Open
  • Priority: Major Major
  • Resolution: Unresolved
  • Affects Version/s: None
  • Fix Version/s: None
  • Component/s: None
  • Labels:
  • Approval:
    Triaged

Description

When a mult is tapped at around the same time as the source channel is closed, the tapped channel may not be closed.

(require '[clojure.core.async :refer (chan mult tap close!)])
(let [s (chan)
      m (mult s)
      c (chan)]
  (tap m c)
  (close! s)
  (impl/closed? c))

The above code will sometimes return true, and sometimes return false.

Cause: This is caused by the following code in the mult function:

(if (nil? val)
  (doseq [[c close?] @cs]
    (when close? (close! c)))

Any channels tapped after cs is dereferenced will not be closed.

Approach: A possible solution to this could be to always close channels tapped to a closed source. i.e.

(let [s (chan)
      m (mult s)
      c (chan)]
  (close! s)
  (tap m c))  ;; will always close c

This could be achieved by adding a flag to the cs atom to denote whether the mult is open or closed. If it's closed, any tapped channel is closed automatically.

Activity

Rich Hickey made changes -
Field Original Value New Value
Assignee Rich Hickey [ richhickey ]
Alex Miller made changes -
Labels mult
Description When a mult is tapped at around the same time as the source channel is closed, the tapped channel may not be closed.

(let [s (chan)
      m (mult s)
      c (chan)]
  (tap m c)
  (close! s)
  (impl/closed? c))

The above code will sometimes return true, and sometimes return false. This is caused by the following code in the mult function:

(if (nil? val)
  (doseq [[c close?] @cs]
    (when close? (close! c)))

Any channels tapped after cs is dereferenced will not be closed.

A possible solution to this could be to always close channels tapped to a closed source. i.e.

(let [s (chan)
      m (mult s)
      c (chan)]
  (close! s)
  (tap m c)) ;; will always close c

This could be achieved by adding a flag to the cs atom to denote whether the mult is open or closed. If it's closed, any tapped channel is closed automatically.
When a mult is tapped at around the same time as the source channel is closed, the tapped channel may not be closed.

{code}
(require '[clojure.core.async :refer (chan mult tap close!)])
(let [s (chan)
      m (mult s)
      c (chan)]
  (tap m c)
  (close! s)
  (impl/closed? c))
{code}

The above code will sometimes return true, and sometimes return false.

*Cause:* This is caused by the following code in the {{mult}} function:

{code}
(if (nil? val)
  (doseq [[c close?] @cs]
    (when close? (close! c)))
{code}

Any channels tapped after cs is dereferenced will not be closed.

*Approach:* A possible solution to this could be to always close channels tapped to a closed source. i.e.

{code}
(let [s (chan)
      m (mult s)
      c (chan)]
  (close! s)
  (tap m c)) ;; will always close c
{code}

This could be achieved by adding a flag to the cs atom to denote whether the mult is open or closed. If it's closed, any tapped channel is closed automatically.
Approval Triaged [ 10120 ]

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated: