Current implementation of reactor allows events to be triggered from any thread within a single reactor, with a single lock to ensure propagation turns are serialized. However, this is a dangerous feature because when multiple reactors are involved it is rather easy to deadlock the system, e.g :
(defn echo [in out]
(m/ap (loop []
(in nil)
(m/? (m/via m/cpu))
(m/amb (m/? out) (recur)))))
(let [l (m/mbx)
r (m/mbx)]
(m/join vector
(m/reactor (m/stream! (echo l r)))
(m/reactor (m/stream! (echo r l)))))
The only way to solve the deadlock problem is to bind each reactor to an event loop to make sure only one thread is ever able to trigger propagation turns. This raises the question, is there a single valid use case for multithreaded reactors ? If there isn't any, it may be safer to require all events to happen from a single thread within a single reactor, and raise an error otherwise. This would prevent deadlock issues, also removing the lock could slightly improve performance.
Current implementation of
reactor
allows events to be triggered from any thread within a single reactor, with a single lock to ensure propagation turns are serialized. However, this is a dangerous feature because when multiple reactors are involved it is rather easy to deadlock the system, e.g :The only way to solve the deadlock problem is to bind each reactor to an event loop to make sure only one thread is ever able to trigger propagation turns. This raises the question, is there a single valid use case for multithreaded reactors ? If there isn't any, it may be safer to require all events to happen from a single thread within a single reactor, and raise an error otherwise. This would prevent deadlock issues, also removing the lock could slightly improve performance.