Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@vasilmkd
Copy link
Contributor

@vasilmkd vasilmkd commented Jul 24, 2019

Resolves #1203

@vasilmkd
Copy link
Contributor Author

In the current implementation, the Streams produced from each element of the outer stream can enter the "execution window of n parallel streams" completely non-deterministically.

E.g. in Stream(Stream(1), Stream.never).flatMapParSwitch, the never ending stream can enter the execution window before the Stream(1). Is this desired behavior or should the ordering be preserved?

As far as I know, flatMapPar is also non-deterministic in this way.

@iravid
Copy link
Member

iravid commented Jul 24, 2019

Hi @vasilmkd, looks really good from a cursory look. I hope to have more time to dig in tomorrow. Thank you for contributing this so quickly!

Copy link
Member

@iravid iravid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One small comment and this looks good to go. Great work!

@iravid
Copy link
Member

iravid commented Jul 25, 2019

Regarding the ordering: I think it'd be a positive change to move the latch.succeed(()) into flatMap(_ => latch.succeed(()) *> f(a)) (in flatMapPar as well). I wonder if that is enough though; we have to make sure we don't move the latch release too deeply into f(a) or else we risk hangs due to streams like Stream.never.

@vasilmkd
Copy link
Contributor Author

Regarding the ordering: I think it'd be a positive change to move the latch.succeed(()) into flatMap(_ => latch.succeed(()) *> f(a)) (in flatMapPar as well). I wonder if that is enough though; we have to make sure we don't move the latch release too deeply into f(a) or else we risk hangs due to streams like Stream.never.

I made this change, however, I see no difference in behavior.

)
// This finalizer makes sure that in all cases, the driver stops spawning new streams
// and the inner fibers are signalled to interrupt and actually exit.
.ensuring(interruptInners.succeed(()) *> permits.withPermits(n)(ZIO.unit))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be an ensuringFirst, otherwise the finalizer ordering will be wrong; ensuring appends a finalizer so it runs at the end. This means that the finalizer for the driver stream runs first, and only then will the inner fibers be interrupted.

You can actually keep the equivalence to the previous formulation with ZManaged#onExitFirst.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh but the tests are still passing 🤨 Well this is interesting. I'll dig in some more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes. This is probably just racey. There's nothing preventing the inners from completing before the outer in the finalizer ordering test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, that's why we have the n permits semaphore (permits), the driver finalizer kicks off, tells the inner fibers to terminate and waits for their termination (until it succeeds in collecting n permits from the semaphore), at which point all of the inner fibers will have terminated and finalized.

Or am I missing something?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that's exactly it. However, consider that the driver stream has its own set of finalizers (from self) that need to run; for example, the stream you're flatMapping might have acquired a resource that the inner fibers are using.

When you do ensuring, the finalizer passed to ensuring is appended to the list of finalizers, not prepended. So the resulting set of finalizers for the driver stream is:

selfFinalizer *> interruptInners.succeed(()) *> permits.withPermits(n)(ZIO.unit)

Which means that the resources acquired in self would be released before the inner fibers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So just change ensuring to ensuringFirst? Both here and in flatMapPar right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd go with .fork.onExitFirst(...), and interrupt the driver fiber before interruptInners.succeed(()) (if it is Exit.Success).

Copy link
Contributor Author

@vasilmkd vasilmkd Jul 26, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I may be wrong here, but the driver fiber is not available in its own definition block. Doesn't fork on a ZManaged, when exiting, interrupt the fiber itself, then run all of the finalizers?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, that won't be possible with ensuringFirst, but onExitFirst lets you access the result. But I think you're right with your note about the interruption of the fiber, so ensuringFirst is actually sufficient. Thanks for being extra attentive here :-)

@iravid
Copy link
Member

iravid commented Jul 26, 2019

@vasilmkd Let's deal with the execution order issue you've noticed separately. Can you open an issue that describes what you're seeing?

@vasilmkd
Copy link
Contributor Author

I will, let me finish up the other PRs first. I think I have some sample code too.

@vasilmkd
Copy link
Contributor Author

@iravid This is also ready for review.

Copy link
Member

@iravid iravid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great. Superb work. Will merge once CI is done.

@iravid iravid merged commit a717176 into zio:master Jul 26, 2019
@vasilmkd vasilmkd deleted the flatmap-par-switch branch July 26, 2019 11:25
ghostdogpr pushed a commit to ghostdogpr/scalaz-zio that referenced this pull request Aug 5, 2019
* Implement ZStream#flatMapParSwitch

* Test ZStream#flatMapParSwitch

* Change finalizer code ZStream#flatMapParSwitch

* Change finalizer code ZStream#flatMapPar

* Release the latch slightly later ZStream#flatMapPar

* Fix finalizer execution order
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ZStream#flatMapParSwitch

2 participants