Fork me on GitHub
#core-async
<
2017-02-07
>
bcbradley15:02:57

I think i managed to make a clean version of throttle-chan that doesn't need more than one go-loop and doesn't use atoms

bcbradley15:02:02

(defn- throttle-chan [in-chan out-chan valve-chan]
  (async/go-loop [allowance 0]
    (if (< 0 allowance)
      (do (async/>! out-chan (async/<! in-chan))
        (recur (reduce (fn [val f] (f val)) allowance (take-while (complement nil?) (repeatedly (fn [] (async/poll! valve-chan)))))))
      (let [f (async/<! valve-chan)]
        (recur (reduce (fn [val f] (f val)) (f allowance) (take-while (complement nil?) (repeatedly (fn [] (async/poll! valve-chan))))))))))

dergutemoritz15:02:57

@bcbradley Doesn't this have the issue of out-chan potentially blocking valve-chan?

dergutemoritz15:02:24

Even in-chan can block valve-chan in this version AFAICT

dergutemoritz15:02:34

You need to include all your takes and puts in an alt! or alts! (or perhaps you can even work something out with poll!) to prevent that

bcbradley15:02:43

@dergutemoritz i've thought about it for a while and came to the conclusion that because you must "hold" the value you get from the in-chan before sending it off to the out-chan

bcbradley15:02:02

and because during that "hold" you could potentially receive a value from valve-chan unless you block it,

bcbradley15:02:13

and because that value could assign 0 to allowance

bcbradley15:02:24

it isn't logically self-consistent

bcbradley15:02:44

what would you do with the value you held if you are told from the change in allowance that you aren't allowed to push it to the out-chan?

bcbradley15:02:51

do you put it back on the in-chan?

bcbradley15:02:03

thats potentially a way to go, but it mixes up the order of stuff

bcbradley15:02:45

basically, if you want to preserve the order, and you don't want to allow the valve chan to invalidate the thing you "hold"

bcbradley15:02:50

then you have to block the valve chan while you hold it

bcbradley15:02:56

thats the only way

bcbradley15:02:52

if there were a way to forcibly push something backwards into a channel, it might be possible to rewind without reordering

dergutemoritz15:02:54

OK if that's fine then I'm sure your function can be simplified 🙂

bcbradley15:02:48

i mean if you've got some insight i don't please share it

bcbradley15:02:02

i never imagined throttling would be such a deep rabbit hole

dergutemoritz15:02:34

Well yeah, if an allowance change comes in while you wait for out-chan to become available but you've already taken a value from in-chan, the semantics are a bit hazy indeed 🙂

dergutemoritz15:02:44

Not sure if blocking in this case is ideal either, though

bcbradley15:02:09

i'm considering not blocking but not preserving order either

bcbradley15:02:12

i don't know how i feel about that

bcbradley15:02:24

i don't feel like a throttle should be mucking with the order

dergutemoritz15:02:40

What order exactly would be mucked with, though?

dergutemoritz15:02:56

You would check the allowance after taking but before waiting for the put

bcbradley15:02:23

and if the allowance became zero while you were taking

bcbradley15:02:28

then you'd put it back on the in-chan

bcbradley15:02:31

ready to take again later

dergutemoritz15:02:39

If an allowance change comes in then, you would potentially put something at a point in time where the already taken value would not be allowed anymore but you would still put it into out-chan before any next value that comes through in-chan

dergutemoritz15:02:05

Oh I thought you threw away values which were taken while allowance is 0

bcbradley15:02:29

the allowance is basically how many values you will allow to go from in-chan to out-chan

dergutemoritz15:02:37

So you don't want to lose values, yeah?

bcbradley15:02:45

right no loss

bcbradley15:02:04

the valve-chan is a way of modifying the allowance

bcbradley15:02:34

the issue is that someone could set the allowance to zero through the valve-chan, and expect it to freeze the flow of stuff from in-chan to out-chan

bcbradley15:02:43

however, actually implementing a perfect freeze is hard

bcbradley16:02:10

if you are in a state where you already took from in-chan and are ready to put to out-chan, thats where the rabbit hole starts

bcbradley16:02:01

after working with different solutions for a couple days I finally came to the conclusion that the issue is my assumptions

bcbradley16:02:16

i assumed that the valve-chan, in-chan, and out-chan shouldn't block eachother

bcbradley16:02:23

or atleast shouldn't have to

bcbradley16:02:28

i think i was wrong to make that assumption

bcbradley16:02:47

it seems making that assumption isn't logically consistent with the semantics of a throttle

bcbradley16:02:17

a throttle doesn't know anything about data being "half" commuted

dergutemoritz16:02:24

Yeah makes sense

dergutemoritz16:02:46

These things can be really tricky, indeed

bcbradley16:02:10

well atleast i'm happy with my current solution

bcbradley16:02:33

one go-loop, no atoms, pretty simple

bcbradley16:02:20

it also doesn't spin

bcbradley16:02:27

that the big thing i was trying to avoid

dergutemoritz16:02:36

BTW, both your and mine don't currently properly handle closing of any of the channels involved

dergutemoritz16:02:27

In fact, my version is a bit different: it will also allow changing the allowance when it's > 0 while nothing is coming in through in-chan