Fork me on GitHub
#core-async
<
2018-04-06
>
ajs15:04:46

would a good idiom for consuming exceptions and assertion errors that occur in go blocks be to catch the exception and put it on a dedicated channel for exceptions so it is not lost?

ajs15:04:58

i just spent quite some time tracking down a problem because i never saw the exception and then realized it had happened in the go block

justinlee15:04:17

@ajs yes that is a very tricky part of go blocks. have you seen this article? i found it and the stuff it links to helpful. http://martintrojer.github.io/clojure/2014/03/09/working-with-coreasync-exceptions-in-go-blocks

ajs16:04:36

I guess that's another way to do it. My way seems a bit easier to grock for me

justinlee16:04:07

seems like you’ll have to poll both channels or do an alts type take from both, no?

hiredman17:04:20

it depends a lot on how you structure your program

ajs17:04:54

Why not just have a dedicated channel and go block for exceptions?

ajs17:04:09

I don't see a need for anything fancy

hiredman17:04:37

if you use channels like promises and go blocks as one shot async functions then doing things like having the go block stick any exceptions on its "return" sort of makes sense

hiredman17:04:47

but I don't really like that structure

ajs17:04:03

My go blocks are never one shot.

hiredman17:04:07

I usually use go blocks with loops

hiredman17:04:10

yeah. exactly

ajs17:04:38

The exception is when I want to asynchronously handle a future when it's done.

hiredman17:04:43

what I like to do is treat go blocks as little services processes, and they get a input channel, an output channel, an error channel, and maybe a halt channel

ajs17:04:36

Do you then wrap the entire go block in try catch?

ajs17:04:46

That's what I'm thinking

hiredman17:04:42

it sort of depends how an why errors might occur, usually an "error" shouldn't bubble out and terminate the go block, it is just an error for a single request it is handling

ajs17:04:01

If my go block is handling fast streaming data, does a try/catch block in Clojure add any overhead for performance ?

hiredman17:04:04

hard to say

justinlee17:04:14

right i was bringing my clojurescript bias and forgetting about those nasty ol’ threads 🙂

hiredman17:04:28

the clojure (as opposed to clojurescript) version of the go macro has some optimizations where it will emit chunks of the clojure code unaltered if there are no operations in it that need to be exposed in the state machine

hiredman17:04:25

so the overhead of a try/catch that doesn't contain any channel operations would be the same in the go macro vs. normal clojure code

ajs17:04:27

The catch block would have a put! for handling the error, placing on an error channel

hiredman17:04:19

if there is a channel operation in there the go macro has to pull it all apart and emulate the jvm's try/catch/finally semantics on the state machine, which I am not sure what the performance difference would be. if I recall the emulated code pushes handler addresses on to a stack and uses the state machine to jump to them, so similar mechanically (I think) to what the jvm must do, but (wild guess) maybe an order of magnitude or two slower because it is in user code

ajs17:04:19

Would put! vs >! in the go block make a difference?

hiredman17:04:39

I would try it as normal and benchmark it before worrying too much

hiredman17:04:54

I only use put! when I know for sure the channel has capacity, with put! you are bypassing the built in feedback (upstream producers halt until downstream consumers can handle msgs) that you normally get

ajs17:04:22

At least on a large buffered channel your put! is less likely to fail

hiredman17:04:39

I rewrote the how the go macro handles try/catch a few years ago, because it was very broken in a lot of cases, so my emphasis was correctly emulating the semantics of try/catch/finally in normal clojure code

❤️ 4
chouser19:04:28

@hiredman Continuing from #clojure... That's all fine, but I don't think you're addressing my concern. I will restate: You're saying the pair [channel value], which is passed as element of parameter ports, is neither a channel nor a port. Later, when one of these elements of ports is returned, it is apparently never a [channel value] pair but is always a port or perhaps a channel. Surely that should be mentioned in the docstring?

hiredman19:04:28

I am not sure I follow your concern, the docstring reads pretty much like what it is to me

hiredman19:04:48

I guess the parameter "ports" might be better called "operations"

hiredman19:04:37

where a single channel / ReadPort represents a take operation, and a pair of [channel value] (where channel implements WritePort) represents a put operation

hiredman20:04:40

alts! picks one, and returns [result-of-operation port-or-channel-that-was-operated-on]

hiredman20:04:50

so alts! doesn't return an element from the passed in ports

hiredman20:04:05

I guess that is the source of confusion?

chouser20:04:43

yeah, I think so.

chouser20:04:11

both places are called ports but one place is sometimes not a port but a pair that contains a port.

hiredman20:04:29

and I am not confused because I never bothered to read the name of the passed in parameter so I never tried to associate it with the word port in the docstring