Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport fix made in #8765, improve readibility and optimize FiberRefs stack implementation #8779

Draft
wants to merge 19 commits into
base: series/2.x
Choose a base branch
from

Conversation

Copy link
Contributor

@kyri-petrou kyri-petrou left a comment

Choose a reason for hiding this comment

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

Maybe I'm too early to review when the PR is still in draft, but I was interested and wanted to take a look.

Feel free to ignore my comments if you were planning on doing more changes!


def combine(first: Patch, second: Patch): Patch =
override final def combine(first: Patch, second: Patch): Patch =
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we just create a final private class FiberRefPatch for this?

Personally I find reading stack traces & profiling data a lot more annoying with anonymous classes

Copy link
Member Author

Choose a reason for hiding this comment

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

Personally I find reading stack traces & profiling data a lot more annoying with anonymous classes

Completely agree :)

@@ -27,9 +28,8 @@ import scala.annotation.tailrec
* example between an asynchronous producer and consumer.
*/
final class FiberRefs private (
private[zio] val fiberRefLocals: Map[FiberRef[_], FiberRefs.Value]
private[zio] val fiberRefLocals: Map[FiberRef[_], FiberRefStack[_]]
Copy link
Contributor

Choose a reason for hiding this comment

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

I did a quick search of fiberRefLocals across all zio/* repos and it seems there's no other usage of it. Perhaps we can make it a private val? That means we can also make a lot of things in the companion object private

Copy link
Member Author

Choose a reason for hiding this comment

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

Let's try it. Thanks :)

@@ -248,7 +293,7 @@ object FiberRefs {
* Applies the changes described by this patch to the specified collection
* of `FiberRef` values.
*/
def apply(fiberId: FiberId.Runtime, fiberRefs: FiberRefs): FiberRefs = {
final def apply(fiberId: FiberId.Runtime, fiberRefs: FiberRefs): FiberRefs = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Might be worth for the last line in this method to be changed to this

if (self eq Empty) fiberRefs else loop(fiberRefs, List(self))

core/shared/src/main/scala/zio/FiberRefs.scala Outdated Show resolved Hide resolved
core/shared/src/main/scala/zio/FiberRefs.scala Outdated Show resolved Hide resolved
core/shared/src/main/scala/zio/FiberRefs.scala Outdated Show resolved Hide resolved
core/shared/src/main/scala/zio/FiberRefs.scala Outdated Show resolved Hide resolved
/**
* Add a new entry on top of the Stack
*/
@inline def put(fiberId: FiberId.Runtime, value: Any): FiberRefStack[?] =
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
@inline def put(fiberId: FiberId.Runtime, value: Any): FiberRefStack[?] =
@inline def put(fiberId: FiberId.Runtime, value: A): FiberRefStack[A] =

Copy link
Member Author

Choose a reason for hiding this comment

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

Sadly, this change introduce a compilation error that I don't find how to solve:

[error] /Users/jules/workspace/zio/core/shared/src/main/scala/zio/FiberRefs.scala:72:38: type mismatch;
[error]  found   : newValue.type (with underlying type fiberRef.Value)
[error]  required: _$2 where type _$2
[error]           fiberRefStack.put(childId, newValue)
[error]                                      ^

Any idea? I tried to explicitly cast newValue but it doesn't solve the issue 🤔

Copy link
Member Author

Choose a reason for hiding this comment

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

My bad. Found how:

fiberRefStack.asInstanceOf[FiberRefs.FiberRefStack[fiberRef.Value]].put(childId, newValue)

core/shared/src/main/scala/zio/FiberRefs.scala Outdated Show resolved Hide resolved
core/shared/src/main/scala/zio/FiberRefs.scala Outdated Show resolved Hide resolved
guizmaii and others added 14 commits April 26, 2024 14:04
Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com>
Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com>
Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com>
Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com>
Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com>
Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com>
Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com>
@@ -1,11 +0,0 @@
{
Copy link
Member Author

Choose a reason for hiding this comment

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

TODO @guizmaii: Put back

Value(::(StackEntry(fiberId, value, 0), oldStack), oldDepth + 1)
if (oldStack.fiberId == fiberId) {
if (oldStack.value == value) oldStack else oldStack.updateValue(value)
} else if (oldStack.value == value) oldStack
Copy link
Contributor

Choose a reason for hiding this comment

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

By the way is it just me or should this condition be checked first? It seems we check the same thing in L199 and L200, so there's not really any reason to check oldStack.fiberId == fiberId when oldStack.value == value

@ghostdogpr
Copy link
Member

@guizmaii hey 😄 do you think you'll be able to finish this one soon? I'm thinking of making one last 2.1.0 RC and then a final release from that RC. The question is, do I wait for this to be merged? It might be good to include it if there's a risk of regression or any behavior change. What do you think?

@kyri-petrou
Copy link
Contributor

@guizmaii let me know once you'd like me to review again

@guizmaii
Copy link
Member Author

guizmaii commented May 3, 2024

@ghostdogpr

Quick answer:

do I wait for this to be merged?

No, don't wait 🙂

Long answer:

I'm not able to move forward much on this work because I have some health issues affecting quite negatively my everyday day life 😕
I have surgery planned for the 13th of May to fix that.
So I'll probably not be able to spend the necessary time on this before some time 😕

In this PR, I was exploring 2 things and was planning to explore a third thing later, related to the second thing I was exploring:

  1. The first thing I was exploring was the one explained here: Backport fix made in #8765, improve readibility and optimize FiberRefs stack implementation #8779 (comment)

My plan was to write tests for the #joinAs function to explore and specify the behaviour of the findAncestor inner function.
But right now, I have introduced a ClassCastException: class zio.FiberRefs cannot be cast to class zio.ZEnvironment issue with my changes 🤔
We can see it when running the test in FiberRefsSpec (you can run them with sbt coreTestsJVM/testOnly *.FiberRefsSpec):

[error] Uncaught exception when running tests: Exception in thread "zio-fiber-2109190134,597810767,665984147,1415018693,1289310306,940131919,1789083875,1156723497,270737194,1861754121" java.lang.ClassCastException: class zio.FiberRefs cannot be cast to class zio.ZEnvironment (zio.FiberRefs and zio.ZEnvironment are in unnamed module of loader 'app')
[error] 	at zio.Differ$$anon$5.patch(Differ.scala:146)
[error] 	at zio.FiberRefPatch.patch(FiberRef.scala:328)
[error] 	at zio.FiberRefs.$anonfun$forkAs$1(FiberRefs.scala:69)
[error] 	at scala.collection.immutable.Map$Map4.transform(Map.scala:614)
[error] 	at scala.collection.immutable.Map$Map4.transform(Map.scala:524)
[error] 	at zio.FiberRefs.forkAs(FiberRefs.scala:66)
[error] 	at zio.ZIO$unsafe$.makeChildFiber(ZIO.scala:2603)
[error] 	at zio.ZIO$unsafe$.fork(ZIO.scala:2587)

I didn't have enough time to understand why.

  1. The second thing I was exploring was initiated by the fact that I didn't understand the code

It was too obscure for my small brain to grasp so I tried to understand it and to rewrite it to make it easier for me to understand.
This work is mainly the change of the Value case class to a specialised data structure (FiberRefStack, and its associated methods/functions).
IMO, it makes the code much more explicit about what it's doing.
To give an example of what I think is a readability improvement, you can compare these 3 lines: https://github.com/zio/zio/pull/8779/files#diff-672fc2acd16d75c9b38db294811088d1f64bf8f994a05d7ef31c610e81c8ce1eR169-R171
with the corresponding ones here:
https://github.com/zio/zio/pull/8779/files#diff-672fc2acd16d75c9b38db294811088d1f64bf8f994a05d7ef31c610e81c8ce1eL167-L173

  1. The third thing I was planning to explore is related to the fact that we're constantly comparing "values"

There are 2 sorts of "values" in this code. The initial fiber value (FiberRef#initial) and the stack "values", each stack layer containing, amongst over things, a value.
If I understand things correctly, it seems that the first value of the stack is the same as the initial fiber value.
Therefor, to know if we have an empty stack, we need to compare the FiberRef#initial with the stack head value.
See https://github.com/zio/zio/pull/8779/files#diff-672fc2acd16d75c9b38db294811088d1f64bf8f994a05d7ef31c610e81c8ce1eR148-R149
This could be quite an inefficient mechanism based on what we compare and was planning, later, in another PR, to see if we could find another way to represent things so that we don't need to have these comparisons and to see where it would lead.

Feel free to continue and/or copy my work 🙂
I'll try my best to continue to work on this; particularly on the point 1, the point 2 being IMO addressed and the point 3 being for later; but I can't promised any ETA

@kyri-petrou
Copy link
Contributor

@guizmaii I'm really sorry to hear about your health issues 😔 I really hope the surgery works and you're back in good shape. Wish you all the best man ✌️🙏

@ghostdogpr
Copy link
Member

@guizmaii oh man, sorry to hear that. I hope the surgery goes well 🙏🙏🙏

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.

None yet

3 participants