Skip to content

Conversation

@ryangomba
Copy link
Contributor

This diff attempts to fix a number of iOS native animation bugs related to improper node invalidation and a race with view creation. The major issues were presented in #9120 as problems 3 and 3b, but I'll recap here:

Incomplete node invalidation

The invalidation model we use is overly complicated and incomplete. The proper combination of _needsUpdate and _hasUpdated will result in nodes values being recomputed. However, we do not invalidate nodes in all the places we should, e.g. if we create a new view and attach it to an existing value node (see example in #9120). This diff chooses to remove the _hasUpdated flag, and simply relies on the _needsUpdate flag to mark a node as dirty.

We mark nodes as dirty when they are:

  • created
  • updated
  • attached to new parents
  • detached from old parents
  • attached to a view

Calling updateNodeIfNecessary will, if necessary, compute all invalidated parent values before recomputing the node value. It will then apply the update, and mark the node as clean.

Note: the simplification also allows us to remove the cleanupAnimationUpdate routine.

Race condition on view creation

Sometimes, we would attempt to set native animated props on a non-existent view (see example in #9120). This is because we have a race condition with UIManager: sometimes, we run animated updates before UIManager can create the view. To synchronize RCTUIManager and RCTNativeAnimatedModule, we do the following:

  • place RCTNativeAnimatedModule calls on the UIManager queue
  • gather all native animated operations, but don't apply them
  • wait for a batchDidComplete call to flush the operation queue
  • let RCTNativeAnimatedNodesManager apply the updates on the main queue

This module + nodeManager structure mimics the one on Android, and I think it works quite well.

@facebook-github-bot
Copy link
Contributor

By analyzing the blame information on this pull request, we identified @buba447 and @janicduplessis to be potential reviewers.

@ryangomba
Copy link
Contributor Author

@janicduplessis here it is!

@ryangomba
Copy link
Contributor Author

fwiw, I'm working on a large application that makes extensive use of the NativeAnimated API. This diff fixed a boatload of hard to track down bugs.

@ryangomba ryangomba force-pushed the native-animation-invalidate branch from 216ec54 to a40dca3 Compare October 31, 2016 22:06
@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 31, 2016
@ryangomba ryangomba force-pushed the native-animation-invalidate branch 2 times, most recently from 1dc9155 to b634c97 Compare October 31, 2016 22:26
@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 31, 2016
@janicduplessis janicduplessis self-assigned this Nov 1, 2016
@janicduplessis
Copy link
Contributor

Awesome! Tested it and it does fix the node update issues that I had + the weird race conditions that caused some transitions to sometimes not happen at all. Going to review this tomorrow.

@ryangomba ryangomba force-pushed the native-animation-invalidate branch 2 times, most recently from b89a9c5 to 806bbf0 Compare November 1, 2016 19:23
Copy link
Contributor

@janicduplessis janicduplessis left a comment

Choose a reason for hiding this comment

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

Great work! Tested this quite a bit and it did fix the issues you mentioned.

@@ -0,0 +1,78 @@
//
Copy link
Contributor

Choose a reason for hiding this comment

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

FB copyright header

@@ -0,0 +1,350 @@
//
Copy link
Contributor

Choose a reason for hiding this comment

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

FB copyright header

@janicduplessis
Copy link
Contributor

@facebook-github-bot shipit

@facebook-github-bot facebook-github-bot added GH Review: accepted Import Started This pull request has been imported. This does not imply the PR has been approved. and removed GH Review: review-needed labels Nov 2, 2016
@facebook-github-bot
Copy link
Contributor

I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification.

@facebook-github-bot facebook-github-bot added Import Failed and removed Import Started This pull request has been imported. This does not imply the PR has been approved. labels Nov 3, 2016
@ryangomba
Copy link
Contributor Author

Should we try this again? Any context on the failed import @javache ?

@hramos
Copy link
Contributor

hramos commented Nov 11, 2016

Looking.

@nihgwu
Copy link
Contributor

nihgwu commented Nov 15, 2016

what's going on this?

@janicduplessis
Copy link
Contributor

Ping @hramos

@janicduplessis
Copy link
Contributor

@facebook-github-bot shipit

@facebook-github-bot facebook-github-bot added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Nov 17, 2016
@facebook-github-bot
Copy link
Contributor

I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification.

@facebook-github-bot facebook-github-bot removed the Import Started This pull request has been imported. This does not imply the PR has been approved. label Nov 17, 2016
@nihgwu
Copy link
Contributor

nihgwu commented Nov 21, 2016

why the import failed? is anyone looking into it? I've test this PR with my app, it works so great

@mkonicek
Copy link
Contributor

Looks like the import failed with an unrelated error. Let's try again.

@facebook-github-bot shipit

@facebook-github-bot facebook-github-bot added the Import Started This pull request has been imported. This does not imply the PR has been approved. label Nov 22, 2016
@facebook-github-bot
Copy link
Contributor

I tried to merge this pull request into the Facebook internal repo but some checks failed. To unblock yourself please check the following: Does this pull request pass all open source tests on GitHub? If not please fix those. Does the code still apply cleanly on top of GitHub master? If not can please rebase. In all other cases this means some internal test failed, for example a part of a fb app won't work with this pull request. I've added the Import Failed label to this pull request so it is easy for someone at fb to find the pull request and check what failed. If you don't see anyone comment in a few days feel free to comment mentioning one of the core contributors to the project so they get a notification.

@facebook-github-bot facebook-github-bot removed the Import Started This pull request has been imported. This does not imply the PR has been approved. label Nov 22, 2016
@janicduplessis
Copy link
Contributor

@mkonicek Talked with @hramos and this causes some buck errors internally, someone from fb will have to look at it.

@mkonicek
Copy link
Contributor

mkonicek commented Nov 25, 2016

@janicduplessis @ryangomba I'm not sure it's Buck-related. Here are the errors that blocked this from landing internally. I know little about Obj-C, do they make sense to you?

Libraries/NativeAnimation/RCTNativeAnimatedModule.m:181:17: error: block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior [-Werror,-Wimplicit-retain-self]
operation(_nodesManager);
            ^
Libraries/NativeAnimation/RCTNativeAnimatedModule.m:183:6: error: block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior [-Werror,-Wimplicit-retain-self]
[_nodesManager updateAnimations];

Looks like this PR needs to be rebased too, sorry about that!

@janicduplessis
Copy link
Contributor

@mkonicek Looks like we're compiling with different warning flags internally and in OSS. Are you using buck to build objc internally? If so I guess it has its own config that probably add different compiler warning flags as the xcbuild one. You might want to ping some ios person about it as it is easy to fix and would prevent import failures like this.

@mkonicek
Copy link
Contributor

mkonicek commented Nov 28, 2016

Thanks @janicduplessis! Yes we use Buck. Pinged @mmmulani.

@mkonicek
Copy link
Contributor

mkonicek commented Nov 28, 2016

Is there any way you can make the code compile independently of compiler flags to unblock yourself before @mmmulani has time to look into this?

@ryangomba
Copy link
Contributor Author

I can follow up on this now that I understand the import error

@ryangomba ryangomba force-pushed the native-animation-invalidate branch from 6954ca4 to 0e5ab15 Compare November 28, 2016 17:32
@ryangomba ryangomba force-pushed the native-animation-invalidate branch from 0e5ab15 to ad5d967 Compare November 28, 2016 18:44
robclouth pushed a commit to robclouth/react-native that referenced this pull request Dec 7, 2016
Summary:
This diff attempts to fix a number of iOS native animation bugs related to improper node invalidation and a race with view creation. The major issues were presented in facebook#9120 as problems 3 and 3b, but I'll recap here:

The invalidation model we use is overly complicated and incomplete. The proper combination of `_needsUpdate` and `_hasUpdated` will result in nodes values being recomputed. However, we do not invalidate nodes in all the places we should, e.g. if we create a new view and attach it to an existing value node (see example in facebook#9120). This diff chooses to remove the `_hasUpdated` flag, and simply relies on the `_needsUpdate` flag to mark a node as dirty.

We mark nodes as dirty when they are:
- created
- updated
- attached to new parents
- detached from old parents
- attached to a view

Calling `updateNodeIfNecessary` will, if necessary, compute all invalidated parent values before recomputing the node value. It will then apply the update, and mark the no
Closes facebook#10663

Differential Revision: D4120301

Pulled By: mkonicek

fbshipit-source-id: e247afcb5d8c15999b8328c664b9f7e764d76a75
DanielMSchmidt pushed a commit to DanielMSchmidt/react-native that referenced this pull request Jan 4, 2017
Summary:
This diff attempts to fix a number of iOS native animation bugs related to improper node invalidation and a race with view creation. The major issues were presented in facebook#9120 as problems 3 and 3b, but I'll recap here:

The invalidation model we use is overly complicated and incomplete. The proper combination of `_needsUpdate` and `_hasUpdated` will result in nodes values being recomputed. However, we do not invalidate nodes in all the places we should, e.g. if we create a new view and attach it to an existing value node (see example in facebook#9120). This diff chooses to remove the `_hasUpdated` flag, and simply relies on the `_needsUpdate` flag to mark a node as dirty.

We mark nodes as dirty when they are:
- created
- updated
- attached to new parents
- detached from old parents
- attached to a view

Calling `updateNodeIfNecessary` will, if necessary, compute all invalidated parent values before recomputing the node value. It will then apply the update, and mark the no
Closes facebook#10663

Differential Revision: D4120301

Pulled By: mkonicek

fbshipit-source-id: e247afcb5d8c15999b8328c664b9f7e764d76a75
leotm added a commit to MetaMask/metamask-mobile that referenced this pull request Jun 28, 2023
Fix iOS native animation (non-nil/non-zero) assertion failure (on initial parentNode then childNode), after nav to bot 4th webview tab (unable to load pages, likely caused by current excluded RN polyfillGlobal)
- Df (Foundation) *** Assertion failure in - disconnectAnimatedNodes:childTag
  - E [com.facebook.react.log:native] Exception thrown while executing UI block: 'parentNode' is a required parameter
- Df (Foundation) *** Assertion failure in - connectAnimatedNodes:childTag
  - E [com.facebook.react.log:native] Exception thrown while executing UI block: 'childNode' is a required parameter

Exclude iOS native animation (non-nil/non-zero) assertion macros - for now, when nodes (pointers to tags) attach/detach (to old/new parents and new views)
QA: no empty animation frames observed ✅

Low probability possible risks introduced
- incomplete node invalidation (outdated nodes)
- race condition: prop updated before UIManager created view (outdated props)
QA: no outdated animation frames observed ✅
iOS native animation assertion refs
- Summary: facebook/react-native@c858420
- PR: facebook/react-native#10663
- Examples: facebook/react-native#9120
- nb: mimics ReactAndroid (i.e. NativeAnimatedNodesManager.java)

nb: metro-react-native-babel-preset (0.72.3)
  - @babel/plugin-transform-regenerator has been removed since initial investigation
  - @babel/plugin-transform-runtime (removed) - 4 fewer SES warnings
  - @babel/plugin-transform-runtime > regenerator: false - immediate error thrown (recurring)

Todo: Fix webview page loading (likely caused by current excluded RN promisePolyfill), thus fixing these assertion failures on nav, then revert this patch
  - Problem: Including promisePolyfill (default RN) causes app to boot empty root view
Todo: Root cause of above 'regenerator: false' causing nil/zero parent/child nodes
leotm added a commit to MetaMask/metamask-mobile that referenced this pull request Jun 28, 2023
Fix iOS native animation (non-nil/non-zero) assertion failure (on initial parentNode then childNode), after nav to bot 4th webview tab (unable to load pages, likely caused by current excluded RN polyfillGlobal)
- Df (Foundation) *** Assertion failure in - disconnectAnimatedNodes:childTag
  - E [com.facebook.react.log:native] Exception thrown while executing UI block: 'parentNode' is a required parameter
- Df (Foundation) *** Assertion failure in - connectAnimatedNodes:childTag
  - E [com.facebook.react.log:native] Exception thrown while executing UI block: 'childNode' is a required parameter

Exclude iOS native animation (non-nil/non-zero) assertion macros - for now, when nodes (pointers to tags) attach/detach (to old/new parents and new views)
QA: no empty animation frames observed ✅

Low probability possible risks introduced
- incomplete node invalidation (outdated nodes)
- race condition: prop updated before UIManager created view (outdated props)
QA: no outdated animation frames observed ✅
iOS native animation assertion refs
- Summary: facebook/react-native@c858420
- PR: facebook/react-native#10663
- Examples: facebook/react-native#9120
- nb: mimics ReactAndroid (i.e. NativeAnimatedNodesManager.java)

nb: metro-react-native-babel-preset (0.72.3)
  - @babel/plugin-transform-regenerator has been removed since initial investigation
  - @babel/plugin-transform-runtime (removed) - 4 fewer SES warnings
  - @babel/plugin-transform-runtime > regenerator: false - immediate error thrown (recurring)

Todo: Fix webview page loading (likely caused by current excluded RN promisePolyfill), thus fixing these assertion failures on nav, then revert this patch
  - Problem: Including promisePolyfill (default RN) causes app to boot empty root view
Todo: Root cause of above 'regenerator: false' causing nil/zero parent/child nodes
leotm added a commit to MetaMask/metamask-mobile that referenced this pull request Jun 28, 2023
Fix iOS native animation (non-nil/non-zero) assertion failure (on initial parentNode then childNode), after nav to bot 4th WebView tab (unable to load pages, likely caused by current excluded RN Promise polyfillGlobal)
- Df (Foundation) *** Assertion failure in - disconnectAnimatedNodes:childTag
  - E [com.facebook.react.log:native] Exception thrown while executing UI block: 'parentNode' is a required parameter
- Df (Foundation) *** Assertion failure in - connectAnimatedNodes:childTag
  - E [com.facebook.react.log:native] Exception thrown while executing UI block: 'childNode' is a required parameter

Exclude iOS native animation (non-nil/non-zero) assertion macros - for now, when nodes (pointers to tags) attach/detach (to old/new parents and new views)
QA: no empty animation frames observed ✅

Low probability possible risks introduced
- incomplete node invalidation (outdated nodes)
- race condition: prop updated before UIManager created view (outdated props)
QA: no outdated animation frames observed ✅
iOS native animation assertion refs
- Summary: facebook/react-native@c858420
- PR: facebook/react-native#10663
- Examples: facebook/react-native#9120
- nb: mimics ReactAndroid (i.e. NativeAnimatedNodesManager.java)

nb: metro-react-native-babel-preset (0.72.3)
  - @babel/plugin-transform-regenerator has been removed since initial investigation
  - @babel/plugin-transform-runtime (removed) - 4 fewer SES warnings
    - intrinsics: Object.setPrototypeOf.default, Object.setPrototypeOf.__esModule, Reflect.construct.default, Reflect.construct.__esModule
  - @babel/plugin-transform-runtime > regenerator: false - immediate error thrown (recurring)

Todo: Fix WebView page load (likely caused by current excluded RN Promise polyfillGlobal), thus fixing these assertion failures on nav, then revert this patch
  - Problem: Including (default) RN Promise polyfillGlobal causing app to boot empty root view
Todo: Root cause of above 'regenerator: false' causing nil/zero parent/child nodes immediately to reoccur
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Platform: iOS iOS applications.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants