For day 7, part 2, I used coroutines with `Channel...
# advent-of-code
r
For day 7, part 2, I used coroutines with `Channel`s to send I/O around the amps. I have one coroutine for each amp, as well as the system I/O (initial inputs, amp E output). However, I get indeterministic results if I run the coroutines multi-threaded -- using a single-threaded dispatcher works fine. Did anyone else have a similar approach and run into this?
k
Are you sharing the channel between runs?
Are you running the runs parallel or only the computers inside a run?
r
The runs are in parallel (and this works fine). The computers inside a run have their own non-shared channels.
That is to say: they share channels with each other, but never across runs -- each system of amplifiers and channels is independent with no shared memory (as far as I know) with any other system.
k
If multithreaded is messing something up, then there is something being shared between that shouldn't.
r
Indeed, but its not across systems, because running the calculations for each run in parallel works fine. Its something between the different coroutines of an individual run.
k
Wanna share your code?
k
Ok i see what the issue is
I think removing the launch here might do the trick
It might be causing the amplifiers to receive the input from the previous amp before their phase setting
r
Ah, good call. Will try to update.
Well interestingly fixing it is a bit tricky. In order to send the phase before the computers are running (and therefore reaching a state where they are listening for input), the input channel has to have a buffer. But this seems to introduce other issues (like deadlocks).
I could send the phase as part of constructing the computer but that's kind of inelegant -- it couples the computer implementation to this phase concept
k
Mmh, move the initial 0 sending to after the phase inputting loop
potentially in a
launch
r
The input.send(phase) blocks because those computers are not running / looking for inputs yet
I need to run the computers first so that they are listening for input, then send the phases and wait until that is done before sending the 0 input
k
Doesn't line 39 till 47 start the computers?
r
Yup, so basically do exactly as before, but with a
join
on the phase sending launches before sending the 0 input, exactly
k
isn't
launch{/*code*/}.join()
just the same as just running the code
r
No
launch
on its own doesn't suspend
k
I know
r
So why would you think it is the same as
launch { }.join()
?
k
No, i mean that if you also remove the
launch
r
Right, makes sense. No, it still isn't working. Still hanging somewhere.
k
mmh
r
Ok so that approach does work, but only if I don't have the systems running in parallel
Weird
Might be a race between amp A's input being closed, and attempting to send it the output of amp E. And the race is more likely to trigger when lots of context switching is happening.
j
Haven't look at your code properly yet, but if you want you can take a peek at my solution which is similar to yours - each computer launch as a separate coroutine, running in a multi-threaded Dispatcher without issues: https://github.com/jorispz/aoc-2019/blob/master/src/commonMain/kotlin/day07/p07.kt Note that my Channels have buffers so I can send the phases and the first input before even starting the computers. But this could be done later too, like you said above these computers should simply sit there and wait for input if there isn't any available.
r
Ah, you use a broadcast channel for the output, clever -- that would solve the race condition I think