Fork me on GitHub

Looks like offer! always returns true when on a promise-chan:

(def foo (a/promise-chan))
(a/offer! foo 1)
;;=> true
(<?? foo)
;;=> 1
(a/offer! foo 2)
;;=> true
(<?? foo)
;;=> 1
I would consider this a bug, since I was wanting to know if the offer! succeeded or not, as in, was my go block the one to successfully deliver the value of the promise-chan, but I'm asking here in case its by design


> offer! Puts a val into port if it's possible to do so immediately. > nil values are not allowed. Never blocks. Returns true if offer succeeds. from promise-chan > Once full, puts complete but val is dropped (no transfer). That behavior seems to match the docs at least.


Hum, I mean if you equate a "puts" with an "offer"


> Puts a val into port if it's possible to do so immediately. [...] Returns true if offer succeeds.


> Once full, puts complete


Right, so that just means it won't park


So the puts complete, as in, it won't park or block


But then the question is what is an "offer", is it a synonym for a put? Or does it imply that the value was put into the buffer as well


(let [ch (-> (chan (async/dropping-buffer 1)))]
  (async/>!! ch 42)
  (async/offer! ch 42))
;; true


Ya, the fact it behaves the same for dropping-buffer might indicate its by design


it just tries to put, never blocks, and returns true if the offer succeeds. I think that means if the put succeeds


Ya, but that's strange, because offer! says it never blocks, and a put completing only implies it won't block. So like, it seems wrong


It kinds of prevent being able to use offer! to synchronize things though, at least in my case, because I have a race between two things trying to offer! to the promise-chan, and I need to know which one made it first



(let [ch (-> (chan))]
  (async/close! ch)
  (async/offer! ch 42))
;; false


Ya, I would have made offer! return true or false based on if the value is transferred


Since offer! never blocks anyways, the fact that the puts complete or not doesn't seem to matter. If the puts fails well offer! wouldn't block anyway


Or at least if it worked like that, it solved my use case 😛


(def foo (promise-chan))
(go (and (offer! foo 1) (println "Go 1 won!")))
(go (and (offer! foo 2) (println "Go 2 won!")))


This is my use case. Not sure how I can solve it.


you could do something like:

;; not a promise chan!
(def foo (chan))
(go (let [let-me-know-chan (chan)]      
      (when (and (offer! foo [1 let-me-know-chan])
                 (<! let-me-know-chan))
        (println "Go 1 won!"))))
(go (let [let-me-know-chan (chan)]
      (when (and (offer! foo [2 let-me-know-chan])
                 (<! let-me-know-chan))
        (println "Go 2 won!"))))


and the foo process will write true to the first and close! the rest


there's probably something more elegant, but that's what I can think of off the top of my head


although, this wouldn't work with a promise chan since a promise chan can only receive one value


I'm not sure I understand what will deliver to let-me-know-chan ?


Oh I see what you meant now, ya with promise-chan I won't see that like they came in a particular order.


Since the others will be dropped


  (let [[foo-val foo-callback] (<! foo)]
    (>! foo-callback true)
    (close! foo)
    (go (loop []
          (when-let [[_ callback-ch] (<! foo)]
            (close! callback-ch)


I don't think that works


Oh, hum... ok no I see ya it works


there's probably a simpler way to do it


Each put their own channel and so the one that is in the promise-chan is the one that won. Ya, hum, its a bit ugly haha.


It also kind of defeats the point of using the non-blocking offer! just to block right after to know if your offer! worked 😛


I guess I could do:

(def foo (a/promise-chan))
(a/go (let [id (gensym)] (and (a/offer! foo {id 1}) (get (a/poll! foo) id false) (println "Go 1 won!"))))
(a/go (let [id (gensym)] (and (a/offer! foo {id 2}) (get (a/poll! foo) id false) (println "Go 2 won!"))))


That reminds me of another question I had, is there a way to peak into a channel? poll! for promise-chan works since it won't remove it, but on other channels it would remove the item, was curious if there is a peak!


are you just trying to figure out which go loop goes first?


it seems like your example wouldn't actually deliver anything


ah ok, I think that works


In my case its that I have a function that is called race and it returns the first result of a vector of promise-chan. It also cancels all other operations as soon as one succeeds. So I want the one that succeeds to offer to cancel the others, but the offer! and the loop over others and cancel them I guess can happen in parallel, since each one is in a go block. Now to be honest, I'm still trying to figure out if its a problem that another one also cancels the other including the one that won, since the one that one already offered the result it might not matter. But I was like, oh well I can just do (and (offer! ...) (cancel ...)) and not worry about it, and then I realized that does not work haha.


in that case you might want to use >! instead of offer


(defn race
  (let [ret (a/promise-chan)]
    (if (seq chans)
      (doseq [chan chans]
          (let [res (<! chan)]
            (and (a/offer! ret res)
                 (run! #(when-not (= chan %) (cancel %)) chans)))))
      (a/close! ret))


Ah ya, now I remember what I was worried about... So the way my cancellation works, is that it actually just puts a ::cancelled value on the other channels, and the go operations over those channels as they go about their tasks, they check if their return channel has a ::cancelled on it or not, and if they see it does, they just stop what they are doing.


You could also have a separate, shared cancel channel that is closed on cancellation


A pseudo example:

(defn do-something-async
  (let [ret (a/promise-chan)]
    (a/go-loop []
      (Thread/sleep 100)
      (when-not (= ::cancelled (a/poll! ret) (recur))


and whoever wins just closes the shared cancel chan


Ya, but I find that gets intractable quick


I'm not sure if I need to protect this bit:

          (let [res (<! chan)]
            (and (a/offer! ret res)
                 (run! #(when-not (= chan %) (cancel %)) chans)))))


I think in my case I don't need to worry, since whichever won the offer! has won the race, and it doesn't really matter which one tries to cancel the others. But it still got me curious about the problem


For the fun of it, I think this would do what I wanted:

(defn race
  (let [ret (a/promise-chan)
        tmp-ret (a/promise-chan)]
    (if (seq chans)
      (doseq [chan chans]
          (let [res (<! chan)
                id (gensym)]
            (and (a/offer! tmp-ret {id res})
                 (get (a/poll! tmp-ret) id false)
                 (do (run! #(when-not (= chan %) (cancel %)) chans)
                     (a/offer! ret res))))))
      (a/close! ret))

👍 1

So if I see that I do have race condition somewhere I'll remember this trick, thanks for the help @smith.adriane


You are basically trying to implement a lock, in general you will have a better time representing the critical section as a go block that you run a single instance of and pass stuff to via channels vs have multiple instances of the same code running like you have


Hum.... ya I guess I could have one go block that just alts! on the channels and on result cancel all the others


Now I can't remember why I didn't use alts! before and just fired a bunch of go block to monitor this... Maybe I just didn't think of it