Fork me on GitHub
#core-async
<
2021-09-20
>
awebneck15:09:35

Question for folks - I’m noticing some behavior w/ core.async that I’m a bit surprised by. In Java, when an exception occurs in some thread that isn’t handled explicitly in that thread’s Thread#run method, the default thread exception handler is called on the same thread and is waited upon until the thread itself joins. However, with a core.async thread, while the Java default thread exception handler is called, it appears to be called separately from the thread that threw the exception, which means that in the event of an unhandled exception the channel returned by clojure.core.async/thread closes before the exception handling is complete. Examples: Preparation (registering default thread exception handler):

(Thread/setDefaultUncaughtExceptionHandler
 (reify Thread$UncaughtExceptionHandler
   (uncaughtException [_ thread ex] (Thread/sleep 1000) (println "CAUGHT EXCEPTION"))))
Java thread:
(do
  (let [thread (Thread. (reify Runnable (run [this] (throw (Exception. "Test Exception")))))]
    (.start thread)
    (.join  thread))
  (println "THREAD COMPLETE" {}))
;; wait 1 second, then
> CAUGHT EXCEPTION
> THREAD COMPLETE {}
core.async thread:
(do
  (a/<!! (a/thread (throw (Exception. "Test Exception"))))
  (println "THREAD COMPLETE" {}))
> THREAD COMPLETE {}
;; wait 1 second, then
> CAUGHT EXCEPTION
This was surprising behavior to me, but now I wonder if there’s any method I can use to explicitly wait on the exception handler to complete before proceeding in execution?

hiredman16:09:05

the core.async channel is closed before the exception bubbles out

awebneck16:09:17

yeah - is there a means I can use to halt execution until the default exception handler completes?

awebneck16:09:19

welp, there’s my answer, I guess. Thanks!

hiredman16:09:39

you could call the default exception handler yourself, wrap everything in a try catch, get the default exception handler and invoke it

awebneck16:09:08

Yeah, there are workarounds I’m exploring now, just didn’t know if there was some established means of handling the issue that I wasn’t aware of.

hiredman16:09:04

it is kind of an interesting case, should core.async being doing something like that before the finally that closes the channel, dunno, you might throw something on http://ask.clojure.org

hiredman16:09:34

(I am not a huge fan of the default exception handler)

Dynom20:09:44

Bit of a beginner question. Are Atoms threadlocal? I have a bug where I edit an atom in a async context, but when I read it back I only find the value stored there after the first swap!. However my unit tests find no such issue, but those work in a synchronous context.

hiredman20:09:57

atoms are not thread local, you are experiencing a race condition

hiredman20:09:20

firing off another thread to mutate an atom, then reading the atom, without making sure the task in the other thread has completed first

Dynom20:09:25

I'm reading them after getting a notification from the thread which edits them that it has been altered.

hiredman20:09:22

then you have some other bug, either some unsafe modifcation of the atom(something with reset!), racing with another thread, etc

Dynom20:09:24

Alright, I'll continue debugging, thank you anyway.