core-async

Flawless 2024-12-12T04:22:54.134319Z

Hello everyone! I've got a question, is it safe (probably it is, I just want to understand why it's safe) to use binding inside go . Correct me if I'm wrong, but as I've found in sources, core-async state machine manipulate some ThreadBindingFrames, but I don't quite understand how do they work. Is it correct that all clojure vars are stored in this thread local https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/Var.java#L71 and state machine just remaps them for task local values? Since state machine knows when to switch between tasks it seams it could safely remaps tasks contexts so every task sees its own vars values. https://github.com/clojure/core.async/blob/master/src/main/clojure/clojure/core/async/impl/ioc_macros.clj#L926

(defn- emit-state-machine [machine num-user-params custom-terminators]
  ...
  (let [old-frame# (clojure.lang.Var/getThreadBindingFrame)
        ret-value# (try
                     (clojure.lang.Var/resetThreadBindingFrame (aget-object ~state-sym ~BINDINGS-IDX))
                     (loop [] ...)))
                     ...
                     (finally
                               (aset-object ~state-sym ~BINDINGS-IDX (clojure.lang.Var/getThreadBindingFrame))
                               (clojure.lang.Var/resetThreadBindingFrame old-frame#)))]
  ...))))))
Thanks in advance if somebody could help me understand, what's exactly going on in the go contexts (:

2024-12-12T04:38:11.457549Z

More or less yes

2024-12-12T04:40:13.641289Z

A binding frame is a map of vars to the local values for a thread, they are captured when a go block is created, and that captured frame is reinstalled on whatever thread a go block continues running on when it resumes from a parking channel operation

2024-12-12T04:44:42.029549Z

And that captured frame is installed on whatever thread is running on, and when a go block parks the possibly new frame is captured

✅ 1
Flawless 2024-12-12T05:05:56.721069Z

Thanks, looks like we could see this happens in the fn returned from emit-state-machine so we capture vars on creation and can mutate their values inside the context later. Does it mean that every go has it's own copy of every dynamic Var we have declared and so consumes memory to contain their values? Answering to myself - I've found that Frames are just a normal Clojure maps, so copying them for each go block should not be a big deal.

Flawless 2024-12-12T05:08:40.746099Z

this sounds much more powerful than just a thread locals in java even for virtual threads

2024-12-12T05:22:08.587869Z

Well, I dunno about that, it is built on thread local, and binding just works with virtual threads just because thread locals work on them

🙌 1
Flawless 2024-12-12T08:16:54.980989Z

Never mind actually this my statement is obviously incorrect, since copying clojure persistent map to thread local is as easy as copying it to any other place like go block context, sometimes It's just hard to realise that clojure persistent structures are so well designed so using them across the different threads is safe and effective at the same time, even if i understand how them works internally)