Closed zepumph closed 3 months ago
This issue is primarily to determine if it is worth fixing how TinyEmitter notifies across the project. When you apply the above patch, changing TinyEmitter to "breadth-first" notifying, then we immediately ran into two infinite loops. We should investigate these to determine how much energy it may be to tackle this generally in axon. Remember to apply the above TinyEmitter patch before reproducing the below:
Problem 1 - "stringTest=dynamic" infinite loop (originally reported in https://github.com/phetsims/buoyancy/issues/67#issuecomment-1736350681)
?stringTest=dynamic
Problem 2 - "maxWidth" infinite loop (https://github.com/phetsims/buoyancy/issues/67#issuecomment-1738104059)
Ok, I cleaned up the patch in the first comment and added doc and TODOs. This will be a good starting point for our conversation.
Lots of great progress here. @jonathanolson and I fixed up the TinyEmitter changes and investigated the infinite loops. Here are the details:
listenerArray
logic was causing listeners to sometimes be called more than once per emit (oops). That fixed the dynamicLocale infinite loop reported above.In my opinion this patch is ready to commit, but I didn't want to do this on a Friday afternoon, and I'd like @jonathanolson to take another look before committing. I'm sure that CT will find some more trouble, it will just be a matter of handling things as they come up (since we can't locally reproduce all CT testing).
I'm also marking as high priority because @jonathanolson and I are both excited for this work, and I would hate for it to lose momentum so close to publication.
After talking with @jonathanolson, we feel like it is best that "depth" first be the default strategy for TinyEmitter(), and that "breadth" first be the default strategy for TinyProperty (since the state about value applies a more intuitive default behavior). So we propose making an option:
reentrantNotificationStrategy: "queue" | "stack"
.
Here is a good patch that improves on a few things, since may often run into the situation where our emitContext
already is starting with a listenerArray (which is different from how main is working right now).
OK. Updates here:
Ok. I couldn't figure out the Keplers laws recursion, and the hookes law was causing a reentrancy assertion which makes sense. Here is a patch for investigating both. I'd like to investigate and ask questions with @jonathanolson if he is available and interested.
Lots of good progress here today. I fixed the infinite loops from hookes law and keplers laws with help from @pixelzoom and @AgustinVallejo. @jonathanolson and I were able to discuss the TinyEmitter changes and are feeling quite good. From here we would like to do two things:
We had a meeting today that went well. Before moving to main I will make sure to another full round of fuzz testing and also snapshot comparison (to help prevent view-related differences). Over to @jonathanolson to code review before continuing. I'm hoping to be able to get back to this Friday. @jonathanolson can you review this by the end of Thursday?
Implemented EmitContext pooling (hah, sims aren't creating more than 10-15 of these, and I think I cut two different places we were creating array copies).
Added review comments and documentation. @zepumph let me know how it looks? This looks like it's in a good state.
I found two more infinite loops in calculus grapher and bending light. It may have to do with recent shapeProperty work. I'm still investigating.
The bending light one is not an infinite loop. I have found that propagateTheRay
can be called recursively thousands of times depending on certain cases, this patch helped me see that this behavior is also in the "stack only" branch too:
Again for calculus grapher it wan't actually an infinite loop, it was just a large amount of lag in recalculating the curve while fuzzing. I was able to witness this in both stack and queue based branches.
@jonathanolson went over the remaining thoughts about review comments. From here I will:
I factored out where the loop logic is going, but since sometimes the list is a set, and sometimes it is a list, I don't really think we can always have a list and swap things out. Or, we could, but coercing the set into a list for every emit call seems much worse for performance than the index storage on EmitContext. @jonathanolson can you think of a better way?
Snapshot comparison showed a couple sims that were quite different behaviorally from the queue-based change:
To solve them, I'm inclined to try to fix by switching things back to stack instead of trying to find them. But I haven't investigated it at all. I also don't know if this is even a problem. It will need some time.
For CAV. I was able to reproduce this without the reentrant cases, so ignore that one.
For Natural Selection, In >500 snapshots, I wasn't able to get an assertion with this patch. That said, I also want to note that I can see that this can happen in the frame that doesn't have my changes, so I don't believe that specific piece is caused by the TinyEmitter changes.
Hmmm. I'm actually seeing that it is quite possible to fail the snapshot comparison by just testing main/ vs main/ using the multi snapshot comparison tool in firefox. I'm running through all phet sims now to see if there is a pattern to the type of sims in which this can occur. Perhaps we are closer to merging to main than I thought.
I haven't been back over here for two weeks because I have been so stumped about how to proceed with the snapshot comparison errors I got in https://github.com/phetsims/axon/issues/447#issuecomment-1965693132. It turns out that this was an error with webGL updates during snapshot comparison, and not with our branch. The only snapshot difference I see is in hookes law, which makes sense because we had to solve an infinite loop in that sim, but the image is not noticibly different.
From here we are ready to merge to main/. I will likely do that on monday.
Alright. This was pushed to main this morning. I'll keep an eye on CT.
I didn't find any infinite loops on CT this morning, after a full night. I'm ready to close this issue, and handle any other problems in side issues. Closing
From https://github.com/phetsims/buoyancy/issues/67.
Over there we found a case in Buoyancy where changing a DynamicProperty such that it reentrantly changes itself again can lead to a listener order in which you cannot depend on the "oldValue". We were able to write unit tests that show this issue. Basically in a reentrant case where we increment a counter on each emit callback, we notify on the last change first, so the listener counts backwards instead of forwards (as you'd expect).
Taking bits and pieces from that issue, here are unit tests that demonstrate that problem:
The patch in https://github.com/phetsims/buoyancy/issues/67#issuecomment-1738104059 has a first working prototype of updating TinyEmitter to work as a "bread first" notification strategy instead of what we currently have (depth first):