Clojure core.async put! versus go block
Asked Answered
F

1

8

I've read this great article about core.async here:

http://www.core-async.info/reference/primitives

I'm struggling to understand the internal mechanic of put! and go. I understand that:

  1. put! is asynchronous and can accept a callback. That works well in simple scenarios, but you can end in a callback hell.
  2. go fixes the callback hell, and allows to write asynchronous code in a synchronous style.
  3. go leverages a lightweight thread-pool, and leverages parking to enable concurrency.
  4. go uses a finite state-machine

I don't understand:

  1. How does put! achieve asynchrony? Does it also uses a thread-pool?
  2. Does put! also uses parking?
  3. What is the role of the finite state-machine in the go block? Is it what enables parking?
  4. Should I always try to use put! rather than go because it is cheaper? In that case, does that mean that put! achieve the exact same concurrency goodness as go, and that go is only used when I want to reason about complex asynchronous code?

Thanks a lot for shedding the lights on those mysteries.

Fisticuffs answered 11/2, 2016 at 14:16 Comment(0)
A
11

If you want to understand how core.async channels work, there's no better source than Rich Hickey's EuroClojure 2014 presentation: Implementation details of core.async channels.

As for your specific questions:

  1. If put! is not accepted immediately, it places a pending put (the value to be put on the channel + the put! callback) on a queue internal to the channel. Note that an exception will be thrown if there is no room in the queue (max capacity is currently fixed at 1024).

    The callback will be called on a pooled thread if (1) the put is not immediately accepted or (2) an explicit false is passed in as a final argument to the put! call (this argument is called on-caller?, see put!'s docstring for details).

  2. "Parking", in the context of go blocks, refers to suspending execution of a go block's state machine by recording certain details of its current state and saving them inside a channel, or possibly several channels, so that it can be restarted later. (Note that this arrangement means that if all the channels holding references to a suspended go block are GC'd, the go block itself can also be GC'd.) In other contexts it similarly refers to putting a thread of control in a state of suspended animation. put! is just a function (well, it's backed by a protocol method, but then that is just a protocol method), so the concept doesn't apply.

  3. Yes. It basically steps through the code in the go block, possibly suspending it when control reaches certain "custom terminals" (<!, >!, alt!).

  4. Not necessarily, you should first consider the risk of overflowing the internal queue (see point 1 above).

Accord answered 11/2, 2016 at 14:47 Comment(3)
Fantastic answer Michal! I've just finished watching the presentation on EuroClojure, and that was highly enlightening. Thanks for pointing that one out.Fisticuffs
Would love to see some side-by-side use-cases for the various port semantics to understand when to use which :)Scorpio
Slides hereGrenville

© 2022 - 2024 — McMap. All rights reserved.