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

TinyEmitter.emit should assert that it isn't disposed #242

Closed
7 tasks done
zepumph opened this issue Apr 18, 2019 · 21 comments
Closed
7 tasks done

TinyEmitter.emit should assert that it isn't disposed #242

zepumph opened this issue Apr 18, 2019 · 21 comments

Comments

@zepumph
Copy link
Member

zepumph commented Apr 18, 2019

From @pixelzoom review in #222, it would be nice to add this assertion to TinyEmitter.emit().
assert && assert( !this.isDisposed, 'should not be called if disposed' );

When I do this, a number of sims fail:

  • circuit-construction-kit-dc-virtual-lab
  • equality-explorer
  • equality-explorer-two-variables
  • fractions-equality
  • fractions-intro
  • fractions-mixed-numbers
  • unit-rates

These don't always appear, so there may be more. For example CCK took a few minutes of fuzzing to fail, but then it failed with:

Uncaught Error: Assertion failed: should not be called if disposed
Error: Assertion failed: should not be called if disposed
    at window.assertions.assertFunction (http://localhost/assert/js/assert.js:22:13)
    at TinyEmitter.emit (http://localhost/axon/js/TinyEmitter.js?bust=1555625285992:50:17)
    at BooleanProperty._notifyListeners (http://localhost/axon/js/Property.js?bust=1555625285992:247:27)
    at BooleanProperty.set (http://localhost/axon/js/Property.js?bust=1555625285992:160:14)
    at TinyEmitter.emit (http://localhost/axon/js/TinyEmitter.js?bust=1555625285992:59:55)
    at BooleanProperty._notifyListeners (http://localhost/axon/js/Property.js?bust=1555625285992:247:27)
    at BooleanProperty.set (http://localhost/axon/js/Property.js?bust=1555625285992:160:14)
    at BooleanProperty.set value [as value] (http://localhost/axon/js/Property.js?bust=1555625285992:316:34)
    at PressListener.invalidateOver (http://localhost/scenery/js/listeners/PressListener.js?bust=1555625285992:449:33)
    at TinyEmitter.emit (http://localhost/axon/js/TinyEmitter.js?bust=1555625285992:59:55)

I'm pretty sure that this is the sort of assertion that we would want to have in TinyEmitter, but I don't want to add this until these usages are fixed, and it's not obvious to me how these should be fixed. Marking for dev meeting to see if we want to fix these, and how to go about doing it.

UPDATE:
Adding on to this issue, it seems like we should also add something like the following in dispose:
assert && assert( this.activeListenersStack.length === 0, 'should not dispose an emitter while firing its listeners' );

@jonathanolson could you review the two proposed lines of code and let me know what you think.

@samreid
Copy link
Member

samreid commented Apr 25, 2019

This can be quickly triggered in CCK by dragging a wire from the carousel, then selecting the wire and pressing the trash can button at the bottom of the screen. I would like to understand what is happening better before commenting on whether we can proceed with this assertion.

@oliver-phet
Copy link

@pixelzoom will investigate his sims and report back

@pixelzoom
Copy link
Contributor

pixelzoom commented May 13, 2019

To reproduce manually in unit-rates:

  1. Start the sim.
  2. Go to Shopping screen.
  3. Press any one of the yellow "edit" buttons (pencil icon) that opens a keypad.
  4. Press the "Enter" button on the keypad.

Stack trace:

window.assertions.assertFunction (assert.js:22)
emit (TinyEmitter.js?bust=1557772523274:50)
_notifyListeners (Property.js?bust=1557772523274:247)
set (Property.js?bust=1557772523274:160)
emit (TinyEmitter.js?bust=1557772523274:59)
_notifyListeners (Property.js?bust=1557772523274:247)
set (Property.js?bust=1557772523274:160)
set value (Property.js?bust=1557772523274:316)
invalidateOver (PressListener.js?bust=1557772523274:449)
emit (TinyEmitter.js?bust=1557772523274:59)
_notifyListeners (Property.js?bust=1557772523274:247)
set (Property.js?bust=1557772523274:160)
set (NumberProperty.js?bust=1557772523274:87)
pop (ObservableArray.js?bust=1557772523274:242)
clear (ObservableArray.js?bust=1557772523274:298)
👉dispose (PressListener.js?bust=1557772523274:754)
👉RectangularButtonView.disposeRectangularButtonView (RectangularButtonView.js?bust=1557772523274:197)
dispose (RectangularButtonView.js?bust=1557772523274:605)
dispose (RectangularPushButton.js?bust=1557772523274:64)
PhetioObject.dispose (PhetioObject.js?bust=1557772523274:144)
KeypadPanel.disposeKeypadPanel (KeypadPanel.js?bust=1557772523274:112)
dispose (KeypadPanel.js?bust=1557772523274:194)
PhetioObject.dispose (PhetioObject.js?bust=1557772523274:144)
endEdit (KeypadLayer.js?bust=1557772523274:117)
cancelEdit (KeypadLayer.js?bust=1557772523274:145)
commitEdit (KeypadLayer.js?bust=1557772523274:139)
emit (TinyEmitter.js?bust=1557772523274:59)
(anonymous) (Emitter.js?bust=1557772523274:46)
execute (Action.js?bust=1557772523274:177)
emit (Emitter.js?bust=1557772523274:61)
fire (PushButtonModel.js?bust=1557772523274:155)
downPropertyObserver (PushButtonModel.js?bust=1557772523274:88)
emit (TinyEmitter.js?bust=1557772523274:59)
_notifyListeners (Property.js?bust=1557772523274:247)
set (Property.js?bust=1557772523274:160)
emit (TinyEmitter.js?bust=1557772523274:59)
_notifyListeners (Property.js?bust=1557772523274:247)
set (Property.js?bust=1557772523274:160)
set value (Property.js?bust=1557772523274:316)
onRelease (PressListener.js?bust=1557772523274:526)
execute (Action.js?bust=1557772523274:177)
release (PressListener.js?bust=1557772523274:389)
pointerUp (PressListener.js?bust=1557772523274:606)
dispatchToListeners (Input.js?bust=1557772523274:1749)
dispatchEvent (Input.js?bust=1557772523274:1705)
upEvent (Input.js?bust=1557772523274:1466)
Input.mouseUpAction.Action.phetioPlayback (Input.js?bust=1557772523274:247)
execute (Action.js?bust=1557772523274:177)
mouseUp (Input.js?bust=1557772523274:1056)
pointerUp (Input.js?bust=1557772523274:1310)
run (BatchedDOMEvent.js?bust=1557772523274:58)
fireBatchedEvents (Input.js?bust=1557772523274:837)
batchEvent (Input.js?bust=1557772523274:798)
batchWindowEvent (BrowserEvents.js?bust=1557772523274:323)
onpointerup (BrowserEvents.js?bust=1557772523274:360)

@pixelzoom
Copy link
Contributor

pixelzoom commented May 13, 2019

To reproduce manually in equality-explorer:

  1. Start the sim
  2. Go to Basics screen
  3. Put one item on the scale
  4. Press the yellow "clear scale" button (eraser icon) on the scale.

Stack trace:

window.assertions.assertFunction (assert.js:22)
emit (TinyEmitter.js?bust=1557772387340:50)
_notifyListeners (Property.js?bust=1557772387340:247)
set (Property.js?bust=1557772387340:160)
set value (Property.js?bust=1557772387340:316)
removeTermFromPlate (TermCreator.js?bust=1557772387340:403)
unmanageTerm (TermCreator.js?bust=1557772387340:342)
emit (TinyEmitter.js?bust=1557772387340:59)
(anonymous) (Emitter.js?bust=1557772387340:46)
execute (Action.js?bust=1557772387340:177)
emit (Emitter.js?bust=1557772387340:61)
dispose (EqualityExplorerMovable.js?bust=1557772387340:94)
dispose (Term.js?bust=1557772387340:97)
disposeTerms (TermCreator.js?bust=1557772387340:500)
disposeTermsOnPlate (TermCreator.js?bust=1557772387340:478)
(anonymous) (BalanceScale.js?bust=1557772387340:188)
clear (BalanceScale.js?bust=1557772387340:187)
ClearScaleButton.options.listener (ClearScaleButton.js?bust=1557772387340:33)
emit (TinyEmitter.js?bust=1557772387340:59)
(anonymous) (Emitter.js?bust=1557772387340:46)
execute (Action.js?bust=1557772387340:177)
emit (Emitter.js?bust=1557772387340:61)
fire (PushButtonModel.js?bust=1557772387340:155)
👉downPropertyObserver (PushButtonModel.js?bust=1557772387340:88)
emit (TinyEmitter.js?bust=1557772387340:59)
_notifyListeners (Property.js?bust=1557772387340:247)
set (Property.js?bust=1557772387340:160)
emit (TinyEmitter.js?bust=1557772387340:59)
_notifyListeners (Property.js?bust=1557772387340:247)
set (Property.js?bust=1557772387340:160)
set value (Property.js?bust=1557772387340:316)
👉onRelease (PressListener.js?bust=1557772387340:526)
execute (Action.js?bust=1557772387340:177)
release (PressListener.js?bust=1557772387340:389)
pointerUp (PressListener.js?bust=1557772387340:606)
dispatchToListeners (Input.js?bust=1557772387340:1749)
dispatchEvent (Input.js?bust=1557772387340:1705)
upEvent (Input.js?bust=1557772387340:1466)
Input.mouseUpAction.Action.phetioPlayback (Input.js?bust=1557772387340:247)
execute (Action.js?bust=1557772387340:177)
mouseUp (Input.js?bust=1557772387340:1056)
pointerUp (Input.js?bust=1557772387340:1310)
run (BatchedDOMEvent.js?bust=1557772387340:58)
fireBatchedEvents (Input.js?bust=1557772387340:837)
batchEvent (Input.js?bust=1557772387340:798)
batchWindowEvent (BrowserEvents.js?bust=1557772387340:323)
onpointerup (BrowserEvents.js?bust=1557772387340:360)

@pixelzoom
Copy link
Contributor

pixelzoom commented May 13, 2019

To reproduce manually in fractions-intro:

  1. Start the sim
  2. Go to Intro screen
  3. Click any of the radio button at top-center of the screen

Stack trace:

window.assertions.assertFunction (assert.js:22)
emit (TinyEmitter.js?bust=1557772991134:50)
_notifyListeners (Property.js?bust=1557772991134:247)
set (Property.js?bust=1557772991134:160)
listener (DerivedProperty.js?bust=1557772991134:61)
emit (TinyEmitter.js?bust=1557772991134:59)
_notifyListeners (Property.js?bust=1557772991134:247)
set (Property.js?bust=1557772991134:160)
(anonymous) (RadioButtonGroupMember.js?bust=1557772991134:125)
emit (TinyEmitter.js?bust=1557772991134:59)
(anonymous) (Emitter.js?bust=1557772991134:46)
execute (Action.js?bust=1557772991134:177)
emit (Emitter.js?bust=1557772991134:61)
fire (RadioButtonGroupMember.js?bust=1557772991134:146)
👉(anonymous) (RadioButtonGroupMember.js?bust=1557772991134:87)
emit (TinyEmitter.js?bust=1557772991134:59)
_notifyListeners (Property.js?bust=1557772991134:247)
set (Property.js?bust=1557772991134:160)
emit (TinyEmitter.js?bust=1557772991134:59)
_notifyListeners (Property.js?bust=1557772991134:247)
set (Property.js?bust=1557772991134:160)
set value (Property.js?bust=1557772991134:316)
👉onRelease (PressListener.js?bust=1557772991134:526)
execute (Action.js?bust=1557772991134:177)
release (PressListener.js?bust=1557772991134:389)
pointerUp (PressListener.js?bust=1557772991134:606)
dispatchToListeners (Input.js?bust=1557772991134:1749)
dispatchEvent (Input.js?bust=1557772991134:1705)
upEvent (Input.js?bust=1557772991134:1466)
Input.mouseUpAction.Action.phetioPlayback (Input.js?bust=1557772991134:247)
execute (Action.js?bust=1557772991134:177)
mouseUp (Input.js?bust=1557772991134:1056)
pointerUp (Input.js?bust=1557772991134:1310)
run (BatchedDOMEvent.js?bust=1557772991134:58)
fireBatchedEvents (Input.js?bust=1557772991134:837)
batchEvent (Input.js?bust=1557772991134:798)
batchWindowEvent (BrowserEvents.js?bust=1557772991134:323)
onpointerup (BrowserEvents.js?bust=1557772991134:360)

@pixelzoom
Copy link
Contributor

pixelzoom commented May 13, 2019

I could not manually reproduce with CCK:DCVL. After 5+ minutes with ?fuzz, here's the stack trace:

window.assertions.assertFunction (assert.js:22)
emit (TinyEmitter.js?bust=1557773083017:50)
_notifyListeners (Property.js?bust=1557773083017:247)
set (Property.js?bust=1557773083017:160)
emit (TinyEmitter.js?bust=1557773083017:59)
_notifyListeners (Property.js?bust=1557773083017:247)
set (Property.js?bust=1557773083017:160)
set value (Property.js?bust=1557773083017:316)
invalidateOver (PressListener.js?bust=1557773083017:449)
emit (TinyEmitter.js?bust=1557773083017:59)
_notifyListeners (Property.js?bust=1557773083017:247)
set (Property.js?bust=1557773083017:160)
set (NumberProperty.js?bust=1557773083017:87)
pop (ObservableArray.js?bust=1557773083017:242)
clear (ObservableArray.js?bust=1557773083017:298)
👉dispose (PressListener.js?bust=1557773083017:754)
👉RoundButtonView.disposeRoundButtonView (RoundButtonView.js?bust=1557773083017:163)
dispose (RoundButtonView.js?bust=1557773083017:514)
dispose (RoundPushButton.js?bust=1557773083017:66)
PhetioObject.dispose (PhetioObject.js?bust=1557773083017:144)
(anonymous) (CircuitElementEditContainerNode.js?bust=1557773083017:98)
emit (TinyEmitter.js?bust=1557773083017:59)
_notifyListeners (Property.js?bust=1557773083017:247)
set (Property.js?bust=1557773083017:160)
(anonymous) (Circuit.js?bust=1557773083017:137)
_fireItemRemoved (ObservableArray.js?bust=1557773083017:162)
remove (ObservableArray.js?bust=1557773083017:202)
listener (TrashButton.js?bust=1557773083017:36)
emit (TinyEmitter.js?bust=1557773083017:59)
(anonymous) (Emitter.js?bust=1557773083017:46)
execute (Action.js?bust=1557773083017:177)
emit (Emitter.js?bust=1557773083017:61)
fire (PushButtonModel.js?bust=1557773083017:155)
downPropertyObserver (PushButtonModel.js?bust=1557773083017:88)
emit (TinyEmitter.js?bust=1557773083017:59)
_notifyListeners (Property.js?bust=1557773083017:247)
set (Property.js?bust=1557773083017:160)
emit (TinyEmitter.js?bust=1557773083017:59)
_notifyListeners (Property.js?bust=1557773083017:247)
set (Property.js?bust=1557773083017:160)
set value (Property.js?bust=1557773083017:316)
onRelease (PressListener.js?bust=1557773083017:526)
execute (Action.js?bust=1557773083017:177)
release (PressListener.js?bust=1557773083017:389)
pointerUp (PressListener.js?bust=1557773083017:606)
dispatchToListeners (Input.js?bust=1557773083017:1749)
dispatchEvent (Input.js?bust=1557773083017:1705)
upEvent (Input.js?bust=1557773083017:1466)
Input.touchEndedAction.Action.phetioPlayback (Input.js?bust=1557773083017:375)
execute (Action.js?bust=1557773083017:177)
touchEnd (Input.js?bust=1557773083017:1150)
touchEnd (InputFuzzer.js?bust=1557773083017:246)
InputFuzzer.touchEndAction (InputFuzzer.js?bust=1557773083017:61)
fuzzEvents (InputFuzzer.js?bust=1557773083017:114)
(anonymous) (Sim.js?bust=1557773083017:187)
execute (Action.js?bust=1557773083017:177)
stepSimulation (Sim.js?bust=1557773083017:931)
stepOneFrame (Sim.js?bust=1557773083017:912)
runAnimationLoop (Sim.js?bust=1557773083017:895)
requestAnimationFrame (async)

@pixelzoom
Copy link
Contributor

pixelzoom commented May 13, 2019

All of the above stack traces involve a sun button and SCENERY/PressListener (see 👉 annotations in stack traces). So I don't think we have a sim-specific problem here.

@ariel-phet I think it would be most efficient/effective to have either @jbphet or @jonathanolson pick up the investigation from here.

@pixelzoom
Copy link
Contributor

5/16/19 dev meeting:

@samreid pointed out that this seems to happen when clicking a button causes it to dispose itself.

We'll wait for @ariel-phet to assign.

@jbphet
Copy link
Contributor

jbphet commented May 29, 2019

@samreid pointed out that this seems to happen when clicking a button causes it to dispose itself.

This is true in some cases, not in others. I dug into it, and found that for two of the cases, the problem is that the PressListener instance is trying to set the state of a ButtonModel instance when it is being disposed because the PressListener is clearing the list of pointers that are over the node as part of the disposal process. This leads it to say, "Hey, if there are no pointers over me, I need to update my isOverProperty to false, which in turn tries to update the overProperty of the button model, which has been disposed by then, and the assertion is hit. I found that reversing the order of the calls in the dispose function in RectangularPushButton and RoundPushButton appears to fix the issue.

But...this only fixed things for CCK and Unit Rates. The other sims had different issues.

I investigated for Equality Explorer, and the problem in this case is that TermCreator.removeTermFromPlate is trying to set onPlateProperty for a term that has been disposed. I tried changing to code so that it only did this if the term hadn't been disposed, and this seemed to fix the issue.

I looked at the fractions sims, and I wasn't sure what exactly was happening, but it seemed pretty specific to that sim, and not a general issue with the radio buttons.

I think we should decide whether this assertion is something that we really really want and, if so, each of the responsible developers should be assigned (including me) to do their part before adding the assertion, then we turn it on.

In the mean time, I will run a local aqua test to see if reversing the disposal call order can be safely committed and, if so, I'll do it.

@jbphet
Copy link
Contributor

jbphet commented May 29, 2019

Marking for dev meeting, assigning to

  • @pixelzoom to see if my suggestion for Equality Explorer in the previous comment is reasonable. It's simple, but I don't know the sim well, and he should be the one to implement it if it's reasonable.
  • @jonathanolson since he is the responsible dev for the fractions sims

@pixelzoom
Copy link
Contributor

pixelzoom commented May 29, 2019

In #242 (comment), @jbphet said:

I investigated for Equality Explorer, and the problem in this case is that TermCreator.removeTermFromPlate is trying to set onPlateProperty for a term that has been disposed. I tried changing to code so that it only did this if the term hadn't been disposed, and this seemed to fix the issue.

In #242 (comment), @jbphet said:

Marking for dev meeting, assigning to

  • @pixelzoom to see if my suggestion for Equality Explorer in the previous comment is reasonable. It's simple, but I don't know the sim well, and he should be the one to implement it if it's reasonable.

It sounds like you are referring to the code below. Please provide details on why setting term.onPlateProperty is resulting in this problem, I'm not clear.

    removeTermFromPlate: function( term ) {
      assert && assert( this.allTerms.contains( term ), 'term not found: ' + term );
      assert && assert( this.termsOnPlate.contains( term ), 'term not on plate: ' + term );

      // ORDER IS VERY IMPORTANT HERE!
      var cell = this.plate.removeTerm( term );
      this.termsOnPlate.remove( term );
403   term.onPlateProperty.value = false;
      return cell;
    },

@pixelzoom pixelzoom assigned pixelzoom and unassigned pixelzoom May 30, 2019
@pixelzoom
Copy link
Contributor

pixelzoom commented May 30, 2019

@jbphet explained equality-explorer to me in person. Term.dispose calls onPlateProperty.dispose. So I need to do this:

403   if ( !term.onPlateProperty.isDisposed ) {
        term.onPlateProperty.value = false;
      }

@zepumph
Copy link
Member Author

zepumph commented May 30, 2019

Discussed in dev meeting today.

We will change the usages stated above like @jbphet recommended, assigning to the appropriate humans.

The last person can either add the assertion in TinyEmitter.emit(), or assign to me to add the dispose method back in.

@samreid can't help but to think that we have pondered this before, and run into an edge case where we don't want a strict assertion like this. For now we will continue, with the caveat that we may bail if we remember a deal breaking edge case.

pixelzoom added a commit to phetsims/equality-explorer that referenced this issue May 30, 2019
@pixelzoom
Copy link
Contributor

pixelzoom commented May 30, 2019

equality-explorer and equality-explorer-two-variables are done.

@pixelzoom pixelzoom removed their assignment May 30, 2019
@jbphet
Copy link
Contributor

jbphet commented May 30, 2019

I committed the changes to the order of disposal actions in the button views, and so far continuous testing seems to be okay. This should fix Unit Rates and CCK. Up to @jonathanolson to track things down for the fractions suite.

@jonathanolson
Copy link
Contributor

Fixed up fractions (and as I was the last, I added the assertion). @zepumph anything else to do here?

@pixelzoom
Copy link
Contributor

pixelzoom commented Jun 5, 2019

Looking at CT like the assertion was commited...

equality-explorer-two-variables is still having problems. gas-properties + gases-intro + diffusion are having problems. Self assigning since those are all sims that I'm responsible for. (@pixelzoom edit: correction, not a problem with gas-properties suite.)

@jbphet gene-expression-essential is also having problems. (@pixelzoom edit: created issue phetsims/gene-expression-essentials#127)

@pixelzoom
Copy link
Contributor

pixelzoom commented Jun 5, 2019

@zepumph I've created sim-specific issue for the sims noted in #242 (comment), so if you want to close this issue, that's fine with me.

@pixelzoom pixelzoom removed their assignment Jun 5, 2019
@zepumph
Copy link
Member Author

zepumph commented Jun 8, 2019

The last piece of this is to actually add the assertion in once everything is done. I'll keep this open to make sure that happens.

@samreid
Copy link
Member

samreid commented Jun 8, 2019

@jonathanolson said in #242 (comment) that he did add the assertion, and @pixelzoom confirmed in #242 (comment). Looking at master, I can see the assertion is still there. @zepumph anything else for this issue?

@zepumph
Copy link
Member Author

zepumph commented Jun 8, 2019

I'm sorry! I found myself glancing at the dispose method instead of the emit method. Ready to close. Thanks your patience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants