Comments (4)
Within event_test.clj you'll notice that do-receive
is a macro. I had to make it a macro because it did not work as a function. It compiles, but when the tests are executed I get the following error associated with the call to receive!
:
java.lang.AssertionError: Assert failed: alts! used not in (go ...) block
The following code is the implementation I tested of do-receive
as a function:
(defn do-receive
([done-name] (do-receive done-name true 0 false))
([done-name unique] (do-receive done-name false unique true))
([done-name done-is unique unique-is]
(op/receive!
[unique :ok]
(is unique-is "received [ref :ok] message")
[:done done-name]
(is done-is "to-go timeout reached")
:else
(is false "unknown message")
(after 2000 (is false "timeout occurred")))))
The odd thing is that the following similar code executes perfectly in a newly started lein repl
:
(require '[otplike.process :as process :refer [! pid->str self]])
(require '[clojure.core.async :as async])
(defn my-receive
[msg1 msg2 timeout self]
(process/receive!
msg (printf "[%s] %s %s%n" self msg1 msg)
(after timeout
(printf "[%s] %s %s%n" self msg2 timeout))))
(process/proc-defn
await-message
[timeout]
(my-receive "receive" "timeout" timeout (process/self)))
(defn run []
(let [pid0 (process/spawn await-message [1000])]
(println "[main] Started [" pid0 "]")
(let [pid1 (process/spawn await-message [(async/timeout 1000)])]
(println "[main] Started [" pid1 "]")
(let [pid2 (process/spawn await-message [:infinity])]
(println "[main] Started [" pid2 "]")
(println "[main] Waiting for 2 seconds")
(Thread/sleep 2000)
(println "[main] Sending :stop to [" pid2 "]")
(! pid2 :stop)
(println "[main] Waiting for 100mS")
(Thread/sleep 100)
(println "[main] Done")))))
(run)
from otplike.
Could you please make a new issue for the second comment? It's rather important question on its own. I'd better answer it separately if you don't mind.
from otplike.
Finally, I have time to answer this question :)
The short answer is - you did everything right. The rest is about limitations of otplike as of 0.3.x-alpha
.
The only (but very interesting) part of your code I want to talk about is this function:
(defn event-cancel
"Send a cancel request from 'server' to an 'event' process. If the event
process is already canceled it will still work. This function will not
return until the event process is canceled. Returns 'true'."
[server event]
(opu/execute-proc!!
(let [monitor-ref (op/monitor event)]
(op/! event [server monitor-ref :cancel])
(op/receive!
[monitor-ref :ok] (op/demonitor monitor-ref)
[:DOWN monitor-ref _ _ _] nil)))
true)
The good thing about it - it works as expected. And this is almost the only way to do it now.
What is not so good:
- Each call to
event-cancel
creates a new process. Which is not as lightweight as I wish it was. - Each call blocks the "real" thread. In case of multiple simultaneous calls it can lead to situation when each thread is blocked waiting for result and no thread awailable to compute and deliver the result.
The code below shows how gen-server/call
is implemented in 0.3.x-alpha
.
(defn call* [server message timeout-ms]
(process/async
(let [reply-to (async/chan)
timeout (if (= :infinity timeout-ms)
(async/chan)
(async/timeout timeout-ms))]
(if-not (! server [::call reply-to message])
[:error :noproc]
(match (async/alts! [reply-to timeout])
[[::terminated reason] reply-to] [:error reason]
[[::reply value] reply-to] [:ok value]
[nil timeout] [:error :timeout])))))
As you can see, we use channel to get the answer back.
The problem with this code is that you can not monitor server while waiting for the answer. Checking the result of !
ensures that the message has been delivered to the server's inbox. Some code on the server side ensures that reply will be sent back even if the server crashed while processing the call. But if the server exits after the message has been placed into its inbox but before it started processing the call, reply will be never sent back.
It is our compromise between non-blocking "fast" call in common case and possible delay for the time of a timeout. And that's why as of now it's not recommended to use gen-server/call
without timeout.
I hope to implement process/selective-receive!
and "flushing" process/demonitor
in 0.4.0-alpha
to make possible fast and reliable calls.
Also there is an additional tinkering with the timeout value, which would have been unnecessary with selective receive.
I don't have a lot to say about the tests. It seems like they do what they are supposed to do :)
from otplike.
As you suggested in issue 26 (#26), using "receive!!" allowed me to make do-receive just a regular function:
(defn do-receive
([done-name] (do-receive done-name true 0 false))
([done-name unique] (do-receive done-name false unique true))
([done-name done-is unique unique-is]
(op/receive!! ;; <<<<<===== Here is the change
[unique :ok]
(is unique-is "received [ref :ok] message")
[:done done-name]
(is done-is "to-go timeout reached")
:else
(is false "unknown message")
(after 2000 (is false "timeout occurred")))))
from otplike.
Related Issues (20)
- Using receive In A Helper Function HOT 2
- Simple Logging System HOT 2
- Example: otplike Logging System HOT 2
- Require :reload-all Question HOT 2
- java.lang.NullPointerException during fn call in process/spawn HOT 2
- Don't allow parallel receive from process' inbox HOT 1
- Selective receive HOT 1
- Example application for otplike with app restart feature HOT 1
- supervisor/which-children
- map async value HOT 1
- Fix supervisor's :period property units
- Special processes running on the agents thread pool
- Initial application module/behaviour
- spawn-opt doesn't throw exception when process name is already registered HOT 1
- supervisor must react on :shutdown exit reason
- Add gen-server/stop fn
- protocols for swapping out core.async and core.match? HOT 20
- Handling backpressure HOT 2
- circuit-breaker-like supervision? HOT 4
- A default circuit-breaker implementation
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from otplike.