GithubHelp home page GithubHelp logo

Comments (4)

pwhittin avatar pwhittin commented on August 23, 2024 1

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.

sskorokhodov avatar sskorokhodov commented on August 23, 2024

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.

sskorokhodov avatar sskorokhodov commented on August 23, 2024

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:

  1. Each call to event-cancel creates a new process. Which is not as lightweight as I wish it was.
  2. 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.

pwhittin avatar pwhittin commented on August 23, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.