-
Notifications
You must be signed in to change notification settings - Fork 89
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mount and core.async #84
Comments
a couple observations: configuration over public constants(defstate workers-nbr :start 3) this would usually come from configuration / program parameters rather than being defined as a state stop functionsis there any reason your stop functions are anonymous functions: :stop #(a/close! heartbeat-chan) rather than just: :stop (a/close! heartbeat-chan) not stopping state on stopmost likely the root of your problem is that a single state is started and stopped in different states, whereas it should be started and stopped in/as the same state: (defstate a :start (foo)
:stop b)
(defstate b :start (bar)
:stop a) vs. (defstate a :start (foo)
:stop a)
(defstate b :start (bar)
:stop b) |
Hi, I'm having a very similar problem. In general, I think
But this doesn't actually work. The basic problem is that asynchronous goroutines expect to see a channel in the "closed" state in order to terminate themselves, but
Your program will randomly crash because Personally, I'm not really a fan of the whole (undocumented) |
why
|
Yeah, I see your point. |
I don't think mount has limitations when it comes to core.async. Besides the solution above with a (defn listen-to-events []
(let [req-chan (chan)]
(async/go-loop []
(when-let [request (<! req-chan)]
;; handle request
(recur)))
req-chan))
(defstate listener :start (listen-to-events)
:stop (async/close! listener)) It also conveys a notion of state a bit better since it is not just a channel that is stateful, but a listener as well. |
Hi, sorry for my low participation after asking a question, changed a bit of focus at work and went on holidays. You were obviously right for your first two remarks (external conf, using anonymous function instead of a simple function call block). Your third remark was a bit confusing to me since you display a cyclic dependency with a and b. My go-loop pipeline is acyclic, with a-fetching -> b-accumulating -> c-output. And I had in mind I would be able to manage dependant states/the dependency order with mount, starting automatically c, then b, then a, and stopping a, then b, then c. I'll have to wrap my head around this a bit more, since it's still not clear to me at this point what could/should be handled by mount as state (the channels, the go-loops, should I have just one restartable state for the whole pipeline ?, etc.). I might still be doing pretty basic mistakes. Anyway, I'm glad thurn stepped in and fueled the discussion a bit further. Cheers, |
These are not basic mistakes, different developers would come up with different designs and will stand their ground to prove their way is the best :) It would depend on the application design and usage of course, but when it has to do with Sometimes it makes sense to deem the whole pipeline as a stateful component: https://stackoverflow.com/a/40435030/211277 Things to consider when deciding when create a mount state and what should be "in it":
|
This SO answer is awesome / many food for thoughts, thanx ! |
Little update : I made sure states are responsible for stopping only themselves, but still since I have running go-loops /thread-loops and dependencies (state B loop depending on state A), I keep having some kind of race condition. I tried with a stop-channel, it seems like state B gets his stop called, which would close its loop in the next cycle (when alt!!-ing on the stop channel)... and then A gets stopped... But during this last cycle, the B loop is still running and calls A -> IllegalArgumentException: No implementation of method: :my-method! of protocol: #'my-ns/StateAProtocol found for class: mount.core.NotStartedState. So as I get it, there are only two options here :
Regards, |
i just wanna put it here for the ones to come - (defstate listener
:start (listen-to-events)
:stop (async/close! ^clojure.core.async.impl.protocols/Channel listener)) notice the type hint above --> ^clojure.core.async.impl.protocols/Channel Why ?before using the type hint we get this message:
it means that it gets Enjoy 😉 |
Hi,
Sorry to post here instead of Slack (blocked by proxy), and it might be a bit of a noob question, but I've got trouble finding documentation on Mount regarding the stopping process / running concurrent processes.
When I have a starting order : state1 -> state2 -> state3
And the stopping order : state3 -> state2 -> state1
Can I block in the state3 function, waiting to clean up some running concurrent process ?
(For more context :
I've got quite a few channels and go-loops running, with dependencies between them (1 go loop giving tempo -> 3 thread I/O workers -> 1 test go-loop downstream-consumer).
I have the following states in my namespace :
And my "main" :
I get these kinds of exceptions when I exit :
Exception in thread "async-thread-macro-3" java.lang.IllegalArgumentException: No implementation of method: :take! of protocol: #'clojure.core.async.impl.protocols/ReadPort found for class: mount.core.NotStartedState
I guess the states get stopped while my go-loops are still running meaning work-chan is stopped right after #(a/close! work-chan) got fired, but before the go-loop had time to pick up the closed signal, hence the go-loop trying a <! on an already closed state.
I'm thinking of blocking inside the stop functions on the go-loop / threads like :
Will it work properly ? Are mount and core.async meant to be combined in this way ? Or is it bad design ?
Cheers,
François
The text was updated successfully, but these errors were encountered: