core-async

2019-06-10T18:32:48.058800Z

@hiredman It appears the promise-chan issue we discussed last week is somewhat worse than I thought. It looks like a fulfilled promise-chan will always be preferred by alts! to a timeout chan, even if the timeout chan has already been triggered (ie, x millis have passed).

ghadi 2019-06-10T18:36:31.060200Z

alts! randomizes the order in which it evaluates channels unless you set :priority @pmooser

ghadi 2019-06-10T18:38:02.062200Z

If you alts! over ready channels (like a fulfilled promise or an elapsed timeout) you should get an even distribution of chosen ops

ghadi 2019-06-10T18:38:54.062900Z

If that isn’t the case, I’d like to see a minimal repro case

2019-06-10T18:41:36.063900Z

@ghadi if you have an alts! in a loop, even if the timeout expires, the js thread isn't yielded, so the fact that the timeout has expired is never registered

2019-06-10T18:42:08.064300Z

because alts!, if it can proceed right way, never yields control of the js thread

2019-06-10T19:40:03.064800Z

@ghadi As requested, here is your case:

2019-06-10T19:40:21.064900Z

ghadi 2019-06-10T19:40:33.065300Z

javascript, right? @pmooser

2019-06-10T19:41:57.065900Z

Yes, cljs

2019-06-10T19:42:17.066400Z

I actually have not run that snippet in clj, so I'm not sure if it would be the same.

2019-06-10T19:42:33.066800Z

Actually I suspect it won't be, based on some tests I had before, but I'm not 100%.

ghadi 2019-06-10T19:42:53.067200Z

it makes sense given the background from @hiredman

ghadi 2019-06-10T19:43:04.067800Z

99% sure it is different in clj

2019-06-10T19:43:25.068300Z

I don't agree. I mean I "understand why" from the implementation, but it doesn't make sense in "this is non-surprising and what everyone would expect from a supposedly non-deterministic choice".

2019-06-10T19:43:38.068700Z

It's basically an implementation detail, and it is undesirable.

ghadi 2019-06-10T19:44:19.069700Z

yeah I also don't think it is correct -- I meant it makes sense given the impl in cljs

2019-06-10T19:44:41.070300Z

In my opinion, we shouldn't have to know the type of chans to know if our code will behave as expected - especially since chans have no mechanism for tracking identity, like a name or even an IMeta implementation.

2019-06-10T19:45:46.071500Z

Anyway, I am guessing this will never be addressed but it is definitely unfortunate. I just provided my own implementation of promise-chan that doesn't do this (because it is not based on an infinite buffer) and we will probably always avoid the other one just to be sure not to run into this.

2019-06-10T19:46:13.072300Z

I think it's basically an edge case of an implementation detail that rears its head because most buffers won't infinitely provide values (and thus be preferred indefinitely).

ghadi 2019-06-10T19:46:15.072400Z

it's not a problem with promise-chan

ghadi 2019-06-10T19:46:38.073300Z

it's a problem that the timeout is never fulfilled

2019-06-10T19:46:42.073500Z

That is not true.

2019-06-10T19:46:50.073800Z

The code fulfills the timeout before alts-ing.

2019-06-10T19:47:06.074300Z

Note the:

(<! t)

ghadi 2019-06-10T19:47:10.074500Z

oh -- I misread

ghadi 2019-06-10T19:47:38.074800Z

what mod did you make to promise-chan?

ghadi 2019-06-10T19:48:51.075700Z

(in clj it behaves)

2019-06-10T19:49:09.076Z

I wrote my own, and didn't implement it as a buffer. I implement ReadPort and WritePort myself, with a go-loop handling reads and writes (and deferring to some internal chans).

2019-06-10T19:49:16.076400Z

Thank you for testing it in clj!

2019-06-10T19:50:01.077500Z

Basically the trick (I think) is that I'm providing the same API, but because I'm falling back to operations on normal many-to-many chans and using a go-loop to ferry values around, it does yield as expected.

2019-06-10T19:50:05.077700Z

I'm sure it is marginally less efficient,

2019-06-10T19:50:16.078100Z

but for my use, the efficiency of this won't pose a problem,

2019-06-10T19:50:24.078500Z

especially given the alternatives.

2019-06-10T19:50:59.078800Z

One moment, I want to write another test to confirm a suspicion ...

2019-06-10T19:53:01.079300Z

Ok, yes, it works how I thought.

2019-06-10T19:55:32.080300Z

So it looks like any buffered chans with ready values will always be preferred over the timeout chan, even if the timeout has already triggered.

ghadi 2019-06-10T19:56:18.081Z

yup ^ it's not a promise-chan problem

2019-06-10T19:57:50.081700Z

Sure, but the reason a promise chan is "the problem" for me is that no other chan (that I know of) will indefinitely provide values. Ie, a normal buffered chan will exhaust its buffer pretty soon, and then at least timeouts/etc will get a chance.

2019-06-10T19:58:15.082300Z

So while it isn't specific to promise chan technically, the fact that it can basically get "stuck" doesn't really occur with any other buffer implementation I've seen.

ghadi 2019-06-10T20:54:48.083400Z

cljs.user=> (dotimes [i 10] (println (clojure.core.async/random-array 2)))
WARNING: var: cljs.core.async/random-array is not public at line 1 <cljs repl>
#js [1 0]
#js [1 0]
#js [1 0]
#js [1 0]
#js [1 0]
#js [1 0]
#js [1 0]
#js [1 0]
#js [1 0]
#js [1 0]
@pmooser @hiredman I had a feeling we didn't have all the facts straight. alts! is broken in the 2 channel but not in the three+ channel case

ghadi 2019-06-10T20:55:40.083600Z

cljs.user=> (dotimes [i 10] (println (clojure.core.async/random-array 3)))
WARNING: var: cljs.core.async/random-array is not public at line 1 <cljs repl>
#js [1 2 0]
#js [2 0 1]
#js [2 0 1]
#js [1 2 0]
#js [1 2 0]
#js [1 2 0]
#js [2 0 1]
#js [1 2 0]
#js [2 0 1]
#js [2 0 1]

ghadi 2019-06-10T20:57:47.084100Z

thanks for the report @pmooser, we'll get that fixed

2019-06-10T21:02:54.084400Z

@ghadi Really? That would be great!! Thank you.

ghadi 2019-06-10T21:03:10.084700Z

so it has nothing to do with the channels 🙂

ghadi 2019-06-10T21:03:20.085100Z

it's alts! always trying in the same order

2019-06-10T21:03:39.085700Z

Yeah, in my case, it wasn't just two chans, but the other chan had no values

ghadi 2019-06-10T21:03:42.086Z

even the 3+ channel case it looks like channel 1 is never tried first

2019-06-10T21:03:54.086400Z

But I am glad to hear that it is something understandable and that you think it might even get fixed!!